Skip to content

Commit ba85fc7

Browse files
cor3ntinyuxuanchen1997
authored andcommitted
[Clang] Fix handling of qualified id-expressions in unevaluated contexts (#99807)
Summary: 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 --------- Co-authored-by: YanzuoLiu <[email protected]> Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251286
1 parent 1b086a9 commit ba85fc7

File tree

4 files changed

+40
-33
lines changed

4 files changed

+40
-33
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ C++ Specific Potentially Breaking Changes
5858
versions of clang. The deprecation warning for the negative spelling can be
5959
disabled with `-Wno-deprecated-no-relaxed-template-template-args`.
6060

61-
- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).
61+
- Clang no longer tries to form pointer-to-members from qualified and parenthesized unevaluated expressions
62+
such as ``decltype(&(foo::bar))``. (#GH40906).
6263

6364
- Clang now performs semantic analysis for unary operators with dependent operands
6465
that are known to be of non-class non-enumeration type prior to instantiation.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7596,9 +7596,6 @@ def err_nested_non_static_member_use : Error<
75967596
def warn_cxx98_compat_non_static_member_use : Warning<
75977597
"use of non-static data member %0 in an unevaluated context is "
75987598
"incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore;
7599-
def err_form_ptr_to_member_from_parenthesized_expr : Error<
7600-
"cannot form pointer to member from a parenthesized expression; "
7601-
"did you mean to remove the parentheses?">;
76027599
def err_invalid_incomplete_type_use : Error<
76037600
"invalid use of incomplete type %0">;
76047601
def err_builtin_func_cast_more_than_one_arg : Error<

clang/lib/Sema/SemaExpr.cpp

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14117,7 +14117,14 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
1411714117
// Okay: we can take the address of a field.
1411814118
// Could be a pointer to member, though, if there is an explicit
1411914119
// scope qualifier for the class.
14120-
if (isa<DeclRefExpr>(op) && cast<DeclRefExpr>(op)->getQualifier()) {
14120+
14121+
// [C++26] [expr.prim.id.general]
14122+
// If an id-expression E denotes a non-static non-type member
14123+
// of some class C [...] and if E is a qualified-id, E is
14124+
// not the un-parenthesized operand of the unary & operator [...]
14125+
// the id-expression is transformed into a class member access expression.
14126+
if (isa<DeclRefExpr>(op) && cast<DeclRefExpr>(op)->getQualifier() &&
14127+
!isa<ParenExpr>(OrigOp.get())) {
1412114128
DeclContext *Ctx = dcl->getDeclContext();
1412214129
if (Ctx && Ctx->isRecord()) {
1412314130
if (dcl->getType()->isReferenceType()) {
@@ -14127,22 +14134,6 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
1412714134
return QualType();
1412814135
}
1412914136

14130-
// C++11 [expr.unary.op] p4:
14131-
// A pointer to member is only formed when an explicit & is used and
14132-
// its operand is a qualified-id not enclosed in parentheses.
14133-
if (isa<ParenExpr>(OrigOp.get())) {
14134-
SourceLocation LeftParenLoc = OrigOp.get()->getBeginLoc(),
14135-
RightParenLoc = OrigOp.get()->getEndLoc();
14136-
14137-
Diag(LeftParenLoc,
14138-
diag::err_form_ptr_to_member_from_parenthesized_expr)
14139-
<< SourceRange(OpLoc, RightParenLoc)
14140-
<< FixItHint::CreateRemoval(LeftParenLoc)
14141-
<< FixItHint::CreateRemoval(RightParenLoc);
14142-
14143-
// Continuing might lead to better error recovery.
14144-
}
14145-
1414614137
while (cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion())
1414714138
Ctx = Ctx->getParent();
1414814139

clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,36 @@ namespace test2 {
4343
}
4444

4545
namespace GH40906 {
46-
struct A {
47-
int val;
48-
void func() {}
49-
};
46+
struct S {
47+
int x;
48+
void func();
49+
static_assert(__is_same_as(decltype((S::x)), int&), "");
50+
static_assert(__is_same_as(decltype(&(S::x)), int*), "");
5051

51-
void test() {
52-
decltype(&(A::val)) ptr1; // expected-error {{cannot form pointer to member from a parenthesized expression; did you mean to remove the parentheses?}}
53-
int A::* ptr2 = &(A::val); // expected-error {{invalid use of non-static data member 'val'}}
52+
// FIXME: provide better error messages
53+
static_assert(__is_same_as(decltype((S::func)), int&), ""); // expected-error {{call to non-static member function without an object argument}}
54+
static_assert(__is_same_as(decltype(&(S::func)), int*), ""); // expected-error {{call to non-static member function without an object argument}}
55+
};
56+
static_assert(__is_same_as(decltype((S::x)), int&), "");
57+
static_assert(__is_same_as(decltype(&(S::x)), int*), "");
58+
static_assert(__is_same_as(decltype((S::func)), int&), ""); // expected-error {{call to non-static member function without an object argument}}
59+
static_assert(__is_same_as(decltype(&(S::func)), int*), ""); // expected-error {{call to non-static member function without an object argument}}
60+
61+
struct A { int x;};
62+
63+
char q(int *);
64+
short q(int A::*);
65+
66+
template <typename T>
67+
constexpr int f(char (*)[sizeof(q(&T::x))]) { return 1; }
68+
69+
template <typename T>
70+
constexpr int f(char (*)[sizeof(q(&(T::x)))]) { return 2; }
71+
72+
constexpr int g(char (*p)[sizeof(char)] = 0) { return f<A>(p); }
73+
constexpr int h(char (*p)[sizeof(short)] = 0) { return f<A>(p); }
74+
75+
static_assert(g() == 2);
76+
static_assert(h() == 1);
5477

55-
// FIXME: Error messages in these cases are less than clear, we can do
56-
// better.
57-
int size = sizeof(&(A::func)); // expected-error {{call to non-static member function without an object argument}}
58-
void (A::* ptr3)() = &(A::func); // expected-error {{call to non-static member function without an object argument}}
59-
}
6078
}

0 commit comments

Comments
 (0)