https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/99807
>From 0a243d09a38667a71b3e3f29456b488c5288185f Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Sun, 21 Jul 2024 11:49:45 +0200 Subject: [PATCH 1/2] [Clang] Fix handling of qualified id-expressions in unevaluated contexts In #89713, we made qualified, parenthesized id-expression ill-formed in and address of expressions. The expected behavior should instead be to form a pointer (rather than a pointer to member) The fix has been suggested by @zwuis and the tests by @hubert-reinterpretcast. It is worth pointing out that some of these tests seem rejected by all compilers, however the tests do seem correct. --- clang/docs/ReleaseNotes.rst | 3 +- .../clang/Basic/DiagnosticSemaKinds.td | 3 -- clang/lib/Sema/SemaExpr.cpp | 25 ++++------- .../CXX/expr/expr.unary/expr.unary.op/p4.cpp | 42 +++++++++++++------ 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e0e86af257a19..3dccd85895160 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -58,7 +58,8 @@ C++ Specific Potentially Breaking Changes versions of clang. The deprecation warning for the negative spelling can be disabled with `-Wno-deprecated-no-relaxed-template-template-args`. -- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906). +- Clang no longer tries to form pointer-to-members from qualified and parenthesized unevaluated expressions + such``decltype(&(foo::bar))``. (#GH40906). - Clang now performs semantic analysis for unary operators with dependent operands that are known to be of non-class non-enumeration type prior to instantiation. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d60f32674ca3a..0d6af644f1539 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7596,9 +7596,6 @@ def err_nested_non_static_member_use : Error< def warn_cxx98_compat_non_static_member_use : Warning< "use of non-static data member %0 in an unevaluated context is " "incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; -def err_form_ptr_to_member_from_parenthesized_expr : Error< - "cannot form pointer to member from a parenthesized expression; " - "did you mean to remove the parentheses?">; def err_invalid_incomplete_type_use : Error< "invalid use of incomplete type %0">; def err_builtin_func_cast_more_than_one_arg : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8d24e34520e77..1a441d99515f4 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14117,7 +14117,14 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { // Okay: we can take the address of a field. // Could be a pointer to member, though, if there is an explicit // scope qualifier for the class. - if (isa<DeclRefExpr>(op) && cast<DeclRefExpr>(op)->getQualifier()) { + + // [C++26] [expr.prim.id.general] + // If an id-expression E denotes a non-static non-type member + // of some class C [...] and if E is a qualified-id, E is + // not the un-parenthesized operand of the unary & operator [...] + // the id-expression is transformed into a class member access expression. + if (isa<DeclRefExpr>(op) && cast<DeclRefExpr>(op)->getQualifier() && + !isa<ParenExpr>(OrigOp.get())) { DeclContext *Ctx = dcl->getDeclContext(); if (Ctx && Ctx->isRecord()) { if (dcl->getType()->isReferenceType()) { @@ -14127,22 +14134,6 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { return QualType(); } - // C++11 [expr.unary.op] p4: - // A pointer to member is only formed when an explicit & is used and - // its operand is a qualified-id not enclosed in parentheses. - if (isa<ParenExpr>(OrigOp.get())) { - SourceLocation LeftParenLoc = OrigOp.get()->getBeginLoc(), - RightParenLoc = OrigOp.get()->getEndLoc(); - - Diag(LeftParenLoc, - diag::err_form_ptr_to_member_from_parenthesized_expr) - << SourceRange(OpLoc, RightParenLoc) - << FixItHint::CreateRemoval(LeftParenLoc) - << FixItHint::CreateRemoval(RightParenLoc); - - // Continuing might lead to better error recovery. - } - while (cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion()) Ctx = Ctx->getParent(); diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp index 162d59439d08e..170ca0a3f1c6b 100644 --- a/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp +++ b/clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp @@ -43,18 +43,36 @@ namespace test2 { } namespace GH40906 { - struct A { - int val; - void func() {} - }; +struct S { + int x; + void func(); + static_assert(__is_same_as(decltype((S::x)), int&), ""); + static_assert(__is_same_as(decltype(&(S::x)), int*), ""); - void test() { - decltype(&(A::val)) ptr1; // expected-error {{cannot form pointer to member from a parenthesized expression; did you mean to remove the parentheses?}} - int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}} + // FIXME: provide better error messages + static_assert(__is_same_as(decltype((S::func)), int&), ""); // expected-error {{call to non-static member function without an object argument}} + static_assert(__is_same_as(decltype(&(S::func)), int*), ""); // expected-error {{call to non-static member function without an object argument}} +}; +static_assert(__is_same_as(decltype((S::x)), int&), ""); +static_assert(__is_same_as(decltype(&(S::x)), int*), ""); +static_assert(__is_same_as(decltype((S::func)), int&), ""); // expected-error {{call to non-static member function without an object argument}} +static_assert(__is_same_as(decltype(&(S::func)), int*), ""); // expected-error {{call to non-static member function without an object argument}} + +struct A { int x;}; + +char q(int *); +short q(int A::*); + +template <typename T> +constexpr int f(char (*)[sizeof(q(&T::x))]) { return 1; } + +template <typename T> +constexpr int f(char (*)[sizeof(q(&(T::x)))]) { return 2; } + +constexpr int g(char (*p)[sizeof(char)] = 0) { return f<A>(p); } +constexpr int h(char (*p)[sizeof(short)] = 0) { return f<A>(p); } + +static_assert(g() == 2); +static_assert(h() == 1); - // FIXME: Error messages in these cases are less than clear, we can do - // better. - int size = sizeof(&(A::func)); // expected-error {{call to non-static member function without an object argument}} - void (A::* ptr3)() = &(A::func); // expected-error {{call to non-static member function without an object argument}} - } } >From 6130a37cdafb7e7aa4480cfc41d7b237ace282c8 Mon Sep 17 00:00:00 2001 From: cor3ntin <corentinja...@gmail.com> Date: Sun, 21 Jul 2024 14:44:29 +0200 Subject: [PATCH 2/2] Update clang/docs/ReleaseNotes.rst Co-authored-by: YanzuoLiu <zw...@outlook.com> --- clang/docs/ReleaseNotes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3dccd85895160..708be0e750af2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -59,7 +59,7 @@ C++ Specific Potentially Breaking Changes disabled with `-Wno-deprecated-no-relaxed-template-template-args`. - Clang no longer tries to form pointer-to-members from qualified and parenthesized unevaluated expressions - such``decltype(&(foo::bar))``. (#GH40906). + such as ``decltype(&(foo::bar))``. (#GH40906). - Clang now performs semantic analysis for unary operators with dependent operands that are known to be of non-class non-enumeration type prior to instantiation. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits