diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -528,6 +528,12 @@ return RequiresExprBits.IsSatisfied; } + void setSatisfied(bool IsSatisfied) { + assert(!isValueDependent() && + "setSatisfied called on a dependent RequiresExpr"); + RequiresExprBits.IsSatisfied = IsSatisfied; + } + SourceLocation getRequiresKWLoc() const { return RequiresExprBits.RequiresKWLoc; } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -3507,6 +3507,8 @@ // Expressions appearing within a requirement-body are unevaluated operands. EnterExpressionEvaluationContext Ctx( Actions, Sema::ExpressionEvaluationContext::Unevaluated); + // Non dependent diagnostics must be undelayed. + Sema::ContextRAII UndelayDiagnostics(Actions, Actions.CurContext); ParseScope BodyScope(this, Scope::DeclScope); RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1002,6 +1002,7 @@ S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; } else if (auto *RE = dyn_cast(SubstExpr)) { + // TODO(usx): Store and diagnose dependent diagnositcs here. for (concepts::Requirement *Req : RE->getRequirements()) if (!Req->isDependent() && !Req->isSatisfied()) { if (auto *E = dyn_cast(Req)) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprConcepts.h" @@ -1363,7 +1364,17 @@ ExprResult TransformRequiresExpr(RequiresExpr *E) { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); - return inherited::TransformRequiresExpr(E); + auto TransReq = inherited::TransformRequiresExpr(E); + if (E->getBody()->isDependentContext()) { + Sema::SFINAETrap Trap(SemaRef); + // We recreate the RequiresExpr body, but not by instantiating it. + // Produce pending diagnostics for dependent access check. + SemaRef.PerformDependentDiagnostics(E->getBody(), TemplateArgs); + // TODO(usx): Store SFINAE diagnostics in RequiresExpr for diagnosis. + if (Trap.hasErrorOccurred()) + TransReq.getAs()->setSatisfied(false); + } + return TransReq; } bool TransformRequiresExprRequirements( diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp @@ -104,3 +104,138 @@ constexpr bool b = requires (X &x) { static_cast(nullptr); }; // expected-error@-1{{constraint variable 'x' cannot be used in an evaluated context}} // expected-note@-2{{'x' declared here}} + +namespace access_checks { +namespace in_requires_expression { +template +struct A { + static constexpr bool foo(); + static constexpr bool bar(); + static constexpr bool baz(); + static constexpr bool faz(); +}; + +class C{}; + +class B { + void p() {} + bool data_member = true; + static const bool static_member = true; + friend struct A<0>; +}; + +template +constexpr bool A::foo() { + return requires(B b) { b.p(); }; +} +static_assert(!A<1>::foo()); +static_assert(A<0>::foo()); + +template +constexpr bool A::bar() { + return requires() { B::static_member; }; +} +static_assert(!A<1>::bar()); +static_assert(A<0>::bar()); + +template +constexpr bool A::baz() { + return requires(B b) { b.data_member; }; +} +static_assert(!A<1>::baz()); +static_assert(A<0>::baz()); + +template +constexpr bool A::faz() { + return requires(B a, B b) { + a.p(); + b.data_member; + B::static_member; + }; +} +static_assert(!A<1>::faz()); +static_assert(A<0>::faz()); +} // namespace in_requires_expression + +namespace in_concepts { +// Dependent access does not cause hard errors. +template class A; + +template <> class A<0> { + static void f() {} +}; +template +concept C1 = requires() { A::f(); }; +static_assert(!C1<0>); + +template <> class A<1> { +public: + static void f() {} +}; +static_assert(C1<1>); + +// Non-dependent access to private member is a hard error. +class B{ + static void f() {} // expected-note 2{{implicitly declared private here}} +}; +template +concept C2 = requires() { B::f(); }; // expected-error {{'f' is a private member}} + +constexpr bool non_template_func() { + return requires() { + B::f(); // expected-error {{'f' is a private member}} + }; +} +template +constexpr bool template_func() { + return requires() { + A::f(); + }; +} +static_assert(!template_func<0>()); +static_assert(template_func<1>()); +} // namespace in_concepts + +namespace in_trailing_requires { +template struct B; +class A { + static void f(); + friend struct B; +}; + +template struct B { + static constexpr int index() requires requires{ A::f(); } { + return 1; + } + static constexpr int index() { + return 2; + } +}; + +static_assert(B::index() == 1); +static_assert(B::index() == 2); + +namespace missing_member_function { +template struct Use; +class X { + int a; + static int B; + friend struct Use; +}; +template struct Use { + constexpr static int foo() requires requires(X x) { x.a; } { + return 1; + } + constexpr static int bar() requires requires { X::B; } { + return 1; + } +}; + +void test() { + // TODO: Propagate diagnostic. + Use::foo(); //expected-error {{invalid reference to function 'foo': constraints not satisfied}} + static_assert(Use::foo() == 1); +} +} // namespace missing_member_function +} // namespace in_trailing_requires +} // namespace access_check