From 8b10a5e801a4b4855562e99a690d8f96c579b0e0 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Tue, 1 Oct 2024 20:50:26 -0300 Subject: [PATCH] [clang] Finish implementation of P0522 (#96023) This finishes the clang implementation of P0522, getting rid of the fallback to the old, pre-P0522 rules. Before this patch, when partial ordering template template parameters, we would perform, in order: * If the old rules would match, we would accept it. Otherwise, don't generate diagnostics yet. * If the new rules would match, just accept it. Otherwise, don't generate any diagnostics yet again. * Apply the old rules again, this time with diagnostics. This situation was far from ideal, as we would sometimes: * Accept some things we shouldn't. * Reject some things we shouldn't. * Only diagnose rejection in terms of the old rules. With this patch, we apply the P0522 rules throughout. This needed to extend template argument deduction in order to accept the historial rule for TTP matching pack parameter to non-pack arguments. This change also makes us accept some combinations of historical and P0522 allowances we wouldn't before. It also fixes a bunch of bugs that were documented in the test suite, which I am not sure there are issues already created for them. This causes a lot of changes to the way these failures are diagnosed, with related test suite churn. The problem here is that the old rules were very simple and non-recursive, making it easy to provide customized diagnostics, and to keep them consistent with each other. The new rules are a lot more complex and rely on template argument deduction, substitutions, and they are recursive. The approach taken here is to mostly rely on existing diagnostics, and create a new instantiation context that keeps track of things. So for example when a substitution failure occurs, we use the error produced there unmodified, and just attach notes to it explaining that it occurred in the context of partial ordering this template argument against that template parameter. This diverges from the old diagnostics, which would lead with an error pointing to the template argument, explain the problem in subsequent notes, and produce a final note pointing to the parameter. --- clang/docs/ReleaseNotes.rst | 10 + .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Sema/Sema.h | 14 +- clang/lib/Frontend/FrontendActions.cpp | 2 + clang/lib/Sema/SemaTemplate.cpp | 94 ++--- clang/lib/Sema/SemaTemplateDeduction.cpp | 353 +++++++++++++----- clang/lib/Sema/SemaTemplateInstantiate.cpp | 15 + .../temp/temp.arg/temp.arg.template/p3-0x.cpp | 31 +- clang/test/CXX/temp/temp.param/p12.cpp | 21 +- clang/test/Modules/cxx-templates.cpp | 15 +- clang/test/SemaCXX/make_integer_seq.cpp | 5 +- clang/test/SemaTemplate/cwg2398.cpp | 160 +++++++- clang/test/SemaTemplate/temp_arg_nontype.cpp | 26 +- clang/test/SemaTemplate/temp_arg_template.cpp | 38 +- .../SemaTemplate/temp_arg_template_p0522.cpp | 82 ++-- .../Templight/templight-empty-entries-fix.cpp | 12 + .../templight-prior-template-arg.cpp | 33 +- 17 files changed, 652 insertions(+), 266 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bf128e370b076e..c351ccff0b6ca0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -173,6 +173,10 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +C++17 Feature Support +^^^^^^^^^^^^^^^^^^^^^ +- The implementation of the relaxed template template argument matching rules is + more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -330,6 +334,10 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. +- Clang now properly explains the reason a template template argument failed to + match a template template parameter, in terms of the C++17 relaxed matching rules + instead of the old ones. + - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -427,6 +435,8 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). +- Fixes to several issues in partial ordering of template template parameters, which + were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1d9c0a4d782a77..81cd0d4ed5cf1e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5262,6 +5262,13 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; +def note_template_arg_template_params_mismatch : Note< + "template template argument has different template parameters than its " + "corresponding template template parameter">; +def err_non_deduced_mismatch : Error< + "could not match %diff{$ against $|types}0,1">; +def err_inconsistent_deduction : Error< + "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 932f3e8fb957a3..3d9f12d45d646e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12484,8 +12484,9 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool IsDeduced); /// Mark which template parameters are used in a given expression. /// @@ -12794,6 +12795,9 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, + + /// We are performing partial ordering for template template parameters. + PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -13015,6 +13019,12 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); + struct PartialOrderingTTP {}; + /// \brief Note that we are partial ordering template template parameters. + InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, + PartialOrderingTTP, TemplateDecl *PArg, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 64f90c493c1055..e4b462b9b0fd81 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -457,6 +457,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; + case CodeSynthesisContext::PartialOrderingTTP: + return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c7d48b81bc0347..226c1172a059d4 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5502,8 +5502,7 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - // FIXME: Don't ignore parameter packs. - if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { + if (ParamIdx == DefaultArgs.StartPos) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5728,8 +5727,9 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - if (ArgIdx < NumArgs && CurrentInstantiationScope && - CurrentInstantiationScope->getPartiallySubstitutedPack()) { + if (PartialOrderingTTP || + (CurrentInstantiationScope && + CurrentInstantiationScope->getPartiallySubstitutedPack())) { while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); @@ -7327,64 +7327,46 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } + if (!getLangOpts().RelaxedTemplateTemplateArgs) + return !TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, /*Complain=*/true, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); + // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (getLangOpts().RelaxedTemplateTemplateArgs) { - // Quick check for the common case: - // If P contains a parameter pack, then A [...] matches P if each of A's - // template parameters matches the corresponding template parameter in - // the template-parameter-list of P. - if (TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, false, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && - // If the argument has no associated constraints, then the parameter is - // definitely at least as specialized as the argument. - // Otherwise - we need a more thorough check. - !Template->hasAssociatedConstraints()) - return false; - - if (isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++2a[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) + return true; + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++20[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - Template->getAssociatedConstraints(TemplateAC); + Template->getAssociatedConstraints(TemplateAC); - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) - << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; - } - return false; - } - // FIXME: Produce better diagnostics for deduction failures. + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; } - - return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), - Params, - true, - TPL_TemplateTemplateArgumentMatch, - Arg.getLocation()); + return false; } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index cc095ae67ac400..0a973a3a0ce036 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,7 +145,9 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -enum class PackFold { ParameterToArgument, ArgumentToParameter }; +/// What directions packs are allowed to match non-packs. +enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -1711,7 +1713,21 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - Info.Param = cast(TemplateParams->getParam(Index)); + // We can also get inconsistencies when matching NTTP type. + switch (NamedDecl *Param = TemplateParams->getParam(Index); + Param->getKind()) { + case Decl::TemplateTypeParm: + Info.Param = cast(Param); + break; + case Decl::NonTypeTemplateParm: + Info.Param = cast(Param); + break; + case Decl::TemplateTemplateParm: + Info.Param = cast(Param); + break; + default: + llvm_unreachable("unexpected kind"); + } Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2549,8 +2565,31 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { + case TemplateArgument::Expression: { + const Expr *E = A.getAsExpr(); + // When checking NTTP, if either the parameter or the argument is + // dependent, as there would be otherwise nothing to deduce, we force + // the argument to the parameter type using this dependent implicit + // cast, in order to maintain invariants. Now we can deduce the + // resulting type from the original type, and deduce the original type + // against the parameter we are checking. + if (const auto *ICE = dyn_cast(E); + ICE && ICE->getCastKind() == clang::CK_Dependent) { + E = ICE->getSubExpr(); + if (auto Result = DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, ICE->getType(), E->getType(), Info, + Deduced, TDF_SkipNonDependent, + PartialOrdering ? PartialOrderingKind::NonCall + : PartialOrderingKind::None, + /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; + } + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), + Info, PartialOrdering, Deduced, HasDeducedAnyParam); + } case TemplateArgument::Integral: - case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2639,50 +2678,75 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Ps, As); + bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || + PackFold == PackFold::Both, + FoldPackArgument = PackFold == PackFold::ArgumentToParameter || + PackFold == PackFold::Both; + // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (hasPackExpansionBeforeEnd(Ps)) + if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) + return TemplateDeductionResult::Success; + + if (FoldPackArgument && hasPackExpansionBeforeEnd(As)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - unsigned ArgIdx = 0, ParamIdx = 0; - for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { - const TemplateArgument &P = Ps[ParamIdx]; - if (!P.isPackExpansion()) { + for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) + ? TemplateDeductionResult::MiscellaneousDeductionFailure + : TemplateDeductionResult::Success; + + if (!Ps[ParamIdx].isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return NumberOfArgumentsMustMatch + return !FoldPackArgument && NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion [and] - // Pi is not a pack expansion, template argument deduction fails. - if (As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (As[ArgIdx].isPackExpansion()) { + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion + // [and] Pi is not a pack expansion, template argument deduction + // fails. + if (!FoldPackArgument) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + + TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); + for (;;) { + // Deduce template parameters from the pattern. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], Pattern, Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Perform deduction for this Pi/Ai pair. - TemplateArgument Pi = P, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + ++ParamIdx; + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return TemplateDeductionResult::Success; + if (Ps[ParamIdx].isPackExpansion()) + break; + } + } else { + // Perform deduction for this Pi/Ai pair. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Move to the next argument. - ++ArgIdx; - continue; + ++ArgIdx; + ++ParamIdx; + continue; + } } // The parameter is a pack expansion. @@ -2692,7 +2756,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = P.getPackExpansionPattern(); + TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2703,13 +2767,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); + if (!FoldPackParameter && !As[ArgIdx].isPackExpansion()) + return TemplateDeductionResult::MiscellaneousDeductionFailure; // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, + Deduced, HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2718,12 +2781,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - if (auto Result = PackScope.finish(); - Result != TemplateDeductionResult::Success) - return Result; + return PackScope.finish(); } - - return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -3302,7 +3361,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3320,20 +3378,36 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Check that we produced the correct argument list. TemplateParameterList *TemplateParams = Template->getTemplateParameters(); + auto isSame = [&](unsigned I, const TemplateArgument &P, + const TemplateArgument &A) { + if (isSameTemplateArg(S.Context, P, A, PartialOrdering, + /*PackExpansionMatchesPack=*/true)) + return true; + Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); + Info.FirstArg = P; + Info.SecondArg = A; + return false; + }; for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - TemplateArgument InstArg = CanonicalBuilder[I]; - if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, - /*PackExpansionMatchesPack=*/true)) { - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = TemplateArgs[I]; - Info.SecondArg = InstArg; - return TemplateDeductionResult::NonDeducedMismatch; + const TemplateArgument &P = TemplateArgs[I]; + if (P.isPackExpansion()) { + assert(I == TemplateArgs.size() - 1); + for (/**/; I != E; ++I) { + const TemplateArgument &A = CanonicalBuilder[I]; + if (A.getKind() == TemplateArgument::Pack) { + for (const TemplateArgument &Ai : A.getPackAsArray()) + if (!isSame(I, P, Ai)) + return TemplateDeductionResult::NonDeducedMismatch; + } else if (!isSame(I, P, A)) { + return TemplateDeductionResult::NonDeducedMismatch; + } + } + break; } + if (!isSame(I, P, CanonicalBuilder[I])) + return TemplateDeductionResult::NonDeducedMismatch; } - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( S, Template, SugaredBuilder, CanonicalBuilder, Info); @@ -3354,7 +3428,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3363,20 +3436,13 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, + S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, CanonicalBuilder); Result != TemplateDeductionResult::Success) return Result; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); - Result != TemplateDeductionResult::Success) - return Result; - - return TemplateDeductionResult::Success; + return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); } /// Perform template argument deduction to determine whether the given template @@ -3423,16 +3489,20 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } TemplateDeductionResult @@ -3488,14 +3558,18 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } /// Determine whether the given type T is a simple-template-id type. @@ -6136,14 +6210,23 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - bool AtLeastAsSpecialized; + + Sema::SFINAETrap Trap(S); + + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), - Deduced, Info) == TemplateDeductionResult::Success; + Result = ::FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, + Info); }); - return AtLeastAsSpecialized; + + if (Result != TemplateDeductionResult::Success) + return false; + + if (Trap.hasErrorOccurred()) + return false; + + return true; } namespace { @@ -6381,8 +6464,9 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool IsDeduced) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6394,6 +6478,12 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); + Sema::InstantiatingTemplate Inst( + *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, + SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); + if (Inst.isInvalid()) + return false; + // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6408,8 +6498,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // templates. SmallVector PArgs; { - SFINAETrap Trap(*this); - Context.getInjectedTemplateArgs(P, PArgs); TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); @@ -6429,18 +6517,17 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector SugaredPArgs; - if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, - SugaredPArgs, PArgs, + SmallVector CanonicalPArgs; + if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false, + PArgs, CanonicalPArgs, /*UpdateArgsWithConversions=*/true, /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderTTP=*/true) || - Trap.hasErrorOccurred()) + /*PartialOrderingTTP=*/true)) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(Loc, A->getDepth()); + TemplateDeductionInfo Info(ArgLoc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6455,29 +6542,89 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, - /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter - : PackFold::ParameterToArgument, - /*HasDeducedAnyParam=*/nullptr) != - TemplateDeductionResult::Success) + switch (::DeduceTemplateArguments( + *this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, + IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both, + /*HasDeducedAnyParam=*/nullptr)) { + case clang::TemplateDeductionResult::Success: + break; + + case TemplateDeductionResult::MiscellaneousDeductionFailure: + Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) + << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true + << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); + return false; + case TemplateDeductionResult::NonDeducedMismatch: + Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) + << Info.FirstArg << Info.SecondArg; return false; + case TemplateDeductionResult::Inconsistent: + Diag(getAsNamedDecl(Info.Param)->getLocation(), + diag::err_inconsistent_deduction) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::AlreadyDiagnosed: + return false; + + // None of these should happen for a plain deduction. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::InstantiationDepth: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); - Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, - Info); - if (Inst.isInvalid()) - return false; - bool AtLeastAsSpecialized; + TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == - TemplateDeductionResult::Success; + TDK = ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); }); - return AtLeastAsSpecialized; + switch (TDK) { + case TemplateDeductionResult::Success: + return true; + + // It doesn't seem possible to get a non-deduced mismatch when partial + // ordering TTPs. + case TemplateDeductionResult::NonDeducedMismatch: + llvm_unreachable("Unexpected NonDeducedMismatch"); + + // Substitution failures should have already been diagnosed. + case TemplateDeductionResult::AlreadyDiagnosed: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::InstantiationDepth: + return false; + + // None of these should happen when just converting deduced arguments. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Inconsistent: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::MiscellaneousDeductionFailure: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } + llvm_unreachable("Unexpected TDK"); } namespace { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index b36381422851f8..898255ff7c6a32 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -572,6 +572,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case LambdaExpressionSubstitution: case BuildingDeductionGuides: case TypeAliasTemplateInstantiation: + case PartialOrderingTTP: return false; // This function should never be called when Kind's value is Memoization. @@ -804,6 +805,11 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( SemaRef, CodeSynthesisContext::BuildingDeductionGuides, PointOfInstantiation, InstantiationRange, Entity) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation ArgLoc, PartialOrderingTTP, + TemplateDecl *PArg, SourceRange InstantiationRange) + : InstantiatingTemplate(SemaRef, CodeSynthesisContext::PartialOrderingTTP, + ArgLoc, InstantiationRange, PArg) {} void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext; @@ -1243,6 +1249,14 @@ void Sema::PrintInstantiationStack() { << cast(Active->Entity) << Active->InstantiationRange; break; + case CodeSynthesisContext::PartialOrderingTTP: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_arg_template_params_mismatch); + if (SourceLocation ParamLoc = Active->Entity->getLocation(); + ParamLoc.isValid()) + Diags.Report(ParamLoc, diag::note_template_prev_declaration) + << /*isTemplateTemplateParam=*/true << Active->InstantiationRange; + break; } } } @@ -1285,6 +1299,7 @@ std::optional Sema::isSFINAEContext() const { case CodeSynthesisContext::PriorTemplateArgumentSubstitution: case CodeSynthesisContext::DefaultTemplateArgumentChecking: case CodeSynthesisContext::RewritingOperatorAsSpaceship: + case CodeSynthesisContext::PartialOrderingTTP: // A default template argument instantiation and substitution into // template parameters with arguments for prior parameters may or may // not be a SFINAE context; look further up the stack. diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp index 19793fe8263726..1bbbd1d3429ddd 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp @@ -2,13 +2,13 @@ template struct eval; // expected-note 3{{template is declared here}} -template