From 65ec1fda479688d143fe2403242cd9c730c800a1 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 21 Aug 2009 23:19:43 +0000 Subject: [PATCH] Implement conversion function templates, along with the ability to use template argument deduction from a conversion function (C++ [temp.deduct.conv]) with implicit conversions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@79693 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 5 + lib/AST/DeclCXX.cpp | 8 + lib/Sema/Sema.h | 13 ++ lib/Sema/SemaDeclCXX.cpp | 32 +++- lib/Sema/SemaOverload.cpp | 157 +++++++++++++++--- lib/Sema/SemaTemplateDeduction.cpp | 140 +++++++++++++++- lib/Sema/SemaTemplateInstantiateDecl.cpp | 56 +++---- .../temp.deduct/temp.deduct.conv/p2.cpp | 36 ++++ .../temp.deduct/temp.deduct.conv/p3.cpp | 30 ++++ .../temp.deduct/temp.deduct.conv/p4.cpp | 48 ++++++ www/cxx_status.html | 8 +- 11 files changed, 461 insertions(+), 72 deletions(-) create mode 100644 test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p2.cpp create mode 100644 test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p3.cpp create mode 100644 test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p4.cpp diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index bffedfcd78..60db8c8334 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -559,6 +559,11 @@ public: /// list of conversion functions. void addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl); + /// \brief Add a new conversion function template to the list of conversion + /// functions. + void addConversionFunction(ASTContext &Context, + FunctionTemplateDecl *ConvDecl); + /// isAggregate - Whether this class is an aggregate (C++ /// [dcl.init.aggr]), which is a class with no user-declared /// constructors, no private or protected non-static data members, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5c0bbec1ff..40870bed8d 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -279,9 +279,17 @@ void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context, void CXXRecordDecl::addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl) { + assert(!ConvDecl->getDescribedFunctionTemplate() && + "Conversion function templates should cast to FunctionTemplateDecl."); Conversions.addOverload(ConvDecl); } +void CXXRecordDecl::addConversionFunction(ASTContext &Context, + FunctionTemplateDecl *ConvDecl) { + assert(isa(ConvDecl->getTemplatedDecl()) && + "Function template is not a conversion function template"); + Conversions.addOverload(ConvDecl); +} CXXConstructorDecl * CXXRecordDecl::getDefaultConstructor(ASTContext &Context) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 9b9d6ebf03..3e0eae57ce 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -823,6 +823,9 @@ public: void AddConversionCandidate(CXXConversionDecl *Conversion, Expr *From, QualType ToType, OverloadCandidateSet& CandidateSet); + void AddTemplateConversionCandidate(FunctionTemplateDecl *FunctionTemplate, + Expr *From, QualType ToType, + OverloadCandidateSet &CandidateSet); void AddSurrogateCandidate(CXXConversionDecl *Conversion, const FunctionProtoType *Proto, Expr *Object, Expr **Args, unsigned NumArgs, @@ -2552,6 +2555,16 @@ public: FunctionDecl *&Specialization, TemplateDeductionInfo &Info); + TemplateDeductionResult + DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + QualType ToType, + CXXConversionDecl *&Specialization, + TemplateDeductionInfo &Info); + + FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, + FunctionTemplateDecl *FT2, + bool isCallContext); + void MarkDeducedTemplateParameters(const TemplateArgumentList &TemplateArgs, llvm::SmallVectorImpl &Deduced); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 49ad45ad7a..79dc24e596 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1853,9 +1853,6 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R, Sema::DeclPtrTy Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) { assert(Conversion && "Expected to receive a conversion function declaration"); - // Set the lexical context of this conversion function - Conversion->setLexicalDeclContext(CurContext); - CXXRecordDecl *ClassDecl = cast(Conversion->getDeclContext()); // Make sure we aren't redeclaring the conversion function. @@ -1887,19 +1884,25 @@ Sema::DeclPtrTy Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) { } if (Conversion->getPreviousDeclaration()) { + const NamedDecl *ExpectedPrevDecl = Conversion->getPreviousDeclaration(); + if (FunctionTemplateDecl *ConversionTemplate + = Conversion->getDescribedFunctionTemplate()) + ExpectedPrevDecl = ConversionTemplate->getPreviousDeclaration(); OverloadedFunctionDecl *Conversions = ClassDecl->getConversionFunctions(); for (OverloadedFunctionDecl::function_iterator Conv = Conversions->function_begin(), ConvEnd = Conversions->function_end(); Conv != ConvEnd; ++Conv) { - if (*Conv - == cast_or_null(Conversion->getPreviousDeclaration())) { + if (*Conv == ExpectedPrevDecl) { *Conv = Conversion; return DeclPtrTy::make(Conversion); } } assert(Conversion->isInvalidDecl() && "Conversion should not get here."); - } else + } else if (FunctionTemplateDecl *ConversionTemplate + = Conversion->getDescribedFunctionTemplate()) + ClassDecl->addConversionFunction(Context, ConversionTemplate); + else if (!Conversion->getPrimaryTemplate()) // ignore specializations ClassDecl->addConversionFunction(Context, Conversion); return DeclPtrTy::make(Conversion); @@ -2845,13 +2848,24 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, for (OverloadedFunctionDecl::function_iterator Func = Conversions->function_begin(); Func != Conversions->function_end(); ++Func) { - CXXConversionDecl *Conv = cast(*Func); + FunctionTemplateDecl *ConvTemplate + = dyn_cast(*Func); + CXXConversionDecl *Conv; + if (ConvTemplate) + Conv = cast(ConvTemplate->getTemplatedDecl()); + else + Conv = cast(*Func); // If the conversion function doesn't return a reference type, // it can't be considered for this conversion. if (Conv->getConversionType()->isLValueReferenceType() && - (AllowExplicit || !Conv->isExplicit())) - AddConversionCandidate(Conv, Init, DeclType, CandidateSet); + (AllowExplicit || !Conv->isExplicit())) { + if (ConvTemplate) + AddTemplateConversionCandidate(ConvTemplate, Init, DeclType, + CandidateSet); + else + AddConversionCandidate(Conv, Init, DeclType, CandidateSet); + } } OverloadCandidateSet::iterator Best; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f21b38a649..ada1a2b432 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1302,6 +1302,19 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) FromType.getUnqualifiedType() == ToType.getUnqualifiedType(); } +/// \brief Given a function template or function, extract the function template +/// declaration (if any) and the underlying function declaration. +template +static void GetFunctionAndTemplate(AnyFunctionDecl Orig, T *&Function, + FunctionTemplateDecl *&FunctionTemplate) { + FunctionTemplate = dyn_cast(Orig); + if (FunctionTemplate) + Function = cast(FunctionTemplate->getTemplatedDecl()); + else + Function = cast(Orig); +} + + /// Determines whether there is a user-defined conversion sequence /// (C++ [over.ics.user]) that converts expression From to the type /// ToType. If such a conversion exists, User will contain the @@ -1381,9 +1394,21 @@ bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, for (OverloadedFunctionDecl::function_iterator Func = Conversions->function_begin(); Func != Conversions->function_end(); ++Func) { - CXXConversionDecl *Conv = cast(*Func); - if (AllowExplicit || !Conv->isExplicit()) - AddConversionCandidate(Conv, From, ToType, CandidateSet); + CXXConversionDecl *Conv; + FunctionTemplateDecl *ConvTemplate; + GetFunctionAndTemplate(*Func, Conv, ConvTemplate); + if (ConvTemplate) + Conv = dyn_cast(ConvTemplate->getTemplatedDecl()); + else + Conv = dyn_cast(*Func); + + if (AllowExplicit || !Conv->isExplicit()) { + if (ConvTemplate) + AddTemplateConversionCandidate(ConvTemplate, From, ToType, + CandidateSet); + else + AddConversionCandidate(Conv, From, ToType, CandidateSet); + } } } } @@ -2295,9 +2320,9 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, CandidateSet, SuppressUserConversions, ForceRValue); } -/// \brief Add a C++ function template as a candidate in the candidate set, -/// using template argument deduction to produce an appropriate function -/// template specialization. +/// \brief Add a C++ function template specialization as a candidate +/// in the candidate set, using template argument deduction to produce +/// an appropriate function template specialization. void Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, bool HasExplicitTemplateArgs, @@ -2345,6 +2370,9 @@ void Sema::AddConversionCandidate(CXXConversionDecl *Conversion, Expr *From, QualType ToType, OverloadCandidateSet& CandidateSet) { + assert(!Conversion->getDescribedFunctionTemplate() && + "Conversion function templates use AddTemplateConversionCandidate"); + // Add this candidate CandidateSet.push_back(OverloadCandidate()); OverloadCandidate& Candidate = CandidateSet.back(); @@ -2404,6 +2432,35 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, } } +/// \brief Adds a conversion function template specialization +/// candidate to the overload set, using template argument deduction +/// to deduce the template arguments of the conversion function +/// template from the type that we are converting to (C++ +/// [temp.deduct.conv]). +void +Sema::AddTemplateConversionCandidate(FunctionTemplateDecl *FunctionTemplate, + Expr *From, QualType ToType, + OverloadCandidateSet &CandidateSet) { + assert(isa(FunctionTemplate->getTemplatedDecl()) && + "Only conversion function templates permitted here"); + + TemplateDeductionInfo Info(Context); + CXXConversionDecl *Specialization = 0; + if (TemplateDeductionResult Result + = DeduceTemplateArguments(FunctionTemplate, ToType, + Specialization, Info)) { + // FIXME: Record what happened with template argument deduction, so + // that we can give the user a beautiful diagnostic. + (void)Result; + return; + } + + // Add the conversion function template specialization produced by + // template argument deduction as a candidate. + assert(Specialization && "Missing function template specialization?"); + AddConversionCandidate(Specialization, From, ToType, CandidateSet); +} + /// AddSurrogateCandidate - Adds a "surrogate" candidate function that /// converts the given @c Object to a function pointer via the /// conversion function @c Conversion, and then attempts to call it @@ -2801,7 +2858,15 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, for (OverloadedFunctionDecl::function_iterator Func = Conversions->function_begin(); Func != Conversions->function_end(); ++Func) { - CXXConversionDecl *Conv = cast(*Func); + CXXConversionDecl *Conv; + FunctionTemplateDecl *ConvTemplate; + GetFunctionAndTemplate(*Func, Conv, ConvTemplate); + + // Skip conversion function templates; they don't tell us anything + // about which builtin types we can convert to. + if (ConvTemplate) + continue; + if (AllowExplicitConversions || !Conv->isExplicit()) AddTypesConvertedFrom(Conv->getConversionType(), false, false); } @@ -3543,8 +3608,11 @@ Sema::isBetterOverloadCandidate(const OverloadCandidate& Cand1, // if not that, if (Cand1.Function && Cand1.Function->getPrimaryTemplate() && Cand2.Function && Cand2.Function->getPrimaryTemplate()) - // FIXME: Implement partial ordering of function templates. - Diag(SourceLocation(), diag::unsup_function_template_partial_ordering); + if (FunctionTemplateDecl *BetterTemplate + = getMoreSpecializedTemplate(Cand1.Function->getPrimaryTemplate(), + Cand2.Function->getPrimaryTemplate(), + true)) + return BetterTemplate == Cand1.Function->getPrimaryTemplate(); // -- the context is an initialization by user-defined conversion // (see 8.5, 13.3.1.5) and the standard conversion sequence @@ -3842,21 +3910,61 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, // C++ [over.over]p4: // If more than one function is selected, [...] llvm::SmallVector RemainingMatches; + typedef llvm::SmallPtrSet::iterator MatchIter; if (FoundNonTemplateFunction) { - // [...] any function template specializations in the set are eliminated - // if the set also contains a non-template function, [...] - for (llvm::SmallPtrSet::iterator M = Matches.begin(), - MEnd = Matches.end(); - M != MEnd; ++M) + // [...] any function template specializations in the set are + // eliminated if the set also contains a non-template function, [...] + for (MatchIter M = Matches.begin(), MEnd = Matches.end(); M != MEnd; ++M) if ((*M)->getPrimaryTemplate() == 0) RemainingMatches.push_back(*M); } else { - // [...] and any given function template specialization F1 is eliminated - // if the set contains a second function template specialization whose - // function template is more specialized than the function template of F1 - // according to the partial ordering rules of 14.5.5.2. - // FIXME: Implement this! - RemainingMatches.append(Matches.begin(), Matches.end()); + // [...] and any given function template specialization F1 is + // eliminated if the set contains a second function template + // specialization whose function template is more specialized + // than the function template of F1 according to the partial + // ordering rules of 14.5.5.2. + + // The algorithm specified above is quadratic. We instead use a + // two-pass algorithm (similar to the one used to identify the + // best viable function in an overload set) that identifies the + // best function template (if it exists). + MatchIter Best = Matches.begin(); + MatchIter M = Best, MEnd = Matches.end(); + // Find the most specialized function. + for (++M; M != MEnd; ++M) + if (getMoreSpecializedTemplate((*M)->getPrimaryTemplate(), + (*Best)->getPrimaryTemplate(), + false) + == (*M)->getPrimaryTemplate()) + Best = M; + + // Determine whether this function template is more specialized + // that all of the others. + bool Ambiguous = false; + for (M = Matches.begin(); M != MEnd; ++M) { + if (M != Best && + getMoreSpecializedTemplate((*M)->getPrimaryTemplate(), + (*Best)->getPrimaryTemplate(), + false) + != (*Best)->getPrimaryTemplate()) { + Ambiguous = true; + break; + } + } + + // If one function template was more specialized than all of the + // others, return it. + if (!Ambiguous) + return *Best; + + // We could not find a most-specialized function template, which + // is equivalent to having a set of function templates with more + // than one such template. So, we place all of the function + // templates into the set of remaining matches and produce a + // diagnostic below. FIXME: we could perform the quadratic + // algorithm here, pruning the result set to limit the number of + // candidates output later. + RemainingMatches.append(Matches.begin(), Matches.end()); } // [...] After such eliminations, if any, there shall remain exactly one @@ -4468,7 +4576,14 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, Func = Conversions->function_begin(), FuncEnd = Conversions->function_end(); Func != FuncEnd; ++Func) { - CXXConversionDecl *Conv = cast(*Func); + CXXConversionDecl *Conv; + FunctionTemplateDecl *ConvTemplate; + GetFunctionAndTemplate(*Func, Conv, ConvTemplate); + + // Skip over templated conversion functions; they aren't + // surrogates. + if (ConvTemplate) + continue; // Strip the reference type (if any) and then the pointer type (if // any) to get down to what might be a function type. diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index b3d370ab12..3a1722671f 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1396,8 +1396,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // FIXME: C++0x [temp.deduct.call] paragraphs 6-9 deal with function // pointer parameters. + + // FIXME: we need to check that the deduced A is the same as A, + // modulo the various allowed differences. } - + return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, Specialization, Info); } @@ -1472,6 +1475,141 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Specialization, Info); } +/// \brief Deduce template arguments for a templated conversion +/// function (C++ [temp.deduct.conv]) and, if successful, produce a +/// conversion function template specialization. +Sema::TemplateDeductionResult +Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + QualType ToType, + CXXConversionDecl *&Specialization, + TemplateDeductionInfo &Info) { + CXXConversionDecl *Conv + = cast(FunctionTemplate->getTemplatedDecl()); + QualType FromType = Conv->getConversionType(); + + // Canonicalize the types for deduction. + QualType P = Context.getCanonicalType(FromType); + QualType A = Context.getCanonicalType(ToType); + + // C++0x [temp.deduct.conv]p3: + // If P is a reference type, the type referred to by P is used for + // type deduction. + if (const ReferenceType *PRef = P->getAs()) + P = PRef->getPointeeType(); + + // C++0x [temp.deduct.conv]p3: + // If A is a reference type, the type referred to by A is used + // for type deduction. + if (const ReferenceType *ARef = A->getAs()) + A = ARef->getPointeeType(); + // C++ [temp.deduct.conv]p2: + // + // If A is not a reference type: + else { + assert(!A->isReferenceType() && "Reference types were handled above"); + + // - If P is an array type, the pointer type produced by the + // array-to-pointer standard conversion (4.2) is used in place + // of P for type deduction; otherwise, + if (P->isArrayType()) + P = Context.getArrayDecayedType(P); + // - If P is a function type, the pointer type produced by the + // function-to-pointer standard conversion (4.3) is used in + // place of P for type deduction; otherwise, + else if (P->isFunctionType()) + P = Context.getPointerType(P); + // - If P is a cv-qualified type, the top level cv-qualifiers of + // P’s type are ignored for type deduction. + else + P = P.getUnqualifiedType(); + + // C++0x [temp.deduct.conv]p3: + // If A is a cv-qualified type, the top level cv-qualifiers of A’s + // type are ignored for type deduction. + A = A.getUnqualifiedType(); + } + + // Template argument deduction for function templates in a SFINAE context. + // Trap any errors that might occur. + SFINAETrap Trap(*this); + + // C++ [temp.deduct.conv]p1: + // Template argument deduction is done by comparing the return + // type of the template conversion function (call it P) with the + // type that is required as the result of the conversion (call it + // A) as described in 14.8.2.4. + TemplateParameterList *TemplateParams + = FunctionTemplate->getTemplateParameters(); + llvm::SmallVector Deduced; + Deduced.resize(TemplateParams->size()); + + // C++0x [temp.deduct.conv]p4: + // In general, the deduction process attempts to find template + // argument values that will make the deduced A identical to + // A. However, there are two cases that allow a difference: + unsigned TDF = 0; + // - If the original A is a reference type, A can be more + // cv-qualified than the deduced A (i.e., the type referred to + // by the reference) + if (ToType->isReferenceType()) + TDF |= TDF_ParamWithReferenceType; + // - The deduced A can be another pointer or pointer to member + // type that can be converted to A via a qualification + // conversion. + // + // (C++0x [temp.deduct.conv]p6 clarifies that this only happens when + // both P and A are pointers or member pointers. In this case, we + // just ignore cv-qualifiers completely). + if ((P->isPointerType() && A->isPointerType()) || + (P->isMemberPointerType() && P->isMemberPointerType())) + TDF |= TDF_IgnoreQualifiers; + if (TemplateDeductionResult Result + = ::DeduceTemplateArguments(Context, TemplateParams, + P, A, Info, Deduced, TDF)) + return Result; + + // FIXME: we need to check that the deduced A is the same as A, + // modulo the various allowed differences. + + // Finish template argument deduction. + FunctionDecl *Spec = 0; + TemplateDeductionResult Result + = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, Spec, Info); + Specialization = cast_or_null(Spec); + return Result; +} + +/// \brief Returns the more specialization function template according +/// to the rules of function template partial ordering (C++ [temp.func.order]). +/// +/// \param FT1 the first function template +/// +/// \param FT2 the second function template +/// +/// \param isCallContext whether partial ordering is being performed +/// for a function call (which ignores the return types of the +/// functions). +/// +/// \returns the more specialization function template. If neither +/// template is more specialized, returns NULL. +FunctionTemplateDecl * +Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, + FunctionTemplateDecl *FT2, + bool isCallContext) { +#if 0 + // FIXME: Implement this + bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, isCallContext); + bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, isCallContext); + if (Better1 == Better2) + return 0; + if (Better1) + return FT1; + return FT2; +#else + Diag(SourceLocation(), diag::unsup_function_template_partial_ordering); + return 0; +#endif +} static void MarkDeducedTemplateParameters(Sema &SemaRef, diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index ff97631e72..ded49b33ab 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -505,6 +505,17 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) { Method = CXXDestructorDecl::Create(SemaRef.Context, Record, Destructor->getLocation(), Name, T, Destructor->isInline(), false); + } else if (CXXConversionDecl *Conversion = dyn_cast(D)) { + CanQualType ConvTy + = SemaRef.Context.getCanonicalType( + T->getAsFunctionType()->getResultType()); + Name = SemaRef.Context.DeclarationNames.getCXXConversionFunctionName( + ConvTy); + Method = CXXConversionDecl::Create(SemaRef.Context, Record, + Conversion->getLocation(), Name, + T, Conversion->getDeclaratorInfo(), + Conversion->isInline(), + Conversion->isExplicit()); } else { Method = CXXMethodDecl::Create(SemaRef.Context, Record, D->getLocation(), D->getDeclName(), T, D->getDeclaratorInfo(), @@ -541,11 +552,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) { if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag) PrevDecl = 0; } - - bool Redeclaration = false; - bool OverloadableAttrRequired = false; - SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration, - /*FIXME:*/OverloadableAttrRequired); if (FunctionTemplate) // Record this function template specialization. @@ -553,7 +559,13 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) { FunctionTemplate, &TemplateArgs, InsertPos); - else if (!Method->isInvalidDecl() || !PrevDecl) + + bool Redeclaration = false; + bool OverloadableAttrRequired = false; + SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration, + /*FIXME:*/OverloadableAttrRequired); + + if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl)) Owner->addDecl(Method); return Method; @@ -568,37 +580,7 @@ Decl *TemplateDeclInstantiator::VisitCXXDestructorDecl(CXXDestructorDecl *D) { } Decl *TemplateDeclInstantiator::VisitCXXConversionDecl(CXXConversionDecl *D) { - // FIXME: Look for existing, explicit specializations. - Sema::LocalInstantiationScope Scope(SemaRef); - - llvm::SmallVector Params; - QualType T = InstantiateFunctionType(D, Params); - if (T.isNull()) - return 0; - assert(Params.size() == 0 && "Destructor with parameters?"); - - // Build the instantiated conversion declaration. - CXXRecordDecl *Record = cast(Owner); - QualType ClassTy = SemaRef.Context.getTypeDeclType(Record); - CanQualType ConvTy - = SemaRef.Context.getCanonicalType(T->getAsFunctionType()->getResultType()); - CXXConversionDecl *Conversion - = CXXConversionDecl::Create(SemaRef.Context, Record, - D->getLocation(), - SemaRef.Context.DeclarationNames.getCXXConversionFunctionName(ConvTy), - T, D->getDeclaratorInfo(), - D->isInline(), D->isExplicit()); - Conversion->setInstantiationOfMemberFunction(D); - if (InitMethodInstantiation(Conversion, D)) - Conversion->setInvalidDecl(); - - bool Redeclaration = false; - bool OverloadableAttrRequired = false; - NamedDecl *PrevDecl = 0; - SemaRef.CheckFunctionDeclaration(Conversion, PrevDecl, Redeclaration, - /*FIXME:*/OverloadableAttrRequired); - Owner->addDecl(Conversion); - return Conversion; + return VisitCXXMethodDecl(D); } ParmVarDecl *TemplateDeclInstantiator::VisitParmVarDecl(ParmVarDecl *D) { diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p2.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p2.cpp new file mode 100644 index 0000000000..7d175781c2 --- /dev/null +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p2.cpp @@ -0,0 +1,36 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// FIXME: [temp.deduct.conv]p2 bullets 1 and 2 can't actually happen without +// references? +// struct ConvertibleToArray { +// // template +// // operator T(()[]) const; + +// private: +// typedef int array[17]; + +// operator array() const; +// }; + +// void test_array(ConvertibleToArray cta) { +// int *ip = cta; +// ip = cta; +// const float *cfp = cta; +// } + +// bullet 2 +// struct ConvertibleToFunction { +// template +// operator T(A1, A2) const () { }; +// }; + +// bullet 3 +struct ConvertibleToCVQuals { + template + operator T* const() const; +}; + +void test_cvqual_conv(ConvertibleToCVQuals ctcv) { + int *ip = ctcv; + const int *icp = ctcv; +} diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p3.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p3.cpp new file mode 100644 index 0000000000..95bd7fe121 --- /dev/null +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p3.cpp @@ -0,0 +1,30 @@ +// RUN: clang-cc -fsyntax-only -verify %s +struct AnyPtr { + template + operator T*() const; +}; + +// If A is a cv-qualified type, the top level cv-qualifiers of A's type +// are ignored for type deduction. +void test_cvquals(AnyPtr ap) { + int* const ip = ap; + const float * const volatile fp = ap; +} + +// If A is a reference type, the type referred to by A is used for +// type deduction. +void test_ref_arg(AnyPtr ap) { + const int* const &ip = ap; + double * const &dp = ap; +} + +struct AnyRef { + template + operator T&() const; +}; + +void test_ref_param(AnyRef ar) { + int &ir = ar; + const float &fr = ar; + int i = ar; +} diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p4.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p4.cpp new file mode 100644 index 0000000000..439afa8c04 --- /dev/null +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.conv/p4.cpp @@ -0,0 +1,48 @@ +// RUN: clang-cc -fsyntax-only %s + +struct AnyT { + template + operator T(); +}; + +void test_cvqual_ref(AnyT any) { + const int &cir = any; +} + +struct AnyThreeLevelPtr { + template + operator T***() const; + // FIXME: Can't handle definitions of member templates yet +#if 0 + { + T x = 0; + x = 0; // will fail if T is deduced to a const type + // (EDG and GCC get this wrong) + return 0; + } +#endif +}; + +void test_deduce_with_qual(AnyThreeLevelPtr a3) { + int * const * const * const ip = a3; +} + +struct X { }; + +struct AnyPtrMem { + template + operator T Class::*() const; + // FIXME: Can't handle definitions of member templates yet +#if 0 + { + T x = 0; + x = 0; // will fail if T is deduced to a const type. + // (EDG and GCC get this wrong) + return 0; + } +#endif +}; + +void test_deduce_ptrmem_with_qual(AnyPtrMem apm) { + const float X::* pm = apm; +} diff --git a/www/cxx_status.html b/www/cxx_status.html index 5985e6c5e7..be86c5f3ee 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -2111,10 +2111,10 @@ welcome!

      14.8.2.3 [temp.deduct.conv] - - - - + + + + -- 2.40.0