https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/100692
>From c7ef00e6b9b2c798ef002696be6ebe65195d2fab Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <mizve...@gmail.com> Date: Wed, 24 Jul 2024 03:59:41 -0300 Subject: [PATCH] [clang] check deduction consistency when partial ordering function templates This makes partial ordering of function templates consistent with other entities. Fixes #18291 --- .../checkers/bugprone/stringview-nullptr.cpp | 85 +--- clang/docs/ReleaseNotes.rst | 2 + clang/lib/AST/ExprConstant.cpp | 1 - clang/lib/Sema/SemaTemplateDeduction.cpp | 425 +++++++++++++----- .../test/CodeCompletion/variadic-template.cpp | 2 +- clang/test/SemaTemplate/GH18291.cpp | 16 + clang/test/SemaTemplate/cwg2398.cpp | 14 + clang/test/SemaTemplate/deduction.cpp | 4 +- clang/test/SemaTemplate/temp_arg_nontype.cpp | 14 +- clang/test/SemaTemplate/temp_arg_type.cpp | 7 +- .../Templight/templight-empty-entries-fix.cpp | 92 ++-- 11 files changed, 419 insertions(+), 243 deletions(-) create mode 100644 clang/test/SemaTemplate/GH18291.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/stringview-nullptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/stringview-nullptr.cpp index 02fcab31dcf3e..19c4da437c85f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/stringview-nullptr.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/stringview-nullptr.cpp @@ -30,98 +30,27 @@ class basic_string_view { constexpr basic_string_view &operator=(const basic_string_view &) {} }; -template <typename CharT> -constexpr bool operator<(basic_string_view<CharT>, basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator<(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator<(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { - return {}; -} - -template <typename CharT> -constexpr bool operator<=(basic_string_view<CharT>, basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator<=(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator<=(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { - return {}; -} - -template <typename CharT> -constexpr bool operator>(basic_string_view<CharT>, basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator>(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator>(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { - return {}; -} - -template <typename CharT> -constexpr bool operator>=(basic_string_view<CharT>, basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator>=(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { - return {}; -} -template <typename CharT> -constexpr bool operator>=(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { - return {}; -} +using string_view = basic_string_view<char>; -template <typename CharT> -constexpr bool operator==(basic_string_view<CharT>, basic_string_view<CharT>) { +constexpr bool operator<(string_view, string_view) { return {}; } -template <typename CharT> -constexpr bool operator==(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { +constexpr bool operator<=(string_view, string_view) { return {}; } -template <typename CharT> -constexpr bool operator==(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { +constexpr bool operator>(string_view, string_view) { return {}; } - -template <typename CharT> -constexpr bool operator!=(basic_string_view<CharT>, basic_string_view<CharT>) { +constexpr bool operator>=(string_view, string_view) { return {}; } -template <typename CharT> -constexpr bool operator!=(type_identity_t<basic_string_view<CharT>>, - basic_string_view<CharT>) { +constexpr bool operator==(string_view, string_view) { return {}; } -template <typename CharT> -constexpr bool operator!=(basic_string_view<CharT>, - type_identity_t<basic_string_view<CharT>>) { +constexpr bool operator!=(string_view, string_view) { return {}; } -using string_view = basic_string_view<char>; - } // namespace std using SV = std::string_view; // Used in some places for shorter line length diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5dddd8f1c5af5..6098101a73a62 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -153,6 +153,8 @@ Bug Fixes to C++ Support - Fixed a crash when an expression with a dependent ``__typeof__`` type is used as the operand of a unary operator. (#GH97646) - Fixed a failed assertion when checking invalid delete operator declaration. (#GH96191) +- When performing partial ordering of function templates, clang now checks that + the deduction was consistent. Fixes (#GH18291). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 558e20ed3e423..2512cd575fbdd 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5346,7 +5346,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue(); FullExpressionRAII Scope(Info); if (RetExpr && RetExpr->isValueDependent()) { - EvaluateDependentExpr(RetExpr, Info); // We know we returned, but we don't know what the value is. return ESR_Failed; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b7b857ebf804b..4e5aeab125a01 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -837,9 +837,11 @@ class PackDeductionScope { PackDeductionScope(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl<DeducedTemplateArgument> &Deduced, TemplateDeductionInfo &Info, TemplateArgument Pattern, - bool DeducePackIfNotAlreadyDeduced = false) + bool DeducePackIfNotAlreadyDeduced = false, + bool FinishingDeduction = false) : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info), - DeducePackIfNotAlreadyDeduced(DeducePackIfNotAlreadyDeduced){ + DeducePackIfNotAlreadyDeduced(DeducePackIfNotAlreadyDeduced), + FinishingDeduction(FinishingDeduction) { unsigned NumNamedPacks = addPacks(Pattern); finishConstruction(NumNamedPacks); } @@ -859,8 +861,10 @@ class PackDeductionScope { // by this pack expansion, then clear out the deduction. DeducedFromEarlierParameter = !Deduced[Index].isNull(); DeducedPack Pack(Index); - Pack.Saved = Deduced[Index]; - Deduced[Index] = TemplateArgument(); + if (!FinishingDeduction) { + Pack.Saved = Deduced[Index]; + Deduced[Index] = TemplateArgument(); + } // FIXME: What if we encounter multiple packs with different numbers of // pre-expanded expansions? (This should already have been diagnosed @@ -1024,18 +1028,20 @@ class PackDeductionScope { // Capture the deduced template arguments for each parameter pack expanded // by this pack expansion, add them to the list of arguments we've deduced // for that pack, then clear out the deduced argument. - for (auto &Pack : Packs) { - DeducedTemplateArgument &DeducedArg = Deduced[Pack.Index]; - if (!Pack.New.empty() || !DeducedArg.isNull()) { - while (Pack.New.size() < PackElements) - Pack.New.push_back(DeducedTemplateArgument()); - if (Pack.New.size() == PackElements) - Pack.New.push_back(DeducedArg); - else - Pack.New[PackElements] = DeducedArg; - DeducedArg = Pack.New.size() > PackElements + 1 - ? Pack.New[PackElements + 1] - : DeducedTemplateArgument(); + if (!FinishingDeduction) { + for (auto &Pack : Packs) { + DeducedTemplateArgument &DeducedArg = Deduced[Pack.Index]; + if (!Pack.New.empty() || !DeducedArg.isNull()) { + while (Pack.New.size() < PackElements) + Pack.New.push_back(DeducedTemplateArgument()); + if (Pack.New.size() == PackElements) + Pack.New.push_back(DeducedArg); + else + Pack.New[PackElements] = DeducedArg; + DeducedArg = Pack.New.size() > PackElements + 1 + ? Pack.New[PackElements + 1] + : DeducedTemplateArgument(); + } } } ++PackElements; @@ -1045,11 +1051,14 @@ class PackDeductionScope { /// producing the argument packs and checking for consistency with prior /// deductions. TemplateDeductionResult finish() { + if (FinishingDeduction) + return TemplateDeductionResult::Success; // Build argument packs for each of the parameter packs expanded by this // pack expansion. for (auto &Pack : Packs) { // Put back the old value for this pack. - Deduced[Pack.Index] = Pack.Saved; + if (!FinishingDeduction) + Deduced[Pack.Index] = Pack.Saved; // Always make sure the size of this pack is correct, even if we didn't // deduce any values for it. @@ -1149,6 +1158,7 @@ class PackDeductionScope { bool IsPartiallyExpanded = false; bool DeducePackIfNotAlreadyDeduced = false; bool DeducedFromEarlierParameter = false; + bool FinishingDeduction = false; /// The number of expansions, if we have a fully-expanded pack in this scope. std::optional<unsigned> FixedNumExpansions; @@ -1157,43 +1167,13 @@ class PackDeductionScope { } // namespace -/// Deduce the template arguments by comparing the list of parameter -/// types to the list of argument types, as in the parameter-type-lists of -/// function types (C++ [temp.deduct.type]p10). -/// -/// \param S The semantic analysis object within which we are deducing -/// -/// \param TemplateParams The template parameters that we are deducing -/// -/// \param Params The list of parameter types -/// -/// \param NumParams The number of types in \c Params -/// -/// \param Args The list of argument types -/// -/// \param NumArgs The number of types in \c Args -/// -/// \param Info information about the template argument deduction itself -/// -/// \param Deduced the deduced template arguments -/// -/// \param TDF bitwise OR of the TemplateDeductionFlags bits that describe -/// how template argument deduction is performed. -/// -/// \param PartialOrdering If true, we are performing template argument -/// deduction for during partial ordering for a call -/// (C++0x [temp.deduct.partial]). -/// -/// \returns the result of template argument deduction so far. Note that a -/// "success" result means that template argument deduction has not yet failed, -/// but it may still fail, later, for other reasons. -static TemplateDeductionResult -DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, - const QualType *Params, unsigned NumParams, - const QualType *Args, unsigned NumArgs, - TemplateDeductionInfo &Info, - SmallVectorImpl<DeducedTemplateArgument> &Deduced, - unsigned TDF, bool PartialOrdering = false) { +template <class T> +static TemplateDeductionResult DeduceForEachType( + Sema &S, TemplateParameterList *TemplateParams, const QualType *Params, + unsigned NumParams, const QualType *Args, unsigned NumArgs, + TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, bool PartialOrdering, + bool FinishingDeduction, T &&DeductFunc) { // C++0x [temp.deduct.type]p10: // Similarly, if P has a form that contains (T), then each parameter type // Pi of the respective parameter-type- list of P is compared with the @@ -1219,11 +1199,10 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::MiscellaneousDeductionFailure; } - if (TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, Params[ParamIdx].getUnqualifiedType(), - Args[ArgIdx].getUnqualifiedType(), Info, Deduced, TDF, - PartialOrdering, - /*DeducedFromArrayBound=*/false); + if (TemplateDeductionResult Result = DeductFunc( + S, TemplateParams, ArgIdx, Params[ParamIdx].getUnqualifiedType(), + Args[ArgIdx].getUnqualifiedType(), Info, Deduced, + PartialOrdering); Result != TemplateDeductionResult::Success) return Result; @@ -1239,20 +1218,21 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // template parameter packs expanded by the function parameter pack. QualType Pattern = Expansion->getPattern(); - PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); + PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern, + /*DeducePackIfNotAlreadyDeduced=*/false, + FinishingDeduction); // A pack scope with fixed arity is not really a pack any more, so is not // a non-deduced context. if (ParamIdx + 1 == NumParams || PackScope.hasFixedArity()) { for (; ArgIdx < NumArgs && PackScope.hasNextElement(); ++ArgIdx) { // Deduce template arguments from the pattern. - if (TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, Pattern.getUnqualifiedType(), - Args[ArgIdx].getUnqualifiedType(), Info, Deduced, TDF, - PartialOrdering, /*DeducedFromArrayBound=*/false); + if (TemplateDeductionResult Result = DeductFunc( + S, TemplateParams, ArgIdx, Pattern.getUnqualifiedType(), + Args[ArgIdx].getUnqualifiedType(), Info, Deduced, + PartialOrdering); Result != TemplateDeductionResult::Success) return Result; - PackScope.nextPackElement(); } } else { @@ -1305,6 +1285,56 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::Success; } +/// Deduce the template arguments by comparing the list of parameter +/// types to the list of argument types, as in the parameter-type-lists of +/// function types (C++ [temp.deduct.type]p10). +/// +/// \param S The semantic analysis object within which we are deducing +/// +/// \param TemplateParams The template parameters that we are deducing +/// +/// \param Params The list of parameter types +/// +/// \param NumParams The number of types in \c Params +/// +/// \param Args The list of argument types +/// +/// \param NumArgs The number of types in \c Args +/// +/// \param Info information about the template argument deduction itself +/// +/// \param Deduced the deduced template arguments +/// +/// \param TDF bitwise OR of the TemplateDeductionFlags bits that describe +/// how template argument deduction is performed. +/// +/// \param PartialOrdering If true, we are performing template argument +/// deduction for during partial ordering for a call +/// (C++0x [temp.deduct.partial]). +/// +/// \returns the result of template argument deduction so far. Note that a +/// "success" result means that template argument deduction has not yet failed, +/// but it may still fail, later, for other reasons. +static TemplateDeductionResult +DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, + const QualType *Params, unsigned NumParams, + const QualType *Args, unsigned NumArgs, + TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + unsigned TDF, bool PartialOrdering = false) { + return ::DeduceForEachType( + S, TemplateParams, Params, NumParams, Args, NumArgs, Info, Deduced, + PartialOrdering, /*FinishingDeduction=*/false, + [TDF](Sema &S, TemplateParameterList *TemplateParams, int, QualType P, + QualType A, TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + bool PartialOrdering) { + return DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, P, A, Info, Deduced, TDF, PartialOrdering, + /*DeducedFromArrayBound=*/false); + }); +} + /// Determine whether the parameter has qualifiers that the argument /// lacks. Put another way, determine whether there is no way to add /// a deduced set of qualifiers to the ParamType that would result in @@ -2922,7 +2952,7 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( SmallVectorImpl<TemplateArgument> &SugaredBuilder, SmallVectorImpl<TemplateArgument> &CanonicalBuilder, LocalInstantiationScope *CurrentInstantiationScope = nullptr, - unsigned NumAlreadyConverted = 0, bool PartialOverloading = false) { + unsigned NumAlreadyConverted = 0, bool *IsIncomplete = nullptr) { TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { @@ -3008,11 +3038,17 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( // If there was no default argument, deduction is incomplete. if (DefArg.getArgument().isNull()) { + if (IsIncomplete) { + *IsIncomplete = true; + SugaredBuilder.push_back({}); + CanonicalBuilder.push_back({}); + continue; + } + Info.Param = makeTemplateParameter( const_cast<NamedDecl *>(TemplateParams->getParam(I))); Info.reset(TemplateArgumentList::CreateCopy(S.Context, SugaredBuilder), TemplateArgumentList::CreateCopy(S.Context, CanonicalBuilder)); - if (PartialOverloading) break; return HasDefaultArg ? TemplateDeductionResult::SubstitutionFailure : TemplateDeductionResult::Incomplete; @@ -3227,7 +3263,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( S, Template, /*IsDeduced*/ PartialOrdering, Deduced, Info, SugaredBuilder, CanonicalBuilder, /*CurrentInstantiationScope=*/nullptr, - /*NumAlreadyConverted=*/0U, /*PartialOverloading=*/false); + /*NumAlreadyConverted=*/0U); Result != TemplateDeductionResult::Success) return Result; @@ -3831,11 +3867,12 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // C++ [temp.deduct.type]p2: // [...] or if any template argument remains neither deduced nor // explicitly specified, template argument deduction fails. + bool IsIncomplete = false; SmallVector<TemplateArgument, 4> SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( *this, FunctionTemplate, /*IsDeduced*/ true, Deduced, Info, SugaredBuilder, CanonicalBuilder, CurrentInstantiationScope, - NumExplicitlySpecified, PartialOverloading); + NumExplicitlySpecified, PartialOverloading ? &IsIncomplete : nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -3914,9 +3951,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // ([temp.constr.decl]), those constraints are checked for satisfaction // ([temp.constr.constr]). If the constraints are not satisfied, type // deduction fails. - if (!PartialOverloading || - (CanonicalBuilder.size() == - FunctionTemplate->getTemplateParameters()->size())) { + if (!IsIncomplete) { if (CheckInstantiatedFunctionTemplateConstraints( Info.getLocation(), Specialization, CanonicalBuilder, Info.AssociatedConstraintsSatisfaction)) @@ -5399,11 +5434,85 @@ static QualType GetImplicitObjectParameterType(ASTContext &Context, return Context.getLValueReferenceType(RawType); } +static TemplateDeductionResult FinishTemplateArgumentDeduction( + Sema &S, FunctionTemplateDecl *FTD, int ArgIdx, QualType P, QualType A, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + TemplateDeductionInfo &Info) { + // Unevaluated SFINAE context. + EnterExpressionEvaluationContext Unevaluated( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap Trap(S); + + Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(FTD)); + + // C++ [temp.deduct.type]p2: + // [...] or if any template argument remains neither deduced nor + // explicitly specified, template argument deduction fails. + bool IsIncomplete = false; + SmallVector<TemplateArgument, 4> SugaredBuilder, CanonicalBuilder; + if (auto Result = ConvertDeducedTemplateArguments( + S, FTD, /*IsDeduced=*/true, Deduced, Info, SugaredBuilder, + CanonicalBuilder, /*CurrentInstantiationScope=*/nullptr, + /*NumAlreadyConverted=*/0, &IsIncomplete); + Result != TemplateDeductionResult::Success) + return Result; + + // Form the template argument list from the deduced template arguments. + TemplateArgumentList *SugaredDeducedArgumentList = + TemplateArgumentList::CreateCopy(S.Context, SugaredBuilder); + TemplateArgumentList *CanonicalDeducedArgumentList = + TemplateArgumentList::CreateCopy(S.Context, CanonicalBuilder); + + Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList); + + // Substitute the deduced template arguments into the argument + // and verify that the instantiated argument is both valid + // and equivalent to the parameter. + LocalInstantiationScope InstScope(S); + + QualType InstP; + { + MultiLevelTemplateArgumentList MLTAL(FTD, SugaredBuilder, + /*Final=*/true); + if (ArgIdx != -1) + if (auto *MD = dyn_cast<CXXMethodDecl>(FTD->getTemplatedDecl()); + MD && MD->isImplicitObjectMemberFunction()) + ArgIdx -= 1; + Sema::ArgumentPackSubstitutionIndexRAII PackIndex( + S, ArgIdx != -1 ? ::getPackIndexForParam(S, FTD, MLTAL, ArgIdx) : -1); + InstP = S.SubstType(P, MLTAL, FTD->getLocation(), FTD->getDeclName()); + if (InstP.isNull()) + return TemplateDeductionResult::SubstitutionFailure; + } + + if (auto *PA = dyn_cast<PackExpansionType>(A); + PA && !isa<PackExpansionType>(InstP)) + A = PA->getPattern(); + if (!S.Context.hasSameType( + S.Context.getUnqualifiedArrayType(InstP.getNonReferenceType()), + S.Context.getUnqualifiedArrayType(A.getNonReferenceType()))) + return TemplateDeductionResult::NonDeducedMismatch; + + // C++20 [temp.deduct]p5 - Only check constraints when all parameters have + // been deduced. + if (!IsIncomplete) { + if (auto Result = CheckDeducedArgumentConstraints(S, FTD, SugaredBuilder, + CanonicalBuilder, Info); + Result != TemplateDeductionResult::Success) + return Result; + } + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; +} + /// Determine whether the function template \p FT1 is at least as /// specialized as \p FT2. static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, - const FunctionTemplateDecl *FT1, - const FunctionTemplateDecl *FT2, + FunctionTemplateDecl *FT1, + FunctionTemplateDecl *FT2, TemplatePartialOrderingContext TPOC, bool Reversed, const SmallVector<QualType> &Args1, @@ -5425,16 +5534,41 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, // the partial ordering is done: TemplateDeductionInfo Info(Loc); switch (TPOC) { - case TPOC_Call: + case TPOC_Call: { if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(), Args1.data(), Args1.size(), Info, Deduced, TDF_None, /*PartialOrdering=*/true) != TemplateDeductionResult::Success) return false; - break; + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), + Deduced.end()); + Sema::InstantiatingTemplate Inst( + S, Info.getLocation(), FT2, DeducedArgs, + Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info); + if (Inst.isInvalid()) + return false; - case TPOC_Conversion: + bool AtLeastAsSpecialized = true; + S.runWithSufficientStackSpace(Info.getLocation(), [&] { + AtLeastAsSpecialized = + ::DeduceForEachType( + S, TemplateParams, Args2.data(), Args2.size(), Args1.data(), + Args1.size(), Info, Deduced, + /*PartialOrdering=*/true, /*FinishingDeduction=*/true, + [&FT2](Sema &S, TemplateParameterList *, int ArgIdx, QualType P, + QualType A, TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + bool PartialOrdering) { + return ::FinishTemplateArgumentDeduction(S, FT2, ArgIdx, P, A, + Deduced, Info); + }) == TemplateDeductionResult::Success; + }); + if (!AtLeastAsSpecialized) + return false; + } break; + + case TPOC_Conversion: { // - In the context of a call to a conversion operator, the return types // of the conversion function templates are used. if (DeduceTemplateArgumentsByTypeMatch( @@ -5442,9 +5576,27 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, Info, Deduced, TDF_None, /*PartialOrdering=*/true) != TemplateDeductionResult::Success) return false; - break; - case TPOC_Other: + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), + Deduced.end()); + Sema::InstantiatingTemplate Inst( + S, Info.getLocation(), FT2, DeducedArgs, + Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info); + if (Inst.isInvalid()) + return false; + + bool AtLeastAsSpecialized; + S.runWithSufficientStackSpace(Info.getLocation(), [&] { + AtLeastAsSpecialized = ::FinishTemplateArgumentDeduction( + S, FT2, /*ArgIdx=*/-1, Proto2->getReturnType(), + Proto1->getReturnType(), Deduced, + Info) == TemplateDeductionResult::Success; + }); + if (!AtLeastAsSpecialized) + return false; + } break; + + case TPOC_Other: { // - In other contexts (14.6.6.2) the function template's function type // is used. if (DeduceTemplateArgumentsByTypeMatch( @@ -5452,7 +5604,42 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, TDF_AllowCompatibleFunctionType, /*PartialOrdering=*/true) != TemplateDeductionResult::Success) return false; - break; + + SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), + Deduced.end()); + Sema::InstantiatingTemplate Inst( + S, Info.getLocation(), FT2, DeducedArgs, + Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info); + if (Inst.isInvalid()) + return false; + + bool AtLeastAsSpecialized; + S.runWithSufficientStackSpace(Info.getLocation(), [&] { + AtLeastAsSpecialized = ::FinishTemplateArgumentDeduction( + S, FT2, /*ArgIdx=*/-1, Proto2->getReturnType(), + Proto1->getReturnType(), Deduced, + Info) == TemplateDeductionResult::Success; + if (!AtLeastAsSpecialized) + return; + S.runWithSufficientStackSpace(Info.getLocation(), [&] { + AtLeastAsSpecialized = + ::DeduceForEachType( + S, TemplateParams, Proto2->getParamTypes().data(), + Proto2->getParamTypes().size(), Proto1->getParamTypes().data(), + Proto1->getParamTypes().size(), Info, Deduced, + /*PartialOrdering=*/true, /*FinishingDeduction=*/true, + [&FT2](Sema &S, TemplateParameterList *, int ArgIdx, QualType P, + QualType A, TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + bool PartialOrdering) { + return ::FinishTemplateArgumentDeduction(S, FT2, ArgIdx, P, A, + Deduced, Info); + }) == TemplateDeductionResult::Success; + }); + }); + if (!AtLeastAsSpecialized) + return false; + } break; } // C++0x [temp.deduct.partial]p11: @@ -5466,10 +5653,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, if (Deduced[ArgIdx].isNull()) break; - // FIXME: We fail to implement [temp.deduct.type]p1 along this path. We need - // to substitute the deduced arguments back into the template and check that - // we get the right type. - if (ArgIdx == NumArgs) { // All template arguments were deduced. FT1 is at least as specialized // as FT2. @@ -5558,38 +5741,50 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( // "that is a member function with no expicit object argument". // Otherwise the ordering rules for methods with expicit objet arguments // against anything else make no sense. - ShouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction(); - ShouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction(); - if (ShouldConvert1) { - bool IsRValRef2 = - ShouldConvert2 - ? Method2->getRefQualifier() == RQ_RValue - : Proto2->param_type_begin()[0]->isRValueReferenceType(); - // Compare 'this' from Method1 against first parameter from Method2. - Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1, RawObj1Ty, - IsRValRef2); - Args1.push_back(Obj1Ty); - } - if (ShouldConvert2) { - bool IsRValRef1 = - ShouldConvert1 - ? Method1->getRefQualifier() == RQ_RValue - : Proto1->param_type_begin()[0]->isRValueReferenceType(); - // Compare 'this' from Method2 against first parameter from Method1. - Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2, RawObj2Ty, - IsRValRef1); - Args2.push_back(Obj2Ty); - } + + bool NonStaticMethod1 = Method1 && !Method1->isStatic(), + NonStaticMethod2 = Method2 && !Method2->isStatic(); + + auto Params1Begin = Proto1->param_type_begin(), + Params2Begin = Proto2->param_type_begin(); + size_t NumComparedArguments = NumCallArguments1; - // Either added an argument above or the prototype includes an explicit - // object argument we need to count - if (Method1) - ++NumComparedArguments; - - Args1.insert(Args1.end(), Proto1->param_type_begin(), - Proto1->param_type_end()); - Args2.insert(Args2.end(), Proto2->param_type_begin(), - Proto2->param_type_end()); + + if ((NonStaticMethod1 && NonStaticMethod2) || FD1->isOverloadedOperator()) { + ShouldConvert1 = + NonStaticMethod1 && !Method1->hasCXXExplicitFunctionObjectParameter(); + ShouldConvert2 = + NonStaticMethod2 && !Method2->hasCXXExplicitFunctionObjectParameter(); + NumComparedArguments += 1; + + if (ShouldConvert1) { + bool IsRValRef2 = + ShouldConvert2 + ? Method2->getRefQualifier() == RQ_RValue + : Proto2->param_type_begin()[0]->isRValueReferenceType(); + // Compare 'this' from Method1 against first parameter from Method2. + Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1, + RawObj1Ty, IsRValRef2); + Args1.push_back(Obj1Ty); + } + if (ShouldConvert2) { + bool IsRValRef1 = + ShouldConvert1 + ? Method1->getRefQualifier() == RQ_RValue + : Proto1->param_type_begin()[0]->isRValueReferenceType(); + // Compare 'this' from Method2 against first parameter from Method1. + Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2, + RawObj2Ty, IsRValRef1); + Args2.push_back(Obj2Ty); + } + } else { + if (NonStaticMethod1 && Method1->hasCXXExplicitFunctionObjectParameter()) + Params1Begin += 1; + if (NonStaticMethod2 && Method2->hasCXXExplicitFunctionObjectParameter()) + Params2Begin += 1; + } + Args1.insert(Args1.end(), Params1Begin, Proto1->param_type_end()); + Args2.insert(Args2.end(), Params2Begin, Proto2->param_type_end()); // C++ [temp.func.order]p5: // The presence of unused ellipsis and default arguments has no effect on diff --git a/clang/test/CodeCompletion/variadic-template.cpp b/clang/test/CodeCompletion/variadic-template.cpp index 31fc55ee154f6..370cf5e8461d6 100644 --- a/clang/test/CodeCompletion/variadic-template.cpp +++ b/clang/test/CodeCompletion/variadic-template.cpp @@ -8,7 +8,7 @@ void f() { // The important thing is that we provide OVERLOAD signature in all those cases. // // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):7 %s -o - | FileCheck --check-prefix=CHECK-1 %s - // CHECK-1: OVERLOAD: [#void#]fun(<#T x#>, Args args...) + // CHECK-1: OVERLOAD: [#void#]fun(<#T x#>) // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-7):10 %s -o - | FileCheck --check-prefix=CHECK-2 %s // CHECK-2: OVERLOAD: [#void#]fun(int x) // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-9):13 %s -o - | FileCheck --check-prefix=CHECK-3 %s diff --git a/clang/test/SemaTemplate/GH18291.cpp b/clang/test/SemaTemplate/GH18291.cpp new file mode 100644 index 0000000000000..2dc7328edad52 --- /dev/null +++ b/clang/test/SemaTemplate/GH18291.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++23 -verify %s + +template<bool> struct enable_if { typedef void type; }; +template <class T> class Foo {}; +template <class X> constexpr bool check() { return true; } +template <class X, class Enable = void> struct Bar {}; + +template<class X> void func(Bar<X, typename enable_if<check<X>()>::type>) {} +// expected-note@-1 {{candidate function}} + +template<class T> void func(Bar<Foo<T>>) {} +// expected-note@-1 {{candidate function}} + +void g() { + func(Bar<Foo<int>>()); // expected-error {{call to 'func' is ambiguous}} +} diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp index 7675d4287cb88..6fe1bd3d4f165 100644 --- a/clang/test/SemaTemplate/cwg2398.cpp +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -74,6 +74,20 @@ namespace class_template { // new-error@-1 {{ambiguous partial specialization}} } // namespace class_template +namespace class_template_func { + template <class T1, class T2 = float> struct A {}; + + template <template <class T4> class TT1, class T5> void f(TT1<T5>); + // new-note@-1 {{candidate function}} + + template <class T6, class T7> void f(A<T6, T7>) {}; + // new-note@-1 {{candidate function}} + + void g() { + f(A<int>()); // new-error {{call to 'f' is ambiguous}} + } +} // namespace class_template_func + namespace type_pack1 { template<class T2> struct A; template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>> ; diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp index a209615c36479..088977249e47d 100644 --- a/clang/test/SemaTemplate/deduction.cpp +++ b/clang/test/SemaTemplate/deduction.cpp @@ -140,11 +140,13 @@ namespace test2 { template<typename T> struct Const { typedef void const type; }; template<typename T> void f(T, typename Const<T>::type*); + // expected-note@-1 {{candidate function}} template<typename T> void f(T, void const *); + // expected-note@-1 {{candidate function}} void test() { void *p = 0; - f(0, p); + f(0, p); // expected-error {{call to 'f' is ambiguous}} } } diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp index e091de669fab4..cbcfb2a5b8674 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -458,17 +458,13 @@ namespace dependent_nested_partial_specialization { namespace nondependent_default_arg_ordering { int n, m; template<typename A, A B = &n> struct X {}; - template<typename A> void f(X<A>); // expected-note {{candidate}} - template<typename A> void f(X<A, &m>); // expected-note {{candidate}} - template<typename A, A B> void f(X<A, B>); // expected-note 2{{candidate}} + template<typename A> void f(X<A>); + template<typename A> void f(X<A, &m>); + template<typename A, A B> void f(X<A, B>); template<template<typename U, U> class T, typename A, int *B> void f(T<A, B>); void g() { - // FIXME: The first and second function templates above should be - // considered more specialized than the third, but during partial - // ordering we fail to check that we actually deduced template arguments - // that make the deduced A identical to A. - X<int *, &n> x; f(x); // expected-error {{ambiguous}} - X<int *, &m> y; f(y); // expected-error {{ambiguous}} + X<int *, &n> x; f(x); + X<int *, &m> y; f(y); } } diff --git a/clang/test/SemaTemplate/temp_arg_type.cpp b/clang/test/SemaTemplate/temp_arg_type.cpp index cdbcf281125ef..392d2573d3d0e 100644 --- a/clang/test/SemaTemplate/temp_arg_type.cpp +++ b/clang/test/SemaTemplate/temp_arg_type.cpp @@ -69,11 +69,10 @@ namespace deduce_noexcept { void noexcept_function() noexcept; void throwing_function(); - template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}} - template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}} + template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); + template<typename T> int &deduce_function(T(*)() noexcept); void test_function_deduction() { - // FIXME: This should probably unambiguously select the second overload. - int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}} + int &r = deduce_function(noexcept_function); float &s = deduce_function(throwing_function); } diff --git a/clang/test/Templight/templight-empty-entries-fix.cpp b/clang/test/Templight/templight-empty-entries-fix.cpp index ad029d3070756..e17be9012e59c 100644 --- a/clang/test/Templight/templight-empty-entries-fix.cpp +++ b/clang/test/Templight/templight-empty-entries-fix.cpp @@ -170,6 +170,30 @@ template <bool d = true, class = typename b<d>::c> void a() { a(); } template <bool = true> void d(int = 0) { d(); } +// CHECK-LABEL: {{^---$}} +// CHECK: {{^name:[ ]+a$}} +// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} +// CHECK: {{^event:[ ]+Begin$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}} +// CHECK-LABEL: {{^---$}} +// CHECK: {{^name:[ ]+a$}} +// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} +// CHECK: {{^event:[ ]+End$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:60:57'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}} +// CHECK-LABEL: {{^---$}} +// CHECK: {{^name:[ ]+a$}} +// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} +// CHECK: {{^event:[ ]+Begin$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}} +// CHECK-LABEL: {{^---$}} +// CHECK: {{^name:[ ]+a$}} +// CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} +// CHECK: {{^event:[ ]+End$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:20:25'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:60:63'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} @@ -225,41 +249,41 @@ void e() { } // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}} // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}} // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}} // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:224:5'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:248:5'$}} // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} // CHECK-LABEL: {{^---$}} -// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:223:3\)'$}} +// CHECK: {{^name:[ ]+'\(unnamed struct at .*templight-empty-entries-fix.cpp:247:3\)'$}} // CHECK: {{^kind:[ ]+Memoization$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:223:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:247:3'$}} template <template<typename> class> @@ -275,59 +299,59 @@ void foo() { // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}} // CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}} // CHECK: {{^poi:[ ]+''$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}} // CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:265:35'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}} // CHECK: {{^poi:[ ]+''$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+DeducedTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+'d<C>'$}} // CHECK: {{^kind:[ ]+TemplateInstantiation$}} // CHECK: {{^event:[ ]+Begin$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+'d<C>'$}} // CHECK: {{^kind:[ ]+TemplateInstantiation$}} // CHECK: {{^event:[ ]+End$}} -// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:266:6'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:290:6'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+Begin$}} // CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} // CHECK-LABEL: {{^---$}} // CHECK: {{^name:[ ]+d$}} // CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}} // CHECK: {{^event:[ ]+End$}} // CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:171:29'$}} -// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:271:3'$}} +// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:3'$}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits