Author: Corentin Jabot
Date: 2026-05-13T11:38:52+02:00
New Revision: ac3c588739a09ebb0f80a22ee10282592d858b47

URL: 
https://github.com/llvm/llvm-project/commit/ac3c588739a09ebb0f80a22ee10282592d858b47
DIFF: 
https://github.com/llvm/llvm-project/commit/ac3c588739a09ebb0f80a22ee10282592d858b47.diff

LOG: [Clang] Evaluate concepts in their declaration context. (#197215)

Concepts appearing in a constraint expression of a class member had
access to both `this` and the private member of the class.

This changes fixes that by setting the concext to that of the context
before evaluation of its constraint expression.

This is done after we have substituted the template argument.

Code in `Sema::isThisOutsideMemberFunctionBody` that no longer seems
useful is renoved as it was interefering with this change.

This is not an implementation of CWG2589 - at least not a complete one,
as we still check access when doing substitution in the parameter
mapping.

Fixes #115838
Fixes #194803

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaConcept.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/test/SemaTemplate/concepts.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4b0eb5b9d8505..c71f7b173259f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -575,6 +575,8 @@ Bug Fixes to C++ Support
 - Clang now rejects constant template parameters with block pointer types, 
since these are not implemented anyway and would lead to crashes. (#GH189247)
 - Fixed a crash on error recovery when dealing with invalid templates. 
(#GH183075)
 - Fixed a crash when instantiating ``requires`` expressions involving 
substitution failures in C++ concepts. (#GH176402)
+- Concepts appearing in the require-clause of a member function no longer have 
access to non-public members of that class,
+  or to a current class object. (#GH115838) (#GH194803)
 - We no longer caches invalid variable specializations. (#GH132592)
 - Fixed an incorrect template argument deduction when matching packs of 
template
   template parameters when one of its parameters is also a pack. (#GH181166)

diff  --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index bb3de89c42ccc..996f2657b6767 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -482,6 +482,9 @@ class ConstraintSatisfactionChecker {
   ConstraintSatisfaction &Satisfaction;
   bool BuildExpression;
 
+  // The closest concept declaration when evaluating atomic constraints.
+  ConceptDecl *ParentConcept = nullptr;
+
   // This is for TemplateInstantiator to not instantiate the same template
   // parameter mapping many times, in order to improve substitution 
performance.
   llvm::DenseMap<llvm::FoldingSetNodeID, TemplateArgumentLoc>
@@ -725,6 +728,13 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     return ExprEmpty();
   }
 
+  // Make sure that concepts are not evaluated in the context they are used,
+  // i.e they should not have access to the current class object or its
+  // non-public members.
+  std::optional<Sema::ContextRAII> ConceptContext;
+  if (ParentConcept)
+    ConceptContext.emplace(S, ParentConcept->getDeclContext());
+
   Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex);
   ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint(
       Constraint.getConstraintExpr(), *SubstitutedArgs);
@@ -1035,6 +1045,9 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
 
   unsigned Size = Satisfaction.Details.size();
 
+  llvm::SaveAndRestore PushConceptDecl(
+      ParentConcept, cast<ConceptDecl>(ConceptId->getNamedConcept()));
+
   ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL);
 
   if (E.isInvalid()) {

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e60d5d7748b44..357ceb7f4ea6d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1485,11 +1485,6 @@ void Sema::MarkThisReferenced(CXXThisExpr *This) {
 }
 
 bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
-  // If we're outside the body of a member function, then we'll have a 
specified
-  // type for 'this'.
-  if (CXXThisTypeOverride.isNull())
-    return false;
-
   // Determine whether we're looking into a class that's currently being
   // defined.
   CXXRecordDecl *Class = BaseType->getAsCXXRecordDecl();

diff  --git a/clang/test/SemaTemplate/concepts.cpp 
b/clang/test/SemaTemplate/concepts.cpp
index 1b7cfc96243ef..72a2fab99c581 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1241,7 +1241,7 @@ struct SVGPropertyOwnerRegistry {
   }
 };
 
-class SVGCircleElement {
+struct SVGCircleElement {
   friend SVGPropertyOwnerRegistry<SVGCircleElement>;
   void propertyForAttribute(int);
 };
@@ -1880,3 +1880,70 @@ void g() { static_assert(f<1>() == 42); }
 } // namespace VAR
 
 } // namespace GH188640
+
+namespace GH194803 {
+
+struct B {
+    void f();
+};
+template <typename Base>
+concept C = requires() { Base::f(); }; // expected-note {{because 'Base::f()' 
would be invalid: call to non-static member function without an object 
argument}}
+
+template <typename> struct S : B {
+    void g()
+        requires C<B>; // expected-note {{because 'B' does not satisfy 'C'}}
+    void h()
+        requires requires() { B::f(); }; // #2
+};
+void f() {
+    S<int>{}.g(); // expected-error {{invalid reference to function 'g': 
constraints not satisfied}}
+    S<int>{}.h();
+}
+
+}
+
+namespace GH115838 {
+
+template <typename T>
+concept has_x = requires(T t) {
+    { t.x };
+};
+
+class Publ {
+  public:
+    int x = 0;
+};
+class Priv {
+  private:
+    int x = 0;
+};
+class Prot {
+  protected:
+    int x = 0;
+};
+class Same {
+  protected:
+    int x = 0;
+};
+
+template <typename T> class D;
+template <typename T>
+requires(has_x<T>)
+class D<T> : public T {
+  public:
+    static constexpr bool has = 1;
+};
+template <typename T>
+requires(!has_x<T>)
+class D<T> : public T {
+  public:
+    static constexpr bool has = 0;
+};
+
+static_assert(!has_x<Same>, "Protected should be invisible.");
+static_assert(!D<Same>::has, "Protected should be invisible.");
+
+static_assert(D<Publ>::has, "Public should be visible.");
+static_assert(!D<Priv>::has, "Private should be invisible.");
+static_assert(!D<Prot>::has, "Protected should be invisible.");
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to