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
