Skip to content

Commit

Permalink
[clang] pointer to member with qualified-id enclosed in parentheses i…
Browse files Browse the repository at this point in the history
…n unevaluated context should be invalid (llvm#89713)

clang don't check whether the operand of the & operator is enclosed in
parantheses when pointer to member is formed in unevaluated context, for
example:

```cpp
struct foo { int val; };

int main() { decltype(&(foo::val)) ptr; }
```

`decltype(&(foo::val))` should be invalid, but clang accepts it. This PR
fixes this issue.

Fixes llvm#40906.

---------

Co-authored-by: cor3ntin <[email protected]>
  • Loading branch information
zwuis and cor3ntin authored May 3, 2024
1 parent e4b04b3 commit 8480c93
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 0 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ C++ Specific Potentially Breaking Changes
it's negative spelling can be used to obtain compatibility with previous
versions of clang.

- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).

ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7511,6 +7511,9 @@ 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<
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14651,6 +14651,22 @@ 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();

Expand Down
17 changes: 17 additions & 0 deletions clang/test/CXX/expr/expr.unary/expr.unary.op/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,20 @@ namespace test2 {
int (A::*ptr)(int) = &(A::foo); // expected-error {{cannot create a non-constant pointer to member function}}
}
}

namespace GH40906 {
struct A {
int val;
void func() {}
};

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: 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}}
}
}

0 comments on commit 8480c93

Please sign in to comment.