[clang] [Clang] Reapply CWG2369 "Ordering between constraints and substitution" (PR #122423)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 10 00:58:39 PST 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/122423
>From d40a80859eafe96bdc99957db2aebb70db407e67 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 10 Jan 2025 09:46:24 +0800
Subject: [PATCH 1/3] Reapply "[Clang] Implement CWG2369 "Ordering between
constraints and substitution"" (#122130)
This reverts commit 3972ed57088f6515b787d7d38dec03dc74e51827.
---
clang/include/clang/Sema/Sema.h | 22 +++-
clang/include/clang/Sema/Template.h | 6 +
clang/lib/Sema/SemaConcept.cpp | 47 ++++++-
clang/lib/Sema/SemaTemplateDeduction.cpp | 49 +++++---
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 8 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 115 ++++++++++++++++--
clang/lib/Sema/TreeTransform.h | 2 +-
clang/test/CXX/drs/cwg23xx.cpp | 29 +++++
clang/test/CXX/drs/cwg26xx.cpp | 2 +-
clang/test/CXX/drs/cwg27xx.cpp | 20 +++
.../expr.prim.req/nested-requirement.cpp | 2 +-
.../constrant-satisfaction-conversions.cpp | 6 +-
.../SemaCXX/concept-crash-on-diagnostic.cpp | 2 +-
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +-
clang/test/SemaCXX/cxx23-assume.cpp | 6 +-
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 2 +-
clang/test/SemaCXX/lambda-unevaluated.cpp | 4 +-
.../SemaTemplate/concepts-recursive-inst.cpp | 4 +-
.../SemaTemplate/cxx2a-constraint-exprs.cpp | 2 +-
clang/test/SemaTemplate/deduction-guide.cpp | 5 -
.../nested-implicit-deduction-guides.cpp | 8 +-
clang/www/cxx_dr_status.html | 8 +-
22 files changed, 289 insertions(+), 62 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a41f16f6dc8c9b..18fd95f77ec227 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13062,6 +13062,7 @@ class Sema final : public SemaBase {
///
/// \param SkipForSpecialization when specified, any template specializations
/// in a traversal would be ignored.
+ ///
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
/// when encountering a specialized member function template, rather than
/// returning immediately.
@@ -13073,6 +13074,17 @@ class Sema final : public SemaBase {
bool SkipForSpecialization = false,
bool ForDefaultArgumentSubstitution = false);
+ /// Apart from storing the result to \p Result, this behaves the same as
+ /// another overload.
+ 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);
+
/// RAII object to handle the state changes required to synthesize
/// a function body.
class SynthesizedFunctionScope {
@@ -13342,7 +13354,7 @@ class Sema final : public SemaBase {
ExprResult
SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
- // Unlike the above, this does not evaluates constraints.
+ // Unlike the above, this does not evaluate constraints.
ExprResult SubstConstraintExprWithoutSatisfaction(
Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
@@ -14463,10 +14475,10 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceRange TemplateIDRange);
- bool CheckInstantiatedFunctionTemplateConstraints(
- SourceLocation PointOfInstantiation, FunctionDecl *Decl,
- ArrayRef<TemplateArgument> TemplateArgs,
- ConstraintSatisfaction &Satisfaction);
+ bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
+ FunctionDecl *Decl,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ ConstraintSatisfaction &Satisfaction);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 9800f75f676aaf..59a0575ca98036 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char {
llvm::PointerUnion<Decl *, DeclArgumentPack *> *
findInstantiationOf(const Decl *D);
+ /// Similar to \p findInstantiationOf(), but it wouldn't assert if the
+ /// instantiation was not found within the current instantiation scope. This
+ /// is helpful for on-demand declaration instantiation.
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *
+ findInstantiationUnsafe(const Decl *D);
+
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 539de00bd104f5..10f4920a761f3c 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -846,7 +846,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
bool ForOverloadResolution) {
// Don't check constraints if the function is dependent. Also don't check if
// this is a function template specialization, as the call to
- // CheckinstantiatedFunctionTemplateConstraints after this will check it
+ // CheckFunctionTemplateConstraints after this will check it
// better.
if (FD->isDependentContext() ||
FD->getTemplatedKind() ==
@@ -1111,12 +1111,55 @@ bool Sema::EnsureTemplateArgumentListConstraints(
return false;
}
-bool Sema::CheckInstantiatedFunctionTemplateConstraints(
+static bool CheckFunctionConstraintsWithoutInstantiation(
+ Sema &SemaRef, SourceLocation PointOfInstantiation,
+ FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
+ ConstraintSatisfaction &Satisfaction) {
+ SmallVector<const Expr *, 3> TemplateAC;
+ Template->getAssociatedConstraints(TemplateAC);
+ if (TemplateAC.empty()) {
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ LocalInstantiationScope Scope(SemaRef);
+
+ FunctionDecl *FD = Template->getTemplatedDecl();
+ // 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.
+
+ // FIXME: Add TemplateArgs through the 'Innermost' parameter once
+ // the refactoring of getTemplateInstantiationArgs() relands.
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false);
+ SemaRef.getTemplateInstantiationArgs(
+ MLTAL, /*D=*/FD, FD,
+ /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
+ MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs);
+
+ Sema::ContextRAII SavedContext(SemaRef, FD);
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
+ ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
+ /*ThisQuals=*/Method->getMethodQualifiers());
+ return SemaRef.CheckConstraintSatisfaction(
+ Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
+}
+
+bool Sema::CheckFunctionTemplateConstraints(
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction) {
// In most cases we're not going to have constraints, so check for that first.
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
+
+ if (!Template)
+ return ::CheckFunctionConstraintsWithoutInstantiation(
+ *this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
+ TemplateArgs, Satisfaction);
+
// Note - code synthesis context for the constraints check is created
// inside CheckConstraintsSatisfaction.
SmallVector<const Expr *, 3> TemplateAC;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 1c1f6e30ab7b83..acd1151184e42f 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3936,18 +3936,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
Result != TemplateDeductionResult::Success)
return Result;
- // C++ [temp.deduct.call]p10: [DR1391]
- // If deduction succeeds for all parameters that contain
- // template-parameters that participate in template argument deduction,
- // and all template arguments are explicitly specified, deduced, or
- // obtained from default template arguments, remaining parameters are then
- // compared with the corresponding arguments. For each remaining parameter
- // P with a type that was non-dependent before substitution of any
- // explicitly-specified template arguments, if the corresponding argument
- // A cannot be implicitly converted to P, deduction fails.
- if (CheckNonDependent())
- return TemplateDeductionResult::NonDependentConversionFailure;
-
// Form the template argument list from the deduced template arguments.
TemplateArgumentList *SugaredDeducedArgumentList =
TemplateArgumentList::CreateCopy(Context, SugaredBuilder);
@@ -3977,6 +3965,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
FD = const_cast<FunctionDecl *>(FDFriend);
Owner = FD->getLexicalDeclContext();
}
+ // C++20 [temp.deduct.general]p5: [CWG2369]
+ // If the function template has associated constraints, those constraints
+ // are checked for satisfaction. If the constraints are not satisfied, type
+ // deduction fails.
+ //
+ // FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
+ // to figure out how to instantiate lambda captures to the scope without
+ // first instantiating the lambda.
+ bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
+ if (!IsLambda && !IsIncomplete) {
+ if (CheckFunctionTemplateConstraints(
+ Info.getLocation(),
+ FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
+ CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
+ return TemplateDeductionResult::MiscellaneousDeductionFailure;
+ if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+ Info.reset(Info.takeSugared(),
+ TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
+ return TemplateDeductionResult::ConstraintsNotSatisfied;
+ }
+ }
+ // C++ [temp.deduct.call]p10: [CWG1391]
+ // If deduction succeeds for all parameters that contain
+ // template-parameters that participate in template argument deduction,
+ // and all template arguments are explicitly specified, deduced, or
+ // obtained from default template arguments, remaining parameters are then
+ // compared with the corresponding arguments. For each remaining parameter
+ // P with a type that was non-dependent before substitution of any
+ // explicitly-specified template arguments, if the corresponding argument
+ // A cannot be implicitly converted to P, deduction fails.
+ if (CheckNonDependent())
+ return TemplateDeductionResult::NonDependentConversionFailure;
+
MultiLevelTemplateArgumentList SubstArgs(
FunctionTemplate, CanonicalDeducedArgumentList->asArray(),
/*Final=*/false);
@@ -4011,8 +4032,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
// ([temp.constr.decl]), those constraints are checked for satisfaction
// ([temp.constr.constr]). If the constraints are not satisfied, type
// deduction fails.
- if (!IsIncomplete) {
- if (CheckInstantiatedFunctionTemplateConstraints(
+ if (IsLambda && !IsIncomplete) {
+ if (CheckFunctionTemplateConstraints(
Info.getLocation(), Specialization, CanonicalBuilder,
Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index d42c3765aa534f..5d6c11a75303df 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -902,10 +902,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
Context.getTrivialTypeSourceInfo(
Context.getDeducedTemplateSpecializationType(
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
- /*IsDependent=*/true)), // template specialization type whose
- // arguments will be deduced.
+ /*IsDependent=*/true),
+ AliasTemplate->getLocation()), // template specialization type whose
+ // arguments will be deduced.
Context.getTrivialTypeSourceInfo(
- ReturnType), // type from which template arguments are deduced.
+ ReturnType, AliasTemplate->getLocation()), // type from which template
+ // arguments are deduced.
};
return TypeTraitExpr::Create(
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index fb0f38df62a744..cab9ae79ce5cb0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -475,6 +475,21 @@ 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;
@@ -535,14 +550,12 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
}
if (R.IsDone)
- return Result;
+ return;
if (R.ClearRelativeToPrimary)
RelativeToPrimary = false;
assert(R.NextDecl);
CurDecl = R.NextDecl;
}
-
- return Result;
}
bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
@@ -1349,6 +1362,19 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
+ private:
+ bool isSubstitutingConstraints() const {
+ return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
+ return Context.Kind ==
+ Sema::CodeSynthesisContext::ConstraintSubstitution;
+ });
+ }
+
+ // 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;
@@ -1405,12 +1431,19 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool &ShouldExpand, bool &RetainExpansion,
std::optional<unsigned> &NumExpansions) {
- return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
- PatternRange, Unexpanded,
- TemplateArgs,
- ShouldExpand,
- RetainExpansion,
- NumExpansions);
+ if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
+ for (UnexpandedParameterPack ParmPack : Unexpanded) {
+ NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
+ if (!isa_and_present<ParmVarDecl>(VD))
+ continue;
+ if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
+ return true;
+ }
+ }
+
+ return getSema().CheckParameterPacksForExpansion(
+ EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
+ RetainExpansion, NumExpansions);
}
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1911,9 +1944,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
// template parameter.
}
+ if (SemaRef.CurrentInstantiationScope) {
+ if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
+ maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
+ return nullptr;
+ }
+
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
}
+bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
+ ParmVarDecl *OldParm) {
+ if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm))
+ return false;
+ // We're instantiating a function parameter whose associated function template
+ // has not been instantiated at this point for constraint evaluation, so make
+ // sure the instantiated parameters are owned by a function declaration such
+ // that they can be correctly 'captured' in tryCaptureVariable().
+ Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext());
+
+ if (!OldParm->isParameterPack())
+ return !TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
+ /*NumExpansions=*/std::nullopt,
+ /*ExpectParameterPack=*/false);
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+
+ // Find the parameter packs that could be expanded.
+ TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
+ PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
+ TypeLoc Pattern = ExpansionTL.getPatternLoc();
+ SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+ bool ShouldExpand = false;
+ bool RetainExpansion = false;
+ std::optional<unsigned> OrigNumExpansions =
+ ExpansionTL.getTypePtr()->getNumExpansions();
+ std::optional<unsigned> NumExpansions = OrigNumExpansions;
+ if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
+ Pattern.getSourceRange(), Unexpanded,
+ ShouldExpand, RetainExpansion, NumExpansions))
+ return true;
+
+ assert(ShouldExpand && !RetainExpansion &&
+ "Shouldn't preserve pack expansion when evaluating constraints");
+ ExpandingFunctionParameterPack(OldParm);
+ for (unsigned I = 0; I != *NumExpansions; ++I) {
+ Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
+ if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0,
+ /*NumExpansions=*/OrigNumExpansions,
+ /*ExpectParameterPack=*/false))
+ return true;
+ }
+ return false;
+}
+
Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) {
Decl *Inst = getSema().SubstDecl(D, getSema().CurContext, TemplateArgs);
if (!Inst)
@@ -4591,9 +4677,8 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) {
return D;
}
-
llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
-LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) {
D = getCanonicalParmVarDecl(D);
for (LocalInstantiationScope *Current = this; Current;
Current = Current->Outer) {
@@ -4618,6 +4703,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) {
break;
}
+ return nullptr;
+}
+
+llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
+LocalInstantiationScope::findInstantiationOf(const Decl *D) {
+ auto *Result = findInstantiationUnsafe(D);
+ if (Result)
+ return Result;
// 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) ||
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4a3c739ecbeab8..4e4544ed863b1a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -713,7 +713,7 @@ class TreeTransform {
/// variables vector are acceptable.
///
/// LastParamTransformed, if non-null, will be set to the index of the last
- /// parameter on which transfromation was started. In the event of an error,
+ /// parameter on which transformation was started. In the event of an error,
/// this will contain the parameter which failed to instantiate.
///
/// Return true on error.
diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp
index d144cf9e4e8681..d0ec48e3f86cd6 100644
--- a/clang/test/CXX/drs/cwg23xx.cpp
+++ b/clang/test/CXX/drs/cwg23xx.cpp
@@ -365,6 +365,35 @@ struct A {
#endif
} // namespace cwg2363
+namespace cwg2369 { // cwg2369: partial
+#if __cplusplus >= 202002L
+template <class T> struct Z {
+ typedef typename T::x xx;
+};
+
+template <class T>
+concept C = requires { typename T::A; };
+template <C T> typename Z<T>::xx f(void *, T); // #1
+template <class T> void f(int, T); // #2
+
+struct A {
+} a;
+
+struct ZZ {
+ template <class T, class = typename Z<T>::xx> operator T *();
+ operator int();
+};
+
+void foo() {
+ ZZ zz;
+ f(1, a); // OK, deduction fails for #1 because there is no conversion from int
+ // to void*
+ f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied
+}
+
+#endif
+} // namespace cwg2369
+
namespace cwg2370 { // cwg2370: no
namespace N {
typedef int type;
diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp
index efc49b0b502a7c..23d7635ff90658 100644
--- a/clang/test/CXX/drs/cwg26xx.cpp
+++ b/clang/test/CXX/drs/cwg26xx.cpp
@@ -319,7 +319,7 @@ void f(T) requires requires { []() { T::invalid; } (); };
// since-cxx20-note at -3 {{in instantiation of requirement here}}
// since-cxx20-note at -4 {{while substituting template arguments into constraint expression here}}
// since-cxx20-note@#cwg2672-f-0 {{while checking constraint satisfaction for template 'f<int>' required here}}
-// since-cxx20-note@#cwg2672-f-0 {{in instantiation of function template specialization 'cwg2672::f<int>' requested here}}
+// since-cxx20-note@#cwg2672-f-0 {{while substituting deduced template arguments into function template 'f' [with T = int]}}
void f(...);
template <class T>
diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp
index a87d26dfc9acf9..7caf36a9f23b25 100644
--- a/clang/test/CXX/drs/cwg27xx.cpp
+++ b/clang/test/CXX/drs/cwg27xx.cpp
@@ -174,6 +174,26 @@ static_assert(!__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3)
#endif
} // namespace cwg2759
+namespace cwg2770 { // cwg2770: 20 open 2023-07-14
+#if __cplusplus >= 202002L
+template<typename T>
+struct B {
+ static_assert(sizeof(T) == 1);
+ using type = int;
+};
+
+template<typename T>
+int f(T t, typename B<T>::type u) requires (sizeof(t) == 1);
+
+template<typename T>
+int f(T t, long);
+
+int i = f(1, 2);
+int j = f('a', 2);
+
+#endif
+} // namespace cwg2770
+
namespace cwg2789 { // cwg2789: 18
#if __cplusplus >= 202302L
template <typename T = int>
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
index 763d983d20f615..a23f7dc595171e 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -154,7 +154,7 @@ void func() {
bar<int>();
// expected-note at -1 {{while checking constraint satisfaction for template 'bar<int>' required here}} \
- // expected-note at -1 {{in instantiation of function template specialization}}
+ // expected-note at -1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}}
// expected-note@#bar {{in instantiation of static data member}}
// expected-note@#bar {{in instantiation of requirement here}}
// expected-note@#bar {{while checking the satisfaction of nested requirement requested here}}
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index ba8e2dc372e984..c41de77986bcae 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -11,7 +11,7 @@ template<typename T> struct S {
// expected-error at +3{{atomic constraint must be of type 'bool' (found 'S<int>')}}
// expected-note@#FINST{{while checking constraint satisfaction}}
-// expected-note@#FINST{{in instantiation of function template specialization}}
+// expected-note@#FINST{{while substituting deduced template arguments into function template 'f' [with T = int]}}
template<typename T> requires (S<T>{})
void f(T);
void f(int);
@@ -19,7 +19,7 @@ void f(int);
// Ensure this applies to operator && as well.
// expected-error at +3{{atomic constraint must be of type 'bool' (found 'S<int>')}}
// expected-note@#F2INST{{while checking constraint satisfaction}}
-// expected-note@#F2INST{{in instantiation of function template specialization}}
+// expected-note@#F2INST{{while substituting deduced template arguments into function template 'f2' [with T = int]}}
template<typename T> requires (S<T>{} && true)
void f2(T);
void f2(int);
@@ -32,7 +32,7 @@ template<typename T> requires requires {
// expected-note at -4{{while checking the satisfaction}}
// expected-note at -6{{while substituting template arguments}}
// expected-note@#F3INST{{while checking constraint satisfaction}}
- // expected-note@#F3INST{{in instantiation of function template specialization}}
+ // expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}}
//
}
void f3(T);
diff --git a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
index 71e55c8290ee4a..ccc109cbca0f19 100644
--- a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
+++ b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp
@@ -31,7 +31,7 @@ void function() {
// expected-note@#3 {{checking the satisfaction of concept 'convertible_to<bool, bool>'}}
// expected-note@#2 {{substituting template arguments into constraint expression here}}
// expected-note@#5 {{checking constraint satisfaction for template 'compare<Object *, Object *>'}}
-// expected-note@#5 {{in instantiation of function template specialization 'compare<Object *, Object *>' requested here}}
+// expected-note@#5 {{while substituting deduced template arguments into function template 'compare' [with IteratorL = Object *, IteratorR = Object *]}}
// expected-note@#4 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}}
// We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted.
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 2d43e46b9e3d76..23c898e6379b05 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -196,7 +196,7 @@ struct Foo {
template <int K>
using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for class template 'Foo'}}
-// expected-note at -1 {{candidate template ignored: could not match}}
+// expected-note at -1 {{candidate template ignored: could not match}} expected-note at -1 {{candidate template ignored: constraints not satisfied}}
// expected-note at -2 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(Foo<double, K>) -> Foo<double, K>'}}
// expected-note at -3 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(const double (&)[K]) -> Foo<double, K>'}}
double abc[3];
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 7f80cdfe7d4523..726cb3bff652e7 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -129,12 +129,12 @@ constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while che
static_assert(f5<int>() == 1);
static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}
- // expected-note at -1 3 {{in instantiation of}}
+ // expected-note at -1 3 {{while substituting deduced template arguments}}
// expected-error at -2 {{no matching function for call}}
static_assert(f5<double>() == 2);
-static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}}
-static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}}
+static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}}
+static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}}
// Do not validate assumptions whose evaluation would have side-effects.
constexpr int foo() {
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 48061439941f23..4220486d3aed30 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -233,7 +233,7 @@ void g() {
A<Thingy, Thingy> *ap;
f(ap, ap); // expected-error{{no matching function for call to 'f'}} \
// expected-note {{while checking constraint satisfaction}} \
- // expected-note {{in instantiation of function template specialization}}
+ // expected-note {{while substituting deduced template arguments}}
}
}
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp
index a9bcab58464e26..d3f937281f201a 100644
--- a/clang/test/SemaCXX/lambda-unevaluated.cpp
+++ b/clang/test/SemaCXX/lambda-unevaluated.cpp
@@ -174,7 +174,7 @@ int* func(T) requires requires { []() { T::foo(); }; }; // expected-error{{type
double* func(...);
static_assert(__is_same(decltype(func(0)), double*)); // expected-note {{while checking constraint satisfaction for template 'func<int>' required here}}
- // expected-note at -1 {{in instantiation of function template specialization 'lambda_in_constraints::func<int>'}}
+ // expected-note at -1 {{while substituting deduced template arguments into function template 'func' [with T = int]}}
static_assert(__is_same(decltype(func(WithFoo())), int*));
template <class T>
@@ -252,7 +252,7 @@ S s("a"); // #use
// expected-note@#S-requires {{substituting template arguments into constraint expression here}}
// expected-note@#S-requires {{in instantiation of requirement here}}
// expected-note@#use {{checking constraint satisfaction for template 'S<const char *>' required here}}
-// expected-note@#use {{requested here}}
+// expected-note@#use {{while substituting deduced template arguments into function template 'S' [with value:auto = const char *]}}
// expected-note-re@#S 2{{candidate constructor {{.*}} not viable}}
// expected-note@#S-ctor {{constraints not satisfied}}
// expected-note-re@#S-requires {{because {{.*}} would be invalid}}
diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
index 9330df8cdd0398..30a410cef91ee9 100644
--- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp
+++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
@@ -76,7 +76,7 @@ auto it = begin(rng); // #BEGIN_CALL
// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}}
// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}}
// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}}
-// expected-note@#BEGIN_CALL {{in instantiation of function template specialization}}
+// expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}}
// Fallout of the failure is failed lookup, which is necessary to stop odd
// cascading errors.
@@ -103,7 +103,7 @@ namespace GH50891 {
// expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}}
// expected-note@#OP_TO {{while substituting template arguments into constraint expression here}}
// expected-note@#FOO_CALL {{while checking constraint satisfaction for template}}
- // expected-note@#FOO_CALL {{in instantiation of function template specialization}}
+ // expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}}
// expected-note@#FOO_CALL {{in instantiation of requirement here}}
// expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}}
diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
index f4403587a62594..5809ef684bbf3b 100644
--- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
+++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
@@ -34,7 +34,7 @@ namespace constant_evaluated {
expected-note at -1{{candidate template ignored}}
int a = (foo<int>(), 0);
// expected-note at -1 {{while checking}} expected-error at -1{{no matching function}} \
- expected-note at -1 {{in instantiation}}
+ expected-note at -1 {{while substituting}}
template<typename T> void bar() requires requires { requires f<int[2]>; } { };
// expected-note at -1{{in instantiation}} \
expected-note at -1{{while substituting}} \
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index d03c783313dd71..67d00bb49f77d7 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -234,11 +234,6 @@ F s(0);
// CHECK: | `-CXXBoolLiteralExpr {{.*}} 'bool' false
// CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (U) -> F<>'
// CHECK: | `-ParmVarDecl {{.*}} 'U'
-// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (int) -> F<>'
-// CHECK: |-TemplateArgument integral ''x''
-// CHECK: |-TemplateArgument type 'int'
-// CHECK: | `-BuiltinType {{.*}} 'int'
-// CHECK: `-ParmVarDecl {{.*}} 'int'
// CHECK: FunctionProtoType {{.*}} 'auto (U) -> F<>' dependent trailing_return cdecl
// CHECK: |-InjectedClassNameType {{.*}} 'F<>' dependent
// CHECK: | `-CXXRecord {{.*}} 'F'
diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
index af3e3358f61382..5c7a90273d0e0f 100644
--- a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
@@ -38,7 +38,7 @@ template<typename A, typename T>
concept True = true;
template<typename T>
-concept False = false;
+concept False = false; // #False
template<class X> struct concepts {
template<class Y> struct B {
@@ -68,7 +68,7 @@ template<typename X> struct nested_init_list {
Y y;
};
- template<False F>
+ template<False F> // #INIT_LIST_INNER_INVALID_HEADER
struct concept_fail { // #INIT_LIST_INNER_INVALID
X x;
F f;
@@ -81,7 +81,9 @@ using NIL = nested_init_list<int>::B<int>;
// expected-error at +1 {{no viable constructor or deduction guide for deduction of template arguments of 'nested_init_list<int>::concept_fail'}}
nested_init_list<int>::concept_fail nil_invalid{1, ""};
-// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: substitution failure [with F = const char *]: constraints not satisfied for class template 'concept_fail' [with F = const char *]}}
+// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: constraints not satisfied [with F = const char *]}}
+// expected-note@#INIT_LIST_INNER_INVALID_HEADER {{because 'const char *' does not satisfy 'False'}}
+// expected-note@#False {{because 'false' evaluated to false}}
// expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template <False F> concept_fail(int, F) -> concept_fail<F>'}}
// expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not viable: requires 1 argument, but 2 were provided}}
// expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template <False F> concept_fail(concept_fail<F>) -> concept_fail<F>'}}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index f2716f1e4c6537..70f57a0c00a7f6 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -14045,7 +14045,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2369.html">2369</a></td>
<td>CD6</td>
<td>Ordering between constraints and substitution</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr id="2370">
<td><a href="https://cplusplus.github.io/CWG/issues/2370.html">2370</a></td>
@@ -16464,7 +16464,11 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2770.html">2770</a></td>
<td>open</td>
<td>Trailing <I>requires-clause</I> can refer to function parameters before they are substituted into</td>
- <td align="center">Not resolved</td>
+ <td align="center">
+ <details>
+ <summary>Not resolved</summary>
+ Clang 20 implements 2023-07-14 resolution
+ </details></td>
</tr>
<tr id="2771">
<td><a href="https://cplusplus.github.io/CWG/issues/2771.html">2771</a></td>
>From b26d361cf000c1abc3099355938ac3ff8067c90f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 10 Jan 2025 13:32:11 +0800
Subject: [PATCH 2/3] Prevent code generation when substituting constraints
---
clang/include/clang/Sema/Sema.h | 9 ++++++
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +++++++------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 ++++--
clang/test/CodeGenCXX/ms-mangle-requires.cpp | 29 +++++++++++++++++++
5 files changed, 58 insertions(+), 12 deletions(-)
create mode 100644 clang/test/CodeGenCXX/ms-mangle-requires.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 18fd95f77ec227..87d9a335763e31 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13181,6 +13181,10 @@ class Sema final : public SemaBase {
// FIXME: Should we have a similar limit for other forms of synthesis?
unsigned NonInstantiationEntries;
+ /// The number of \p CodeSynthesisContexts that are not constraint
+ /// substitution.
+ unsigned NonConstraintSubstitutionEntries;
+
/// The depth of the context stack at the point when the most recent
/// error or warning was produced.
///
@@ -13503,6 +13507,11 @@ class Sema final : public SemaBase {
return CodeSynthesisContexts.size() > NonInstantiationEntries;
}
+ /// Determine whether we are currently performing constraint substitution.
+ bool inConstraintSubstitution() const {
+ return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries;
+ }
+
using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
/// \brief create a Requirement::SubstitutionDiagnostic with only a
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index abb46d3a84e74e..b7b154e55b3db9 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -263,7 +263,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
- ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context) {
+ NonConstraintSubstitutionEntries(0), ArgumentPackSubstitutionIndex(-1),
+ SatisfactionCache(Context) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index cab9ae79ce5cb0..6db6d52e27d6dc 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -828,6 +828,9 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
if (!Ctx.isInstantiationRecord())
++NonInstantiationEntries;
+ if (Ctx.Kind != CodeSynthesisContext::ConstraintSubstitution)
+ ++NonConstraintSubstitutionEntries;
+
// Check to see if we're low on stack space. We can't do anything about this
// from here, but we can at least warn the user.
StackHandler.warnOnStackNearlyExhausted(Ctx.PointOfInstantiation);
@@ -840,6 +843,11 @@ void Sema::popCodeSynthesisContext() {
--NonInstantiationEntries;
}
+ if (Active.Kind != CodeSynthesisContext::ConstraintSubstitution) {
+ assert(NonConstraintSubstitutionEntries > 0);
+ --NonConstraintSubstitutionEntries;
+ }
+
InNonInstantiationSFINAEContext = Active.SavedInNonInstantiationSFINAEContext;
// Name lookup no longer looks in this template's defining module.
@@ -1363,13 +1371,6 @@ namespace {
bool BailOutOnIncomplete;
private:
- bool isSubstitutingConstraints() const {
- return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
- return Context.Kind ==
- Sema::CodeSynthesisContext::ConstraintSubstitution;
- });
- }
-
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
@@ -1431,7 +1432,8 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool &ShouldExpand, bool &RetainExpansion,
std::optional<unsigned> &NumExpansions) {
- if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
+ if (SemaRef.CurrentInstantiationScope &&
+ SemaRef.inConstraintSubstitution()) {
for (UnexpandedParameterPack ParmPack : Unexpanded) {
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
if (!isa_and_present<ParmVarDecl>(VD))
@@ -1945,7 +1947,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
}
if (SemaRef.CurrentInstantiationScope) {
- if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
+ if (SemaRef.inConstraintSubstitution() && isa<ParmVarDecl>(D) &&
maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
return nullptr;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e058afe81da589..08104f8199089d 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5284,8 +5284,13 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
savedContext.pop();
}
- DeclGroupRef DG(Function);
- Consumer.HandleTopLevelDecl(DG);
+ // With CWG2369, we substitute constraints before instantiating the associated
+ // function template. This helps prevent potential code generation for
+ // dependent types, particularly under the MS ABI.
+ if (!inConstraintSubstitution()) {
+ DeclGroupRef DG(Function);
+ Consumer.HandleTopLevelDecl(DG);
+ }
// This class may have local implicit instantiations that need to be
// instantiation within this scope.
diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
new file mode 100644
index 00000000000000..1d873a3914fadc
--- /dev/null
+++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc -Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm %s -o - | FileCheck %s
+
+namespace CWG2369_Regression {
+
+template <class, class Up>
+using compare_three_way_result_t = Up::type;
+
+struct sfinae_assign_base {};
+
+template <class Tp>
+concept is_derived_from_optional =
+ requires(Tp param) { []<class Up>(Up) {}(param); };
+
+template <class Tp, class Up>
+ requires(is_derived_from_optional<Up>)
+compare_three_way_result_t<Tp, Up> operator<=>(Tp, Up);
+
+struct RuntimeModeArgs {
+ auto operator<=>(const RuntimeModeArgs &) const = default;
+ sfinae_assign_base needs_admin;
+};
+
+RuntimeModeArgs foo() {
+ return {};
+}
+
+// CHECK: ?foo at CWG2369_Regression@@YA?AURuntimeModeArgs at 1@XZ
+
+}
>From 65829ef4ac5ec5c3e92aebf49441f67012a36d89 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 10 Jan 2025 16:58:07 +0800
Subject: [PATCH 3/3] Is "Prevent code generation when substituting
constraints" related?
---
clang/include/clang/Sema/Sema.h | 9 ------
clang/lib/Sema/Sema.cpp | 3 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 ++++++-------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 ++----
clang/test/CodeGenCXX/ms-mangle-requires.cpp | 29 -------------------
5 files changed, 12 insertions(+), 58 deletions(-)
delete mode 100644 clang/test/CodeGenCXX/ms-mangle-requires.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 87d9a335763e31..18fd95f77ec227 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13181,10 +13181,6 @@ class Sema final : public SemaBase {
// FIXME: Should we have a similar limit for other forms of synthesis?
unsigned NonInstantiationEntries;
- /// The number of \p CodeSynthesisContexts that are not constraint
- /// substitution.
- unsigned NonConstraintSubstitutionEntries;
-
/// The depth of the context stack at the point when the most recent
/// error or warning was produced.
///
@@ -13507,11 +13503,6 @@ class Sema final : public SemaBase {
return CodeSynthesisContexts.size() > NonInstantiationEntries;
}
- /// Determine whether we are currently performing constraint substitution.
- bool inConstraintSubstitution() const {
- return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries;
- }
-
using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
/// \brief create a Requirement::SubstitutionDiagnostic with only a
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index b7b154e55b3db9..abb46d3a84e74e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -263,8 +263,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
- NonConstraintSubstitutionEntries(0), ArgumentPackSubstitutionIndex(-1),
- SatisfactionCache(Context) {
+ ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6db6d52e27d6dc..cab9ae79ce5cb0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -828,9 +828,6 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
if (!Ctx.isInstantiationRecord())
++NonInstantiationEntries;
- if (Ctx.Kind != CodeSynthesisContext::ConstraintSubstitution)
- ++NonConstraintSubstitutionEntries;
-
// Check to see if we're low on stack space. We can't do anything about this
// from here, but we can at least warn the user.
StackHandler.warnOnStackNearlyExhausted(Ctx.PointOfInstantiation);
@@ -843,11 +840,6 @@ void Sema::popCodeSynthesisContext() {
--NonInstantiationEntries;
}
- if (Active.Kind != CodeSynthesisContext::ConstraintSubstitution) {
- assert(NonConstraintSubstitutionEntries > 0);
- --NonConstraintSubstitutionEntries;
- }
-
InNonInstantiationSFINAEContext = Active.SavedInNonInstantiationSFINAEContext;
// Name lookup no longer looks in this template's defining module.
@@ -1371,6 +1363,13 @@ namespace {
bool BailOutOnIncomplete;
private:
+ bool isSubstitutingConstraints() const {
+ return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) {
+ return Context.Kind ==
+ Sema::CodeSynthesisContext::ConstraintSubstitution;
+ });
+ }
+
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
@@ -1432,8 +1431,7 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool &ShouldExpand, bool &RetainExpansion,
std::optional<unsigned> &NumExpansions) {
- if (SemaRef.CurrentInstantiationScope &&
- SemaRef.inConstraintSubstitution()) {
+ if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
for (UnexpandedParameterPack ParmPack : Unexpanded) {
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
if (!isa_and_present<ParmVarDecl>(VD))
@@ -1947,7 +1945,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
}
if (SemaRef.CurrentInstantiationScope) {
- if (SemaRef.inConstraintSubstitution() && isa<ParmVarDecl>(D) &&
+ if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
return nullptr;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 08104f8199089d..e058afe81da589 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5284,13 +5284,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
savedContext.pop();
}
- // With CWG2369, we substitute constraints before instantiating the associated
- // function template. This helps prevent potential code generation for
- // dependent types, particularly under the MS ABI.
- if (!inConstraintSubstitution()) {
- DeclGroupRef DG(Function);
- Consumer.HandleTopLevelDecl(DG);
- }
+ DeclGroupRef DG(Function);
+ Consumer.HandleTopLevelDecl(DG);
// This class may have local implicit instantiations that need to be
// instantiation within this scope.
diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp b/clang/test/CodeGenCXX/ms-mangle-requires.cpp
deleted file mode 100644
index 1d873a3914fadc..00000000000000
--- a/clang/test/CodeGenCXX/ms-mangle-requires.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc -Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm %s -o - | FileCheck %s
-
-namespace CWG2369_Regression {
-
-template <class, class Up>
-using compare_three_way_result_t = Up::type;
-
-struct sfinae_assign_base {};
-
-template <class Tp>
-concept is_derived_from_optional =
- requires(Tp param) { []<class Up>(Up) {}(param); };
-
-template <class Tp, class Up>
- requires(is_derived_from_optional<Up>)
-compare_three_way_result_t<Tp, Up> operator<=>(Tp, Up);
-
-struct RuntimeModeArgs {
- auto operator<=>(const RuntimeModeArgs &) const = default;
- sfinae_assign_base needs_admin;
-};
-
-RuntimeModeArgs foo() {
- return {};
-}
-
-// CHECK: ?foo at CWG2369_Regression@@YA?AURuntimeModeArgs at 1@XZ
-
-}
More information about the cfe-commits
mailing list