[clang] f5efa74 - [Sema] When checking for constraint equivalence, do not calculate satisfaction (#74490)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 4 02:57:56 PST 2024
Author: Ilya Biryukov
Date: 2024-01-04T11:57:53+01:00
New Revision: f5efa74961560070a1e6f127214bcf6b570fef98
URL: https://github.com/llvm/llvm-project/commit/f5efa74961560070a1e6f127214bcf6b570fef98
DIFF: https://github.com/llvm/llvm-project/commit/f5efa74961560070a1e6f127214bcf6b570fef98.diff
LOG: [Sema] When checking for constraint equivalence, do not calculate satisfaction (#74490)
... and only look at equivalence of substituted expressions, not results
of constraint satisfaction.
This is required by the standard when matching redeclarations.
Fixes #74314.
There is already some existing machinery for that in
`TemplateInstantiator` and `Sema` exposed separate functions for
substituting expressions with intention to do that:
- `Sema::SubstExpr` should not evaluate constraints.
- `Sema::SubstConstraintExpr` should.
However, both functions used to be equivalent. Introduce a new function
that does not evaluate constraint and use it when matching declarations.
Also change implementation of `SubstConstraintExpr` to call `SubstExpr`
directly so it's obvious they behave in the same way and add a FIXME to
call out that we might need to revamp this approach in the future.
Added:
Modified:
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/Template.h
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/test/SemaTemplate/concepts-out-of-line-def.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5e3b57ea33220b..8f44adef38159e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10263,11 +10263,13 @@ class Sema final {
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
};
- // Unlike the above, this evaluates constraints, which should only happen at
- // 'constraint checking' time.
+ // Must be used instead of SubstExpr at 'constraint checking' time.
ExprResult
SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);
+ // Unlike the above, this does not evaluates constraints.
+ ExprResult SubstConstraintExprWithoutSatisfaction(
+ Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
/// Substitute the given template arguments into a list of
/// expressions, expanding pack expansions if required.
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 2a553054a0ce51..ce44aca797b0fb 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -564,6 +564,7 @@ enum class TemplateSubstitutionKind : char {
const MultiLevelTemplateArgumentList &TemplateArgs;
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
LocalInstantiationScope *StartingScope = nullptr;
+ // Whether to evaluate the C++20 constraints or simply substitute into them.
bool EvaluateConstraints = true;
/// A list of out-of-line class template partial
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 719c6aab74e017..acfc00f4125407 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -771,10 +771,9 @@ namespace {
};
} // namespace
-static const Expr *
-SubstituteConstraintExpression(Sema &S,
- const Sema::TemplateCompareNewDeclInfo &DeclInfo,
- const Expr *ConstrExpr) {
+static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
+ Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
+ const Expr *ConstrExpr) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false,
/*Innermost=*/nullptr,
@@ -797,8 +796,8 @@ SubstituteConstraintExpression(Sema &S,
std::optional<Sema::CXXThisScopeRAII> ThisScope;
if (auto *RD = dyn_cast<CXXRecordDecl>(DeclInfo.getDeclContext()))
ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
- ExprResult SubstConstr =
- S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
+ ExprResult SubstConstr = S.SubstConstraintExprWithoutSatisfaction(
+ const_cast<clang::Expr *>(ConstrExpr), MLTAL);
if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
return nullptr;
return SubstConstr.get();
@@ -814,12 +813,14 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
if (Old && !New.isInvalid() && !New.ContainsDecl(Old) &&
Old->getLexicalDeclContext() != New.getLexicalDeclContext()) {
if (const Expr *SubstConstr =
- SubstituteConstraintExpression(*this, Old, OldConstr))
+ SubstituteConstraintExpressionWithoutSatisfaction(*this, Old,
+ OldConstr))
OldConstr = SubstConstr;
else
return false;
if (const Expr *SubstConstr =
- SubstituteConstraintExpression(*this, New, NewConstr))
+ SubstituteConstraintExpressionWithoutSatisfaction(*this, New,
+ NewConstr))
NewConstr = SubstConstr;
else
return false;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index df6b40999e645c..37e5b9cad08bc9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1190,6 +1190,7 @@ namespace {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
DeclarationName Entity;
+ // Whether to evaluate the C++20 constraints or simply substitute into them.
bool EvaluateConstraints = true;
public:
@@ -2499,6 +2500,17 @@ TemplateInstantiator::TransformNestedRequirement(
Req->getConstraintExpr()->getBeginLoc(), Req,
Sema::InstantiatingTemplate::ConstraintsCheck{},
Req->getConstraintExpr()->getSourceRange());
+ if (!getEvaluateConstraints()) {
+ ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
+ if (TransConstraint.isInvalid() || !TransConstraint.get())
+ return nullptr;
+ if (TransConstraint.get()->isInstantiationDependent())
+ return new (SemaRef.Context)
+ concepts::NestedRequirement(TransConstraint.get());
+ ConstraintSatisfaction Satisfaction;
+ return new (SemaRef.Context) concepts::NestedRequirement(
+ SemaRef.Context, TransConstraint.get(), Satisfaction);
+ }
ExprResult TransConstraint;
ConstraintSatisfaction Satisfaction;
@@ -4093,13 +4105,19 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
ExprResult
Sema::SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs) {
+ // FIXME: should call SubstExpr directly if this function is equivalent or
+ // should it be
diff erent?
+ return SubstExpr(E, TemplateArgs);
+}
+
+ExprResult Sema::SubstConstraintExprWithoutSatisfaction(
+ Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!E)
return E;
- // This is where we need to make sure we 'know' constraint checking needs to
- // happen.
TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(),
DeclarationName());
+ Instantiator.setEvaluateConstraints(false);
return Instantiator.TransformExpr(E);
}
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index f134394615fb24..c4e8e6f720c492 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -504,3 +504,35 @@ struct bar {
bar<int> x;
} // namespace GH61763
+
+namespace GH74314 {
+template <class T, class U> constexpr bool is_same_v = __is_same(T, U);
+template <class T, class U> constexpr bool is_not_same_v = !__is_same(T, U);
+
+template <class Result>
+concept something_interesting = requires {
+ true;
+ requires is_same_v<int, Result>;
+};
+
+template <class T>
+struct X {
+ void foo() requires requires { requires is_not_same_v<T, int>; };
+ void bar(decltype(requires { requires is_not_same_v<T, int>; }));
+};
+
+template <class T>
+void X<T>::foo() requires requires { requires something_interesting<T>; } {}
+// expected-error at -1{{definition of 'foo' does not match any declaration}}
+// expected-note@*{{}}
+
+template <class T>
+void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
+
+template <class T>
+void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {}
+// expected-error at -1{{definition of 'bar' does not match any declaration}}
+
+template <class T>
+void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}
+} // namespace GH74314
More information about the cfe-commits
mailing list