[clang] [Clang] Fix handling of qualified id-expressions in unevaluated contexts (PR #99807)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 21 02:56:36 PDT 2024
https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/99807
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.
Fixes #89713
Fixes #40906
>From 0a243d09a38667a71b3e3f29456b488c5288185f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 21 Jul 2024 11:49:45 +0200
Subject: [PATCH] [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}}
- }
}
More information about the cfe-commits
mailing list