llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Younan Zhang (zyn0217) <details> <summary>Changes</summary> Since the function template isn't instantiated before constraint checking, we'll not be able to find the outer template arguments through function specialization when evaluating the inner constraint that is nested within a larger constraint expression. The only practical solution is to get them back through the code synthesis context, which also allows us to eliminate an overload of getTemplateInstantiationArgs. No release note because it's a regression on trunk. Fixes https://github.com/llvm/llvm-project/issues/147772 --- Full diff: https://github.com/llvm/llvm-project/pull/147894.diff 4 Files Affected: - (modified) clang/include/clang/Sema/Sema.h (-12) - (modified) clang/lib/Sema/SemaConcept.cpp (+11-7) - (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+17-17) - (modified) clang/test/SemaTemplate/concepts-lambda.cpp (+18) ``````````diff diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9397546c8fc5d..baf545932fd14 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13315,18 +13315,6 @@ class Sema final : public SemaBase { /// \param ForDefaultArgumentSubstitution indicates we should continue looking /// when encountering a specialized member function template, rather than /// returning immediately. - void getTemplateInstantiationArgs( - MultiLevelTemplateArgumentList &Result, const NamedDecl *D, - const DeclContext *DC = nullptr, bool Final = false, - std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, - bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, - bool ForConstraintInstantiation = false, - bool SkipForSpecialization = false, - bool ForDefaultArgumentSubstitution = false); - - /// This creates a new \p MultiLevelTemplateArgumentList and invokes the other - /// overload with it as the first parameter. Prefer this overload in most - /// situations. MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 1594b4423e4d2..d50a6c0f0d8d3 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1078,15 +1078,19 @@ static bool CheckFunctionConstraintsWithoutInstantiation( // template. We need the entire list, since the constraint is completely // uninstantiated at this point. - // FIXME: Add TemplateArgs through the 'Innermost' parameter once - // the refactoring of getTemplateInstantiationArgs() relands. - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(Template, {}, /*Final=*/false); - SemaRef.getTemplateInstantiationArgs( - MLTAL, /*D=*/FD, FD, + // getTemplateInstantiationArgs uses this instantiation context to find out + // template arguments for uninstantiated functions. + std::optional<Sema::InstantiatingTemplate> Inst( + std::in_place, SemaRef, PointOfInstantiation, + Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs, + PointOfInstantiation); + if (Inst->isInvalid()) + return true; + MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs( + /*D=*/FD, FD, /*Final=*/false, /*Innermost=*/{}, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); - MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs); + Inst = std::nullopt; Sema::ContextRAII SavedContext(SemaRef, FD); std::optional<Sema::CXXThisScopeRAII> ThisScope; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index a1c7143e3b874..c7e26702ba097 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -309,10 +309,24 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) return Response::Done(); - } else if (Function->getDescribedFunctionTemplate()) { + } else if (auto *Template = Function->getDescribedFunctionTemplate()) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); + if (ForConstraintInstantiation) { + for (auto &Inst : llvm::reverse(SemaRef.CodeSynthesisContexts)) { + if (Inst.Kind == Sema::CodeSynthesisContext::ConstraintsCheck && + Inst.Entity == Template) { + // After CWG2369, the outer templates are not instantiated when + // checking its associated constraints. So add them back through the + // synthesis context; this is useful for e.g. nested constraints + // involving lambdas. + Result.addOuterTemplateArguments(Template, Inst.template_arguments(), + /*Final=*/false); + break; + } + } + } } // If this is a friend or local declaration and it declares an entity at // namespace scope, take arguments from its lexical parent @@ -474,21 +488,6 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; - getTemplateInstantiationArgs( - Result, ND, DC, Final, Innermost, RelativeToPrimary, Pattern, - ForConstraintInstantiation, SkipForSpecialization, - ForDefaultArgumentSubstitution); - return Result; -} - -void Sema::getTemplateInstantiationArgs( - MultiLevelTemplateArgumentList &Result, const NamedDecl *ND, - const DeclContext *DC, bool Final, - std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary, - const FunctionDecl *Pattern, bool ForConstraintInstantiation, - bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) { - assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); - // Accumulate the set of template argument lists in this structure. using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; @@ -548,12 +547,13 @@ void Sema::getTemplateInstantiationArgs( } if (R.IsDone) - return; + return Result; if (R.ClearRelativeToPrimary) RelativeToPrimary = false; assert(R.NextDecl); CurDecl = R.NextDecl; } + return Result; } bool Sema::CodeSynthesisContext::isInstantiationRecord() const { diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index 1f67c2511e096..d3702b9be1cb0 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -340,3 +340,21 @@ void foo() { } } + +namespace GH147772 { + +template<int...> +struct seq {}; + +using arr = char[1]; + +struct foo { + template<int... i> + constexpr foo(seq<i...>) requires requires { + arr { [](auto) requires(i, true) { return 0; }(i)... }; + } {} +}; + +constexpr auto bar = foo(seq<0>()); + +} `````````` </details> https://github.com/llvm/llvm-project/pull/147894 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits