summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Shaposhnikov <ashaposhnikov@google.com>2023-05-17 21:02:02 +0000
committerAlexander Shaposhnikov <ashaposhnikov@google.com>2023-05-17 21:24:44 +0000
commit122b938944ceb966e04d7a4d253f7f9ba27c477d (patch)
tree4e28e6b29bfacbb5b8f46e4eb23f47e3e934b165
parenta0615d020a02e252196383439e2c8143c6525e05 (diff)
downloadllvm-122b938944ceb966e04d7a4d253f7f9ba27c477d.tar.gz
[Clang][Sema] Substitute constraints only for declarations with different lexical contexts
Substitute constraints only for declarations with different lexical contexts. This results in avoiding the substitution of constraints during the redeclaration check inside a class (and by product caching the wrong substitution result). Test plan: ninja check-all Differential revision: https://reviews.llvm.org/D150730
-rw-r--r--clang/lib/Sema/SemaConcept.cpp4
-rw-r--r--clang/test/SemaTemplate/concepts-no-early-substitution.cpp33
2 files changed, 36 insertions, 1 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 1126c2c517fe..2f5fb8f8d029 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -780,7 +780,9 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *NewConstr) {
if (OldConstr == NewConstr)
return true;
- if (Old && New && Old != New) {
+ // C++ [temp.constr.decl]p4
+ if (Old && New && Old != New &&
+ Old->getLexicalDeclContext() != New->getLexicalDeclContext()) {
if (const Expr *SubstConstr =
SubstituteConstraintExpression(*this, Old, OldConstr))
OldConstr = SubstConstr;
diff --git a/clang/test/SemaTemplate/concepts-no-early-substitution.cpp b/clang/test/SemaTemplate/concepts-no-early-substitution.cpp
new file mode 100644
index 000000000000..9e576f16a263
--- /dev/null
+++ b/clang/test/SemaTemplate/concepts-no-early-substitution.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -std=c++20 -x c++ %s -verify -fsyntax-only
+// expected-no-diagnostics
+
+template <typename T0>
+concept HasMemberBegin = requires(T0 t) { t.begin(); };
+
+struct GetBegin {
+ template <HasMemberBegin T1>
+ void operator()(T1);
+};
+
+GetBegin begin;
+
+template <typename T2>
+concept Concept = requires(T2 t) { begin(t); };
+
+struct Subrange;
+
+template <typename T3>
+struct View {
+ Subrange &getSubrange();
+
+ operator bool()
+ requires true;
+
+ operator bool()
+ requires requires { begin(getSubrange()); };
+
+ void begin();
+};
+
+struct Subrange : View<void> {};
+static_assert(Concept<Subrange>);