From dbfb371e297794e827ad2e5b33b45fafbfc46b29 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 16 Jun 2011 16:50:48 +0000 Subject: [PATCH] Implement the consistency checking for C++ [temp.deduct.call]p3, which checks that the deduced argument type for a function call matches the actual argument type provided. The only place we've found where the consistency checking should actually cause template argument deduction failure is due to qualifier differences that don't fall into the realm of qualification conversions (which are *not* checked when we initially perform deduction). However, we're performing the full checking as specified in the standard to ensure that no other cases exist. Fixes PR9233 / . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133163 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 17 +- lib/Sema/SemaTemplateDeduction.cpp | 152 ++++++++++++++++-- .../temp.deduct/temp.deduct.call/p3.cpp | 9 ++ 3 files changed, 164 insertions(+), 14 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 2ec868a94d..659106947a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4316,12 +4316,27 @@ public: QualType *FunctionType, sema::TemplateDeductionInfo &Info); + /// brief A function argument from which we performed template argument + // deduction for a call. + struct OriginalCallArg { + OriginalCallArg(QualType OriginalParamType, + unsigned ArgIdx, + QualType OriginalArgType) + : OriginalParamType(OriginalParamType), ArgIdx(ArgIdx), + OriginalArgType(OriginalArgType) { } + + QualType OriginalParamType; + unsigned ArgIdx; + QualType OriginalArgType; + }; + TemplateDeductionResult FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, llvm::SmallVectorImpl &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, - sema::TemplateDeductionInfo &Info); + sema::TemplateDeductionInfo &Info, + llvm::SmallVectorImpl const *OriginalCallArgs = 0); TemplateDeductionResult DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 359c93130a..014c582779 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1497,7 +1497,6 @@ DeduceTemplateArguments(Sema &S, return Sema::TDK_Success; } - // FIXME: Many more cases to go (to go). return Sema::TDK_Success; } @@ -2318,12 +2317,16 @@ Sema::SubstituteExplicitTemplateArguments( /// \brief Finish template argument deduction for a function template, /// checking the deduced template arguments for completeness and forming /// the function template specialization. +/// +/// \param OriginalCallArgs If non-NULL, the original call arguments against +/// which the deduced argument types should be compared. Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, llvm::SmallVectorImpl &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, - TemplateDeductionInfo &Info) { + TemplateDeductionInfo &Info, + llvm::SmallVectorImpl const *OriginalCallArgs) { TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); @@ -2477,6 +2480,95 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, !Trap.hasErrorOccurred()) Info.take(); + if (OriginalCallArgs) { + // C++ [temp.deduct.call]p4: + // In general, the deduction process attempts to find template argument + // values that will make the deduced A identical to A (after the type A + // is transformed as described above). [...] + for (unsigned I = 0, N = OriginalCallArgs->size(); I != N; ++I) { + OriginalCallArg OriginalArg = (*OriginalCallArgs)[I]; + QualType A = OriginalArg.OriginalArgType; + unsigned ParamIdx = OriginalArg.ArgIdx; + + if (ParamIdx >= Specialization->getNumParams()) + continue; + + QualType DeducedA = Specialization->getParamDecl(ParamIdx)->getType(); + QualType OriginalParamType = OriginalArg.OriginalParamType; + + // Check for type equality (top-level cv-qualifiers are ignored). + if (Context.hasSameUnqualifiedType(A, DeducedA)) + continue; + + // Strip off references on the argument types; they aren't needed for + // the following checks. + if (const ReferenceType *DeducedARef = DeducedA->getAs()) + DeducedA = DeducedARef->getPointeeType(); + if (const ReferenceType *ARef = A->getAs()) + A = ARef->getPointeeType(); + + // C++ [temp.deduct.call]p4: + // [...] However, there are three cases that allow a difference: + // - If the original P is a reference type, the deduced A (i.e., the + // type referred to by the reference) can be more cv-qualified than + // the transformed A. + if (const ReferenceType *OriginalParamRef + = OriginalParamType->getAs()) { + // We don't want to keep the reference around any more. + OriginalParamType = OriginalParamRef->getPointeeType(); + + Qualifiers AQuals = A.getQualifiers(); + Qualifiers DeducedAQuals = DeducedA.getQualifiers(); + if (AQuals == DeducedAQuals) { + // Qualifiers match; there's nothing to do. + } else if (!DeducedAQuals.compatiblyIncludes(AQuals)) { + return Sema::TDK_SubstitutionFailure; + } else { + // Qualifiers are compatible, so have the argument type adopt the + // deduced argument type's qualifiers as if we had performed the + // qualification conversion. + A = Context.getQualifiedType(A.getUnqualifiedType(), DeducedAQuals); + } + } + + // - The transformed A can be another pointer or pointer to member + // type that can be converted to the deduced A via a qualification + // conversion. + bool ObjCLifetimeConversion = false; + if ((A->isAnyPointerType() || A->isMemberPointerType()) && + IsQualificationConversion(A, DeducedA, false, ObjCLifetimeConversion)) + continue; + + + // - If P is a class and P has the form simple-template-id, then the + // transformed A can be a derived class of the deduced A. [...] + // [...] Likewise, if P is a pointer to a class of the form + // simple-template-id, the transformed A can be a pointer to a + // derived class pointed to by the deduced A. + if (const PointerType *OriginalParamPtr + = OriginalParamType->getAs()) { + if (const PointerType *DeducedAPtr = DeducedA->getAs()) { + if (const PointerType *APtr = A->getAs()) { + if (A->getPointeeType()->isRecordType()) { + OriginalParamType = OriginalParamPtr->getPointeeType(); + DeducedA = DeducedAPtr->getPointeeType(); + A = APtr->getPointeeType(); + } + } + } + } + + if (Context.hasSameUnqualifiedType(A, DeducedA)) + continue; + + if (A->isRecordType() && isSimpleTemplateIdType(OriginalParamType) && + IsDerivedFrom(A, DeducedA)) + continue; + + return Sema::TDK_SubstitutionFailure; + } + } + // There may have been an error that did not prevent us from constructing a // declaration. Mark the declaration invalid and return with a substitution // failure. @@ -2720,6 +2812,10 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S, return false; } +static bool hasDeducibleTemplateParameters(Sema &S, + FunctionTemplateDecl *FunctionTemplate, + QualType T); + /// \brief Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). /// @@ -2801,10 +2897,12 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // Deduce template arguments from the function parameters. Deduced.resize(TemplateParams->size()); unsigned ArgIdx = 0; + llvm::SmallVector OriginalCallArgs; for (unsigned ParamIdx = 0, NumParams = ParamTypes.size(); ParamIdx != NumParams; ++ParamIdx) { - QualType ParamType = ParamTypes[ParamIdx]; - + QualType OrigParamType = ParamTypes[ParamIdx]; + QualType ParamType = OrigParamType; + const PackExpansionType *ParamExpansion = dyn_cast(ParamType); if (!ParamExpansion) { @@ -2814,20 +2912,25 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Expr *Arg = Args[ArgIdx++]; QualType ArgType = Arg->getType(); + unsigned TDF = 0; if (AdjustFunctionParmAndArgTypesForDeduction(*this, TemplateParams, ParamType, ArgType, Arg, TDF)) continue; + // Keep track of the argument type and corresponding parameter index, + // so we can check for compatibility between the deduced A and A. + if (hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType)) + OriginalCallArgs.push_back(OriginalCallArg(OrigParamType, ArgIdx-1, + ArgType)); + if (TemplateDeductionResult Result = ::DeduceTemplateArguments(*this, TemplateParams, ParamType, ArgType, Info, Deduced, TDF)) return Result; - // FIXME: we need to check that the deduced A is the same as A, - // modulo the various allowed differences. continue; } @@ -2873,9 +2976,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, for (; ArgIdx < NumArgs; ++ArgIdx) { HasAnyArguments = true; - ParamType = ParamPattern; + QualType OrigParamType = ParamPattern; + ParamType = OrigParamType; Expr *Arg = Args[ArgIdx]; QualType ArgType = Arg->getType(); + unsigned TDF = 0; if (AdjustFunctionParmAndArgTypesForDeduction(*this, TemplateParams, ParamType, ArgType, Arg, @@ -2886,6 +2991,12 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, break; } + // Keep track of the argument type and corresponding argument index, + // so we can check for compatibility between the deduced A and A. + if (hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType)) + OriginalCallArgs.push_back(OriginalCallArg(OrigParamType, ArgIdx, + ArgType)); + if (TemplateDeductionResult Result = ::DeduceTemplateArguments(*this, TemplateParams, ParamType, ArgType, Info, Deduced, @@ -2918,7 +3029,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, NumExplicitlySpecified, - Specialization, Info); + Specialization, Info, &OriginalCallArgs); } /// \brief Deduce template arguments when taking the address of a function @@ -3095,9 +3206,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, 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. LocalInstantiationScope InstScope(*this); FunctionDecl *Spec = 0; @@ -3353,8 +3461,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, case TPOC_Other: // - In other contexts (14.6.6.2) the function template's function type // is used. - // FIXME: Don't we actually want to perform the adjustments on the parameter - // types? if (DeduceTemplateArguments(S, TemplateParams, FD2->getType(), FD1->getType(), Info, Deduced, TDF_None, /*PartialOrdering=*/true, RefParamComparisons)) @@ -4136,3 +4242,23 @@ Sema::MarkDeducedTemplateParameters(FunctionTemplateDecl *FunctionTemplate, ::MarkUsedTemplateParameters(*this, Function->getParamDecl(I)->getType(), true, TemplateParams->getDepth(), Deduced); } + +bool hasDeducibleTemplateParameters(Sema &S, + FunctionTemplateDecl *FunctionTemplate, + QualType T) { + if (!T->isDependentType()) + return false; + + TemplateParameterList *TemplateParams + = FunctionTemplate->getTemplateParameters(); + llvm::SmallVector Deduced; + Deduced.resize(TemplateParams->size()); + ::MarkUsedTemplateParameters(S, T, true, TemplateParams->getDepth(), + Deduced); + + for (unsigned I = 0, N = Deduced.size(); I != N; ++I) + if (Deduced[I]) + return true; + + return false; +} diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp index 3c22cf349c..f5481c307d 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp @@ -123,3 +123,12 @@ namespace N { N::F(d); // OK } } + +namespace PR9233 { + template void f(const T **q); // expected-note{{candidate template ignored: substitution failure [with T = int]}} + + void g(int **p) { + f(p); // expected-error{{no matching function for call to 'f'}} + } + +} -- 2.50.1