Author: Alexander Kornienko Date: 2026-05-22T17:10:06Z New Revision: 4ad14a07f5704b950131f361edf05b96a871dfa2
URL: https://github.com/llvm/llvm-project/commit/4ad14a07f5704b950131f361edf05b96a871dfa2 DIFF: https://github.com/llvm/llvm-project/commit/4ad14a07f5704b950131f361edf05b96a871dfa2.diff LOG: Revert "[Clang] Transform lambda's constraints when instantiating parameter mapping (#195995)" (#199228) This reverts commit 7e2821e025f8ee4add31693ddf462947d7618016, which causes a crash-on-valid in clang: https://github.com/llvm/llvm-project/issues/199209 Added: Modified: clang/lib/Parse/ParseTemplate.cpp clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaTemplate/concepts-lambda.cpp clang/test/SemaTemplate/concepts.cpp Removed: ################################################################################ diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index dbc7cbc6cdc0c..330a9c6aea0c5 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -533,6 +533,12 @@ bool Parser::isTypeConstraintAnnotation() { bool Parser::TryAnnotateTypeConstraint() { if (!getLangOpts().CPlusPlus20) return false; + // The type constraint may declare template parameters, notably + // if it contains a generic lambda, so we need to increment + // the template depth as these parameters would not be instantiated + // at the current depth. + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + ++CurTemplateDepthTracker; CXXScopeSpec SS; bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index ac1c716b5c385..ea03c3f408986 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -732,8 +732,17 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( // i.e they should not have access to the current class object or its // non-public members. std::optional<Sema::ContextRAII> ConceptContext; - if (ParentConcept) + if (ParentConcept) { ConceptContext.emplace(S, ParentConcept->getDeclContext()); + // FIXME: the evaluation context should learn to track template arguments + // separately from a Decl. + EvaluationContext.emplace( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/ + ImplicitConceptSpecializationDecl::Create( + S.Context, ParentConcept->getDeclContext(), + ParentConcept->getBeginLoc(), SubstitutedOutermost)); + } Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( @@ -2296,14 +2305,6 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { } assert(!ArgsAsWritten); const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr(); - // Make sure that lambdas within template arguments live in a - // dependent context such that they are assured to be transformed during - // constraint evaluation. - EnterExpressionEvaluationContext EECtx( - SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated, - /*LambdaContextDecl=*/ - const_cast<ImplicitConceptSpecializationDecl *>( - CSE->getSpecializationDecl())); SmallVector<TemplateArgument> InnerArgs(CSE->getTemplateArguments()); ConceptDecl *Concept = CSE->getNamedConcept(); if (RemovePacksForFoldExpr) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index e2aecd21bed70..5667cf53fee0b 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4932,7 +4932,7 @@ ExprResult Sema::CheckConceptTemplateId( LocalInstantiationScope Scope(*this); EnterExpressionEvaluationContext EECtx{ - *this, ExpressionEvaluationContext::Unevaluated}; + *this, ExpressionEvaluationContext::Unevaluated, CSD}; Error = CheckConstraintSatisfaction( NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index c04fff6cbd964..1544fd56b82ae 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5161,6 +5161,20 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, return true; MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted, /*Final=*/true); + // Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so + // that the template arguments of the constraint can be preserved. For + // example: + // + // template <class T> + // concept C = []<D U = void>() { return true; }(); + // + // We need the argument for T while evaluating type constraint D in + // building the CallExpr to the lambda. + EnterExpressionEvaluationContext EECtx( + S, Sema::ExpressionEvaluationContext::Unevaluated, + ImplicitConceptSpecializationDecl::Create( + S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(), + CTAI.SugaredConverted)); if (S.CheckConstraintSatisfaction( Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 3c9ffdb42eeec..f168c99d1ac1a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1753,24 +1753,9 @@ namespace { if (TA.isDependent()) return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; } - if (auto *CD = dyn_cast_if_present<ImplicitConceptSpecializationDecl>( - LSI->Lambda->getLambdaContextDecl())) { - if (llvm::any_of(CD->getTemplateArguments(), - [](const auto &TA) { return TA.isDependent(); })) - return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; - } return inherited::ComputeLambdaDependency(LSI); } - ExprResult TransformConstraint(Expr *AC) { - // We don't want the template argument substitution into parameter - // mappings to preserve the outer depths. - if (AC && SemaRef.inConstraintSubstitution()) - return TransformExpr(const_cast<Expr *>(AC)); - - return AC; - } - ExprResult TransformLambdaExpr(LambdaExpr *E) { // Do not rebuild lambdas to avoid creating a new type. // Lambdas have already been processed inside their eval contexts. @@ -1881,24 +1866,12 @@ namespace { TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; - std::optional<MultiLevelTemplateArgumentList> OldMLTAL; - // We need to preserve the lambda depth in parameter mapping. - // Otherwise the template argument deduction would fail, if we reduced the - // depth too early. - if (SemaRef.inParameterMappingSubstitution() && - OrigTPL->getDepth() >= TemplateArgs.getNumSubstitutedLevels()) - OldMLTAL = ForgetSubstitution(); - DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); - TemplateDeclInstantiator DeclInstantiator(getSema(), Owner, TemplateArgs); - // We don't want the template argument substitution into parameter - // mappings to preserve the outer depths. - DeclInstantiator.setEvaluateConstraints( - SemaRef.inConstraintSubstitution() || EvaluateConstraints); - auto *Transformed = DeclInstantiator.SubstTemplateParams(OrigTPL); - if (OldMLTAL) - RememberSubstitution(std::move(*OldMLTAL)); - return Transformed; + TemplateDeclInstantiator DeclInstantiator(getSema(), + /* DeclContext *Owner */ Owner, + TemplateArgs); + DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); + return DeclInstantiator.SubstTemplateParams(OrigTPL); } concepts::TypeRequirement * diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ce0e390e371e1..c9bc613a7c4ea 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4862,13 +4862,6 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { return nullptr; Expr *InstRequiresClause = L->getRequiresClause(); - if (InstRequiresClause && EvaluateConstraints) { - ExprResult E = - SemaRef.SubstConstraintExpr(InstRequiresClause, TemplateArgs); - if (E.isInvalid()) - return nullptr; - InstRequiresClause = E.get(); - } TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 9c0479fe8aa4f..c3bf71dbf10df 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -836,8 +836,6 @@ class TreeTransform { LSI->Lambda->getLambdaDependencyKind()); } - ExprResult TransformConstraint(Expr *AC) { return AC; } - QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); @@ -16069,16 +16067,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { assert(FPTL && "Not a FunctionProtoType?"); AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause(); - if (TRC) { - ExprResult E = getDerived().TransformConstraint( - const_cast<Expr *>(TRC.ConstraintExpr)); - if (E.isInvalid()) - return E; - TRC.ConstraintExpr = E.get(); - } - // If the concept refers to any outer parameter packs, we track the - // SubstIndex for evaluation. - // FIXME: This seems unnecessary after transforming lambda constraints. + // If the concept refers to any outer parameter packs, we track the SubstIndex + // for evaluation. if (TRC && TRC.ConstraintExpr->containsUnexpandedParameterPack() && !TRC.ArgPackSubstIndex) TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex; diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp index a08cd52843c73..5a19105a5bf51 100644 --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -56,7 +56,9 @@ namespace GH57971 { function_ptr ptr = f<void>; } -namespace GH58368 { +// GH58368: A lambda defined in a concept requires we store +// the concept as a part of the lambda context. +namespace LambdaInConcept { using size_t = unsigned long; template<size_t...Ts> @@ -368,35 +370,3 @@ void test() { f<42>(); } } - -namespace GH193944 { - -template<auto L, typename... Ts> -concept pass_a_concept_inside_a_lambda = requires { L.template operator()<Ts...>(); }; // #requires_pass_a_concept_inside_a_lambda - -template<auto Pred, typename... Ts> -concept PredicateFor_bad = pass_a_concept_inside_a_lambda<[]<typename... Xs> // #pass_a_concept_inside_a_lambda - requires(__is_same(decltype(Pred.template operator()<Xs>()), bool) and ...) - {}, - Ts...>; - -template<auto Pred, typename... Ts> - requires PredicateFor_bad<Pred, Ts...> // #PredicateFor_bad -constexpr const unsigned count_if_v_bad = - [] { return (Pred.template operator()<Ts>() + ... + 0); }(); - -constexpr const auto L = []<typename T> -{ return __is_same(T, long); }; - -constexpr const auto L2 = []<typename T> -{ return 114514; }; - -static_assert(count_if_v_bad<L, double, int, long, void> == 1); - -static_assert(count_if_v_bad<L2, double> == 1); -// expected-error@-1 {{constraints not satisfied}} -// expected-note@#PredicateFor_bad {{evaluated to false}} -// expected-note@#pass_a_concept_inside_a_lambda {{evaluated to false}} -// expected-note@#requires_pass_a_concept_inside_a_lambda {{no matching member function}} - -} diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 0e9fefdc081ed..1a0834452cbdb 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1567,12 +1567,9 @@ template<generic_range_value<[]< >() {}> T> void x() {} -// FIXME: Crashes because it produces a template type parameter with invalid depth -#if 0 void foo() { x<vector<int>>(); } -#endif } namespace GH162770 { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
