This is an archive of the discontinued LLVM Phabricator instance.

[clang] Fix new-expression with elaborated-type-specifier
ClosedPublic

Authored by Fznamznon on Jun 27 2023, 4:47 AM.

Details

Summary

Expressions like

struct A {};
...
new struct A {};
struct A* b = (1 == 1) ? new struct A : new struct A;

Were parsed as redefinitions of struct A and failed, however as clarified by
CWG2141 new-expression cannot define a type, so both these examples
should be considered as references to the previously declared struct A.
The patch adds a "new" kind context for parsing declaration specifiers in
addition to already existing declarator context in order to track that
the parser is inside of a new expression.

Fixes https://github.com/llvm/llvm-project/issues/34341

Diff Detail

Event Timeline

Fznamznon created this revision.Jun 27 2023, 4:47 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 27 2023, 4:47 AM
Fznamznon requested review of this revision.Jun 27 2023, 4:47 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 27 2023, 4:47 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
Fznamznon edited the summary of this revision. (Show Details)Jun 27 2023, 4:48 AM

Thank you for the fix! Just to clarify some things before diving into the review too much... From the patch summary:

Expressions like

new struct A {};
struct A* b = (1 == 1) ? new struct A : new struct A;
Were parsed as definitions of struct A and failed, however as clarified by
CWG2141 new-expression cannot define a type, so both these examples
should be considered as valid.

There's a typo there -- the new struct A{}; bit should be struct A {}; (dropping the new), right?

I think the root cause of that issue is that we don't implement DR2141 (https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2141); are you intending to cover that DR? If so, there should be tests added to clang/test/CXX/drs/dr21xx.cpp.

clang/include/clang/Parse/Parser.h
2221

Adding the comma so the next person doesn't have to, and realigning to the usual indentation

Thank you for the fix! Just to clarify some things before diving into the review too much... From the patch summary:

Expressions like

new struct A {};
struct A* b = (1 == 1) ? new struct A : new struct A;
Were parsed as definitions of struct A and failed, however as clarified by
CWG2141 new-expression cannot define a type, so both these examples
should be considered as valid.

There's a typo there -- the new struct A{}; bit should be struct A {}; (dropping the new), right?

I think the root cause of that issue is that we don't implement DR2141 (https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2141); are you intending to cover that DR? If so, there should be tests added to clang/test/CXX/drs/dr21xx.cpp.

With offline help from @erichkeane and @shafik I realized where my confusion came from here. The patch summary presumes struct A is already defined in order for us to accept those code examples. I was thrown off by the DR and the original issue using struct A {}; as the first relevant line in the code example and was thinking there was confusion as to how the DR was resolved.

How about the summary be changed to something along the lines of:

With code like struct A {}; <and the rest of your example>, the expressions were parsed as redefining struct A and failed. However, as clarified by CWG2141, new-expression cannot define a type, so both these expressions should be considered as valid references to the previously declared struct A.

Fznamznon added a comment.EditedJun 27 2023, 12:18 PM

With code like struct A {}; <and the rest of your example>, the expressions were parsed as redefining struct A and failed. However, as clarified by CWG2141, new-expression cannot define a type, so both these expressions should be considered as valid references to the previously declared struct A.

Sure, thank you and sorry for the confusion! I should have added struct A {}; to the example in the first place.

I think the root cause of that issue is that we don't implement DR2141 (https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2141); are you intending to cover that DR? If so, there should be tests added to clang/test/CXX/drs/dr21xx.cpp.

The original intent was to fix the bug because I wasn't confident enough that I'll be able to cover all possible cases by the tests. I could try go deeper and work to support DR2141. Will the simple examples that I'm already adding to clang/test/Parser/cxx11-type-specifier.cpp test be enough for the first iteration?

Fznamznon edited the summary of this revision. (Show Details)Jun 27 2023, 12:21 PM
shafik added a comment.EditedJun 27 2023, 2:04 PM

What about

template <typename T>
struct A { };

  void foo() {
    new struct A<int> {};
  }

I think this should be allowed, right? Note MSVC accepts it: https://godbolt.org/z/j7djYs8bq

MSVC rejects the first but accepts the second which I think is wrong but worth testing:

alignof(struct B {});
sizeof(struct B{});

https://godbolt.org/z/153jYqaPM

clang/include/clang/Parse/Parser.h
2359

Should this be below since DeclaratorContext::CXXNew was grouped w/ DSC_normal normal before?

What about
template <typename T>
struct A { };

void foo() {
  new struct A<int> {};
}

I think this should be allowed, right?

Yes, I think so. The patch helps to allow. Thanks, I'll add it to the test.

MSVC rejects the first but accepts the second which I think is wrong but worth testing:

alignof(struct B {});
sizeof(struct B{});

These don't have new so they are not affected by the patch ATM.
But why MSVC wrong? Operand of alignof is a type-id which is not defining-type-id meaning it shouldn't be parsed as type definition. So, struct B{} there is a reference to struct B with braced initializer, i.e. object creation. That is not a type-id, right? On the opposite, sizeof can accept either type-id or an expression, I guess this is why MSVC accepts.

clang/include/clang/Parse/Parser.h
2359

Not really. Since there was no DeclSpecContext for new expression, it was mostly handled as if it was DSC_type_specifier.

Fznamznon updated this revision to Diff 535317.Jun 28 2023, 4:10 AM

Add comma and test

Fznamznon added inline comments.Jun 28 2023, 4:11 AM
clang/include/clang/Parse/Parser.h
2221

Added comma, but the indentation is what clang-format gave me. I'm afraid I won't pass the pre-commit with different .

MSVC rejects the first but accepts the second which I think is wrong but worth testing:

alignof(struct B {});
sizeof(struct B{});

These don't have new so they are not affected by the patch ATM.
But why MSVC wrong? Operand of alignof is a type-id which is not defining-type-id meaning it shouldn't be parsed as type definition. So, struct B{} there is a reference to struct B with braced initializer, i.e. object creation. That is not a type-id, right? On the opposite, sizeof can accept either type-id or an expression, I guess this is why MSVC accepts.

new is special b/c it is has new-initializer after the type-id so that can be an initialization: https://eel.is/c++draft/expr.new#nt:new-expression in sizeof it is a definition which is not allowed and so it should be rejected.

if you do:

struct B{ int x; };

void f() {
   struct B{};
}

That is a definition in f: https://godbolt.org/z/38eM6z17K

Fznamznon updated this revision to Diff 535779.Jun 29 2023, 7:08 AM

Move test cases to CXX/drs/dr21xx.cpp, update DR status page, add alignof/sizeof
cases, rebase

aaron.ballman accepted this revision.Jul 2 2023, 10:43 AM

LGTM, thank you!

clang/docs/ReleaseNotes.rst
556
clang/include/clang/Parse/Parser.h
2221

Ah, we don't care if pre-commit CI fails because of clang-format due to following the surrounding formatting style. That said, this formatting is already inconsistent (I hadn't noticed the first three are at a different indentation level as well), so this is fine.

This revision is now accepted and ready to land.Jul 2 2023, 10:43 AM
This revision was landed with ongoing or failed builds.Jul 3 2023, 3:13 AM
This revision was automatically updated to reflect the committed changes.