[clang] Revert "[Clang] Refactor instantiation of declarations within concepts" (PR #223235)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 13 04:34:00 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Younan Zhang (zyn0217)
<details>
<summary>Changes</summary>
This reverts llvm/llvm-project#<!-- -->221707
This broke std::map as reported in https://github.com/llvm/llvm-project/issues/223220
---
Patch is 24.49 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/223235.diff
7 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (-3)
- (modified) clang/include/clang/Sema/Sema.h (+19-7)
- (modified) clang/include/clang/Sema/Template.h (-2)
- (modified) clang/lib/Sema/SemaConcept.cpp (+144-32)
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+35-79)
- (modified) clang/test/SemaCXX/cxx2c-fold-exprs.cpp (-26)
- (modified) clang/test/SemaTemplate/concepts-lambda.cpp (-16)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index fc7ab3efa9731..043a0ddae2a6c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -589,9 +589,6 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when module directive export module foo not following a
semicolon and there are no rest pp-tokens in current module file. (#GH187771)
-- Fixed concept evaluation bugs where some declarations were not added to
- the current instantiation scope. (#GH198052), (#GH209632)
-
- Fixed a crash when a lambda parameter pack was given a default argument that
is a pack expansion referencing an enclosing function's parameter pack (e.g.
`[](Types... = args...) {}`). Clang now diagnoses the illegal default
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fc8da0ed56005..4ff4c669a6b70 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15129,17 +15129,11 @@ class Sema final : public SemaBase {
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
-private:
- friend class ConstraintSatisfactionChecker;
- friend class SubstituteParameterMappings;
-
- UnsignedOrNone EvaluateFoldExpandedConstraintSize(
- const Expr *Pattern, const MultiLevelTemplateArgumentList &MLTAL);
-
/// Cache the satisfaction of an atomic constraint.
/// The key is based on the unsubstituted expression and the parameter
/// mapping. This lets us not substituting the mapping more than once,
/// which is (very!) expensive.
+ /// FIXME: this should be private.
llvm::DenseMap<llvm::FoldingSetNodeID,
UnsubstitutedConstraintSatisfactionCacheResult>
UnsubstitutedConstraintSatisfactionCache;
@@ -15151,6 +15145,7 @@ class Sema final : public SemaBase {
llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc>
*CurrentCachedTemplateArgs = nullptr;
+private:
/// Caches pairs of template-like decls whose associated constraints were
/// checked for subsumption and whether or not the first's constraints did in
/// fact subsume the second's.
@@ -15171,6 +15166,23 @@ class Sema final : public SemaBase {
// The current stack of constraint satisfactions, so we can exit-early.
llvm::SmallVector<SatisfactionStackEntryTy, 10> SatisfactionStack;
+ /// Used by SetupConstraintCheckingTemplateArgumentsAndScope to set up the
+ /// LocalInstantiationScope of the current non-lambda function. For lambdas,
+ /// use LambdaScopeForCallOperatorInstantiationRAII.
+ bool
+ SetupConstraintScope(FunctionDecl *FD,
+ std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ LocalInstantiationScope &Scope);
+
+ /// Used during constraint checking, sets up the constraint template argument
+ /// lists, and calls SetupConstraintScope to set up the
+ /// LocalInstantiationScope to have the proper set of ParVarDecls configured.
+ std::optional<MultiLevelTemplateArgumentList>
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ LocalInstantiationScope &Scope);
+
///@}
//
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 818528744511d..50e950e56c6ca 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -540,8 +540,6 @@ enum class TemplateSubstitutionKind : char {
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
getInstantiationOfIfExists(const Decl *D);
- LocalInstantiationScope *getOuterScope() const { return Outer; }
-
void InstantiatedLocal(const Decl *D, Decl *Inst);
void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst);
void MakeInstantiatedLocalArgPack(const Decl *D);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 0122e920e5d89..ee17f826dc32c 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -549,9 +549,7 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
}
}
};
-} // namespace
-namespace clang {
class ConstraintSatisfactionChecker {
Sema &S;
const NamedDecl *Template;
@@ -654,6 +652,10 @@ class ConstraintSatisfactionChecker {
EvaluateAtomicConstraint(const Expr *AtomicExpr,
const MultiLevelTemplateArgumentList &MLTAL);
+ UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL);
+
// XXX: It is SLOW! Use it very carefully.
std::optional<MultiLevelTemplateArgumentList> SubstitutionInTemplateArguments(
const NormalizedConstraintWithParamMapping &Constraint,
@@ -698,7 +700,7 @@ class ConstraintSatisfactionChecker {
const MultiLevelTemplateArgumentList &MLTAL);
};
-} // namespace clang
+} // namespace
ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) {
@@ -946,6 +948,31 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
return PMCache.cache(EvaluateSlow(Constraint, MLTAL));
}
+UnsignedOrNone
+ConstraintSatisfactionChecker::EvaluateFoldExpandedConstraintSize(
+ const FoldExpandedConstraint &FE,
+ const MultiLevelTemplateArgumentList &MLTAL) {
+
+ Expr *Pattern = const_cast<Expr *>(FE.getPattern());
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions(std::nullopt);
+ if (S.CheckParameterPacksForExpansion(
+ Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
+ /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion,
+ NumExpansions, /*Diagnose=*/false) ||
+ !Expand || RetainExpansion)
+ return std::nullopt;
+
+ if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions)
+ return std::nullopt;
+ return NumExpansions;
+}
+
ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
const FoldExpandedConstraint &Constraint,
const MultiLevelTemplateArgumentList &MLTAL) {
@@ -966,15 +993,9 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
return ExprError();
}
- UnsignedOrNone NumExpansions(std::nullopt);
- {
- Sema::InstantiatingTemplate InstTemplate(
- S, TemplateNameLoc,
- Sema::InstantiatingTemplate::ConstraintSubstitution{},
- const_cast<NamedDecl *>(Template), Constraint.getSourceRange());
- NumExpansions = S.EvaluateFoldExpandedConstraintSize(
- Constraint.getPattern(), *SubstitutedArgs);
- }
+ ExprResult Out;
+ UnsignedOrNone NumExpansions =
+ EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs);
if (!NumExpansions)
return ExprEmpty();
@@ -983,7 +1004,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
return ExprEmpty();
}
- ExprResult Out;
for (unsigned I = 0; I < *NumExpansions; I++) {
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
@@ -1392,6 +1412,98 @@ SubstituteConceptsInConstraintExpression(Sema &S, const NamedDecl *D,
MLTAL);
}
+bool Sema::SetupConstraintScope(
+ FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ LocalInstantiationScope &Scope) {
+ assert(!isLambdaCallOperator(FD) &&
+ "Use LambdaScopeForCallOperatorInstantiationRAII to handle lambda "
+ "instantiations");
+ if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) {
+ FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
+ InstantiatingTemplate Inst(
+ *this, FD->getPointOfInstantiation(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
+ TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
+ SourceRange());
+ if (Inst.isInvalid())
+ return true;
+
+ // addInstantiatedParametersToScope creates a map of 'uninstantiated' to
+ // 'instantiated' parameters and adds it to the context. For the case where
+ // this function is a template being instantiated NOW, we also need to add
+ // the list of current template arguments to the list so that they also can
+ // be picked out of the map.
+ if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) {
+ MultiLevelTemplateArgumentList JustTemplArgs(FD, SpecArgs->asArray(),
+ /*Final=*/false);
+ if (addInstantiatedParametersToScope(
+ FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs))
+ return true;
+ }
+
+ // If this is a member function, make sure we get the parameters that
+ // reference the original primary template.
+ if (FunctionTemplateDecl *FromMemTempl =
+ PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
+ if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
+ Scope, MLTAL))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization ||
+ FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) {
+ FunctionDecl *InstantiatedFrom =
+ FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization
+ ? FD->getInstantiatedFromMemberFunction()
+ : FD->getInstantiatedFromDecl();
+
+ InstantiatingTemplate Inst(
+ *this, FD->getPointOfInstantiation(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
+ TemplateArgs ? *TemplateArgs : ArrayRef<TemplateArgument>{},
+ SourceRange());
+ if (Inst.isInvalid())
+ return true;
+
+ // Case where this was not a template, but instantiated as a
+ // child-function.
+ if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL))
+ return true;
+ }
+
+ return false;
+}
+
+// This function collects all of the template arguments for the purposes of
+// constraint-instantiation and checking.
+std::optional<MultiLevelTemplateArgumentList>
+Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
+ FunctionDecl *FD, std::optional<ArrayRef<TemplateArgument>> TemplateArgs,
+ LocalInstantiationScope &Scope) {
+ MultiLevelTemplateArgumentList MLTAL;
+
+ // Collect the list of template arguments relative to the 'primary' template.
+ // We need the entire list, since the constraint is completely uninstantiated
+ // at this point.
+ MLTAL =
+ getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
+ /*Final=*/false, /*Innermost=*/std::nullopt,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+ // Lambdas are handled by LambdaScopeForCallOperatorInstantiationRAII.
+ if (isLambdaCallOperator(FD))
+ return MLTAL;
+ if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
+ return std::nullopt;
+
+ return MLTAL;
+}
+
bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction,
SourceLocation UsageLoc,
@@ -1431,12 +1543,12 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ContextRAII SavedContext{*this, CtxToSave};
LocalInstantiationScope Scope(*this, !ForOverloadResolution);
- MultiLevelTemplateArgumentList MLTAL =
- getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
- /*Final=*/false, /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true);
+ std::optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(
+ const_cast<FunctionDecl *>(FD), {}, Scope);
+
+ if (!MLTAL)
+ return true;
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
@@ -1447,11 +1559,11 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
LambdaScopeForCallOperatorInstantiationRAII LambdaScope(
- *this, const_cast<FunctionDecl *>(FD), MLTAL, Scope,
+ *this, const_cast<FunctionDecl *>(FD), *MLTAL, Scope,
ForOverloadResolution);
return CheckConstraintSatisfaction(
- FD, FD->getTrailingRequiresClause(), MLTAL,
+ FD, FD->getTrailingRequiresClause(), *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction);
}
@@ -1684,12 +1796,12 @@ bool Sema::CheckFunctionTemplateConstraints(
Sema::ContextRAII savedContext(*this, Decl);
LocalInstantiationScope Scope(*this);
- MultiLevelTemplateArgumentList MLTAL =
- getTemplateInstantiationArgs(Decl, Decl->getLexicalDeclContext(),
- /*Final=*/false, /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true);
+ std::optional<MultiLevelTemplateArgumentList> MLTAL =
+ SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
+ Scope);
+
+ if (!MLTAL)
+ return true;
Qualifiers ThisQuals;
CXXRecordDecl *Record = nullptr;
@@ -1699,10 +1811,10 @@ bool Sema::CheckFunctionTemplateConstraints(
}
CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr);
- LambdaScopeForCallOperatorInstantiationRAII LambdaScope(*this, Decl, MLTAL,
+ LambdaScopeForCallOperatorInstantiationRAII LambdaScope(*this, Decl, *MLTAL,
Scope);
- return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
+ return CheckConstraintSatisfaction(Template, TemplateAC, *MLTAL,
PointOfInstantiation, Satisfaction);
}
@@ -1975,7 +2087,7 @@ void Sema::DiagnoseUnsatisfiedConstraint(
ConstraintExpr->getBeginLoc(), First);
}
-namespace clang {
+namespace {
class SubstituteParameterMappings {
Sema &SemaRef;
@@ -2013,8 +2125,6 @@ class SubstituteParameterMappings {
bool substitute(NormalizedConstraint &N);
};
-} // namespace clang
-
void SubstituteParameterMappings::buildParameterMapping(
NormalizedConstraintWithParamMapping &N) {
TemplateParameterList *TemplateParams =
@@ -2308,6 +2418,8 @@ bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) {
llvm_unreachable("Unknown ConstraintKind enum");
}
+} // namespace
+
NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
assert(ACs.size() != 0);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index cadc5689cebc2..ffbe8bb0506bc 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1324,10 +1324,11 @@ namespace {
bool BailOutOnIncomplete;
std::optional<llvm::FoldingSetNodeID> TemplateArgsHashValue;
- llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc>
- *CurrentCachedTemplateArgs = nullptr;
- bool instantiateMissingDeclsToScopeForConcepts(Decl *D);
+ // CWG2770: Function parameters should be instantiated when they are
+ // needed by a satisfaction check of an atomic constraint or
+ // (recursively) by another function parameter.
+ bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
public:
typedef TreeTransform<TemplateInstantiator> inherited;
@@ -1357,14 +1358,12 @@ namespace {
inline static struct ForConstraintSubstitution_t {
} ForConstraintSubstitution;
- TemplateInstantiator(
- ForParameterMappingSubstitution_t, Sema &SemaRef, SourceLocation Loc,
- const MultiLevelTemplateArgumentList &TemplateArgs,
- llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc> *Cache)
+ TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
+ SourceLocation Loc,
+ const MultiLevelTemplateArgumentList &TemplateArgs)
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
- EvaluateLambdaConstraint(true), BailOutOnIncomplete(false),
- CurrentCachedTemplateArgs(Cache) {
- if (!Cache)
+ EvaluateLambdaConstraint(true), BailOutOnIncomplete(false) {
+ if (!SemaRef.CurrentCachedTemplateArgs)
return;
auto &V = TemplateArgsHashValue.emplace();
for (auto &Level : TemplateArgs)
@@ -1411,18 +1410,22 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool FailOnPackProducingTemplates,
bool &ShouldExpand, bool &RetainExpansion,
- UnsignedOrNone &NumExpansions,
- bool Diagnose = true) {
- for (UnexpandedParameterPack ParmPack : Unexpanded) {
- if (instantiateMissingDeclsToScopeForConcepts(
- dyn_cast<NamedDecl *>(ParmPack.first)))
- return true;
+ UnsignedOrNone &NumExpansions) {
+ if (SemaRef.CurrentInstantiationScope &&
+ (SemaRef.inConstraintSubstitution() ||
+ SemaRef.inParameterMappingSubstitution())) {
+ for (UnexpandedParameterPack ParmPack : Unexpanded) {
+ NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
+ if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
+ PVD && maybeInstantiateFunctionParameterToScope(PVD))
+ return true;
+ }
}
return getSema().CheckParameterPacksForExpansion(
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs,
FailOnPackProducingTemplates, ShouldExpand, RetainExpansion,
- NumExpansions, Diagnose);
+ NumExpansions);
}
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1634,7 +1637,7 @@ namespace {
TemplateArgumentLoc &Output,
bool Uneval = false) {
const TemplateArgument &Arg = Input.getArgument();
- if (auto *Cache = CurrentCachedTemplateArgs;
+ if (auto *Cache = SemaRef.CurrentCachedTemplateArgs;
Cache && TemplateArgsHashValue) {
llvm::FoldingSetNodeID ID = *TemplateArgsHashValue;
ID.AddInteger(SemaRef.ArgPackSubstIndex.toInternalRepresentation());
@@ -1977,7 +1980,11 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
// template parameter.
}
- if (instantiateMissingDeclsToScopeForConcepts(D))
+ if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
+ PVD && SemaRef.CurrentInstantiationScope &&
+ (SemaRef.inConstraintSubstitution() ||
+ SemaRef.inParameterMappingSubstitution()) &&
+ maybeInstantiateFunctionParameterToScope(PVD))
return nullptr;
if (isa<CXXExpansionStmtDecl>(D)) {
@@ -1989,39 +1996,9 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
}
-bool TemplateInstantiator::instantiateMissingDeclsToScopeForConcepts(Decl *D) {
- if (!(D && (SemaRef.inConstraintSubstitution() ||
- SemaRef.inParameterMappingSubstitution())))
- return false;
-
- auto *Current = SemaRef.CurrentInstantiationScope;
- if (!Current)
- return false;
- if (Current->getInstantiationOfIfExists(D))
- return false;
-
- for (auto *Outer = Current...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/223235
More information about the cfe-commits
mailing list