https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/199671
Register implicit concept specializations in the instantiation scope, so that Subst* nodes are properly handled during transform. - ImplicitConceptSpecializationDecl was modified (added a reference to the concept, made it a NamedDecl) - so that it was usable with the findinstantiation machinery, etc. - ConceptSpecializationExprs are always substituted, not just when we need them for diagnostics (might have a small perf impact) #Fixes 196375 >From 003df0a75ccd73bb8970dc6ad103d40d3d1d35f9 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Tue, 26 May 2026 14:43:39 +0200 Subject: [PATCH] [Clang] Handle references to concept in Subst* nodes --- clang/include/clang/AST/DeclTemplate.h | 7 +- clang/include/clang/Basic/DeclNodes.td | 2 +- clang/lib/AST/ASTImporter.cpp | 7 +- clang/lib/AST/DeclTemplate.cpp | 19 ++-- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 2 +- clang/lib/Sema/SemaConcept.cpp | 99 ++++++++++++------- clang/lib/Sema/SemaTemplate.cpp | 2 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 2 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +- clang/lib/Serialization/ASTReaderDecl.cpp | 3 +- clang/lib/Serialization/ASTWriterDecl.cpp | 5 +- clang/test/SemaTemplate/concepts.cpp | 19 ++++ 12 files changed, 117 insertions(+), 53 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 6ca9d67af0710..0cb7b36d070d0 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3234,23 +3234,28 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> { // arguments, so we can later use this to reconstitute the template arguments // during constraint checking. class ImplicitConceptSpecializationDecl final - : public Decl, + : public NamedDecl, private llvm::TrailingObjects<ImplicitConceptSpecializationDecl, TemplateArgument> { + const TemplateDecl *Template; unsigned NumTemplateArgs; ImplicitConceptSpecializationDecl(DeclContext *DC, SourceLocation SL, + const TemplateDecl *Template, ArrayRef<TemplateArgument> ConvertedArgs); ImplicitConceptSpecializationDecl(EmptyShell Empty, unsigned NumTemplateArgs); public: static ImplicitConceptSpecializationDecl * Create(const ASTContext &C, DeclContext *DC, SourceLocation SL, + const TemplateDecl *Template, ArrayRef<TemplateArgument> ConvertedArgs); static ImplicitConceptSpecializationDecl * CreateDeserialized(const ASTContext &C, GlobalDeclID ID, unsigned NumTemplateArgs); + const TemplateDecl *getSpecializedTemplate() const { return Template; } + ArrayRef<TemplateArgument> getTemplateArguments() const { return getTrailingObjects(NumTemplateArgs); } diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index ffb58b43812dc..c983f667040de 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -91,7 +91,7 @@ def Named : DeclNode<Decl, "named declarations", 1>; def ObjCImplementation : DeclNode<ObjCImpl>; def ObjCProperty : DeclNode<Named, "Objective-C properties">; def ObjCCompatibleAlias : DeclNode<Named>; -def ImplicitConceptSpecialization : DeclNode<Decl>; +def ImplicitConceptSpecialization : DeclNode<Named>; def LinkageSpec : DeclNode<Decl>, DeclContext; def Export : DeclNode<Decl>, DeclContext; def ObjCPropertyImpl : DeclNode<Decl>; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 0d8243a6bd74b..53f9b42d1f37a 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7052,12 +7052,17 @@ ExpectedDecl ASTNodeImporter::VisitImplicitConceptSpecializationDecl( if (Err) return std::move(Err); + const TemplateDecl *Concept; + if ((Err = importInto(Concept, D->getSpecializedTemplate()))) + return std::move(Err); + SmallVector<TemplateArgument, 2> ToArgs(D->getTemplateArguments().size()); if (Error Err = ImportTemplateArguments(D->getTemplateArguments(), ToArgs)) return std::move(Err); ImplicitConceptSpecializationDecl *To; - if (GetImportedOrCreateDecl(To, D, Importer.getToContext(), DC, ToSL, ToArgs)) + if (GetImportedOrCreateDecl(To, D, Importer.getToContext(), DC, ToSL, Concept, + ToArgs)) return To; To->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(To); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 275fe364e306d..2de8f0faa395e 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1127,24 +1127,25 @@ ConceptDecl *ConceptDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { // ImplicitConceptSpecializationDecl Implementation //===----------------------------------------------------------------------===// ImplicitConceptSpecializationDecl::ImplicitConceptSpecializationDecl( - DeclContext *DC, SourceLocation SL, + DeclContext *DC, SourceLocation SL, const TemplateDecl *Template, ArrayRef<TemplateArgument> ConvertedArgs) - : Decl(ImplicitConceptSpecialization, DC, SL), - NumTemplateArgs(ConvertedArgs.size()) { + : NamedDecl(ImplicitConceptSpecialization, DC, SL, Template->getDeclName()), + Template(Template), NumTemplateArgs(ConvertedArgs.size()) { setTemplateArguments(ConvertedArgs); } ImplicitConceptSpecializationDecl::ImplicitConceptSpecializationDecl( EmptyShell Empty, unsigned NumTemplateArgs) - : Decl(ImplicitConceptSpecialization, Empty), + : NamedDecl(ImplicitConceptSpecialization, /*DC=*/nullptr, SourceLocation(), + DeclarationName()), NumTemplateArgs(NumTemplateArgs) {} ImplicitConceptSpecializationDecl *ImplicitConceptSpecializationDecl::Create( const ASTContext &C, DeclContext *DC, SourceLocation SL, - ArrayRef<TemplateArgument> ConvertedArgs) { + const TemplateDecl *Concept, ArrayRef<TemplateArgument> ConvertedArgs) { return new (C, DC, additionalSizeToAlloc<TemplateArgument>(ConvertedArgs.size())) - ImplicitConceptSpecializationDecl(DC, SL, ConvertedArgs); + ImplicitConceptSpecializationDecl(DC, SL, Concept, ConvertedArgs); } ImplicitConceptSpecializationDecl * @@ -1721,6 +1722,12 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) { return {Info->getTemplate()->getTemplateParameters()->getParam(Index), Info->TemplateArguments->asArray()[Index]}; } + case Decl::Kind::ImplicitConceptSpecialization: { + const auto *Spec = cast<ImplicitConceptSpecializationDecl>(D); + return {Spec->getSpecializedTemplate()->getTemplateParameters()->getParam( + Index), + Spec->getTemplateArguments()[Index]}; + } default: llvm_unreachable("Unhandled templated declaration kind"); } diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index de170c86400d2..1123c44aba74c 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -365,7 +365,7 @@ TemplateParameterListBuilder::constructConceptSpecializationExpr( ImplicitConceptSpecializationDecl *ImplicitCSEDecl = ImplicitConceptSpecializationDecl::Create( - Context, Builder.Record->getDeclContext(), Loc, {CSETA}); + Context, Builder.Record->getDeclContext(), Loc, CD, {CSETA}); // Constraint satisfaction is used to construct the // ConceptSpecailizationExpr, and represents the 2nd Template Argument, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index ea03c3f408986..f1d6adca0c55c 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1022,13 +1022,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred()) return ExprError(); - if (Size != Satisfaction.Details.size()) { + //if (Size != Satisfaction.Details.size()) { Satisfaction.Details.insert( Satisfaction.Details.begin() + Size, UnsatisfiedConstraintRecord( SubstitutedConceptId.getAs<ConceptSpecializationExpr>() ->getConceptReference())); - } + //} return SubstitutedConceptId; } @@ -1037,6 +1037,58 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( const MultiLevelTemplateArgumentList &MLTAL) { const ConceptReference *ConceptId = Constraint.getConceptId(); + + llvm::SaveAndRestore PushConceptDecl( + ParentConcept, cast<ConceptDecl>(ConceptId->getNamedConcept())); + + unsigned Size = Satisfaction.Details.size(); + + auto EvaluateCSE = [&] { + UnsignedOrNone OuterPackSubstIndex = getOuterPackIndex(Constraint); + llvm::FoldingSetNodeID ID; + ID.AddPointer(Constraint.getConceptId()); + ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation()); + HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex) + .VisitConstraint(Constraint); + + if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID); + Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) { + + auto &Cached = Iter->second.Satisfaction; + Satisfaction.ContainsErrors = Cached.ContainsErrors; + Satisfaction.IsSatisfied = Cached.IsSatisfied; + Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, + Cached.Details.begin(), Cached.Details.end()); + return Iter->second.SubstExpr; + } + + ExprResult CE = EvaluateSlow(Constraint, MLTAL, Size); + + if (CE.isInvalid()) + return ExprError(); + UnsubstitutedConstraintSatisfactionCacheResult Cache; + Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors; + Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied; + Cache.Satisfaction.Details.insert(Cache.Satisfaction.Details.end(), + Satisfaction.Details.begin() + Size, + Satisfaction.Details.end()); + Cache.SubstExpr = CE; + S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)}); + if (CE.isInvalid()) + return ExprError(); + return CE; + }; + + auto Res = EvaluateCSE(); + LocalInstantiationScope Scope(S); + if (Res.isUsable()) { + if (const auto *CSE = Res.getAs<ConceptSpecializationExpr>()) { + Scope.InstantiatedLocal(ConceptId->getNamedConcept(), + const_cast<ImplicitConceptSpecializationDecl *>( + CSE->getSpecializationDecl())); + } + } + Sema::InstantiatingTemplate InstTemplate( S, ConceptId->getBeginLoc(), Sema::InstantiatingTemplate::ConstraintsCheck{}, @@ -1052,11 +1104,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( if (InstTemplate.isInvalid()) return ExprError(); - unsigned Size = Satisfaction.Details.size(); - - llvm::SaveAndRestore PushConceptDecl( - ParentConcept, cast<ConceptDecl>(ConceptId->getNamedConcept())); - ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL); if (E.isInvalid()) { @@ -1067,39 +1114,10 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( // ConceptIdConstraint is only relevant for diagnostics, // so if the normalized constraint is satisfied, we should not // substitute into the constraint. - if (Satisfaction.IsSatisfied) + if (Res.isInvalid()) return E; - UnsignedOrNone OuterPackSubstIndex = getOuterPackIndex(Constraint); - llvm::FoldingSetNodeID ID; - ID.AddPointer(Constraint.getConceptId()); - ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation()); - HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex) - .VisitConstraint(Constraint); - - if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID); - Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) { - - auto &Cached = Iter->second.Satisfaction; - Satisfaction.ContainsErrors = Cached.ContainsErrors; - Satisfaction.IsSatisfied = Cached.IsSatisfied; - Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, - Cached.Details.begin(), Cached.Details.end()); - return Iter->second.SubstExpr; - } - - ExprResult CE = EvaluateSlow(Constraint, MLTAL, Size); - if (CE.isInvalid()) - return E; - UnsubstitutedConstraintSatisfactionCacheResult Cache; - Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors; - Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied; - Cache.Satisfaction.Details.insert(Cache.Satisfaction.Details.end(), - Satisfaction.Details.begin() + Size, - Satisfaction.Details.end()); - Cache.SubstExpr = CE; - S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)}); - return CE; + return Res; } ExprResult ConstraintSatisfactionChecker::Evaluate( @@ -2327,6 +2345,11 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { InnerArgs = std::move(CTAI.SugaredConverted); } + LocalInstantiationScope Scope(SemaRef); + Scope.InstantiatedLocal(CSE->getNamedConcept(), + const_cast<ImplicitConceptSpecializationDecl *>( + CSE->getSpecializationDecl())); + MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs( Concept, Concept->getLexicalDeclContext(), /*Final=*/true, InnerArgs, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5667cf53fee0b..cecead933ec38 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4911,7 +4911,7 @@ ExprResult Sema::CheckConceptTemplateId( // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly! auto *CSD = ImplicitConceptSpecializationDecl::Create( Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(), - CTAI.SugaredConverted); + NamedConcept, CTAI.SugaredConverted); ConstraintSatisfaction Satisfaction; bool AreArgsDependent = TemplateSpecializationType::anyDependentTemplateArguments( diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f168c99d1ac1a..32ff54fe729a3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4685,7 +4685,7 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) { // If we're performing a partial substitution during template argument // deduction, we may not have values for template parameters yet. if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || - isa<TemplateTemplateParmDecl>(D)) + isa<TemplateTemplateParmDecl>(D) || isa<ConceptDecl>(D)) return nullptr; // Local types referenced prior to definition may require instantiation. diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index c9bc613a7c4ea..8fc1d4ca52278 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6933,6 +6933,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, return D; if (isa<ParmVarDecl>(D) || isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || isa<TemplateTemplateParmDecl>(D) || + isa<ConceptDecl>(D) || (ParentDependsOnArgs && (ParentDC->isFunctionOrMethod() || isa<OMPDeclareReductionDecl>(ParentDC) || isa<OMPDeclareMapperDecl>(ParentDC))) || @@ -6963,7 +6964,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, // deduction, we may not have values for template parameters yet. They // just map to themselves. if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || - isa<TemplateTemplateParmDecl>(D)) + isa<TemplateTemplateParmDecl>(D) || isa<ConceptDecl>(D)) return D; if (D->isInvalidDecl()) diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 6815a27537034..fadcc72fdde9d 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2438,7 +2438,8 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl( ImplicitConceptSpecializationDecl *D) { // The size of the template list was read during creation of the Decl, so we // don't have to re-read it here. - VisitDecl(D); + VisitNamedDecl(D); + D->Template = cast<TemplateDecl>(readDecl()); llvm::SmallVector<TemplateArgument, 4> Args; for (unsigned I = 0; I < D->NumTemplateArgs; ++I) Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false)); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 7f5005aa666c7..794447a51e4ba 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1870,8 +1870,11 @@ void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) { void ASTDeclWriter::VisitImplicitConceptSpecializationDecl( ImplicitConceptSpecializationDecl *D) { + Record.push_back(D->getTemplateArguments().size()); - VisitDecl(D); + VisitNamedDecl(D); + Record.AddDeclRef(D->getSpecializedTemplate()); + for (const TemplateArgument &Arg : D->getTemplateArguments()) Record.AddTemplateArgument(Arg); Code = serialization::DECL_IMPLICIT_CONCEPT_SPECIALIZATION; diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 1a0834452cbdb..41d72c0b0ae6c 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1994,3 +1994,22 @@ template <> struct StorageTraits<int> { }; View zbi(GetVmo(string(""))); } + + +namespace GH196375 { + + template <typename En, En value> +concept Small = (value <= 2); + +template <int value> +consteval bool f() + requires(Small<int, value> + // && value == 1 + ) +{ + return true; +} + + +static_assert(f<4>()); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
