Skip to content

[Clang] Allow parsing arbitrary order of attributes for declarations #133107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ Bug Fixes to C++ Support
- Improved parser recovery of invalid requirement expressions. In turn, this
fixes crashes from follow-on processing of the invalid requirement. (#GH138820)
- Fixed the handling of pack indexing types in the constraints of a member function redeclaration. (#GH138255)
- Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107)
- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)

Bug Fixes to AST Handling
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3081,13 +3081,16 @@ class Parser : public CodeCompletionHandler {
bool CouldBeBitField = false);
Decl *ParseHLSLBuffer(SourceLocation &DeclEnd);

void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
bool MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
bool AttrsParsed = false;
if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
Tok.is(tok::l_square)) {
ParsedAttributes AttrsWithRange(AttrFactory);
ParseMicrosoftAttributes(AttrsWithRange);
AttrsParsed = !AttrsWithRange.empty();
Attrs.takeAllFrom(AttrsWithRange);
}
return AttrsParsed;
}
void ParseMicrosoftUuidAttributeArgs(ParsedAttributes &Attrs);
void ParseMicrosoftAttributes(ParsedAttributes &Attrs);
Expand Down
20 changes: 18 additions & 2 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3096,11 +3096,24 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
}

ParsedAttributes DeclSpecAttrs(AttrFactory);
MaybeParseMicrosoftAttributes(DeclSpecAttrs);

// Hold late-parsed attributes so we can attach a Decl to them later.
LateParsedAttrList CommonLateParsedAttrs;

while (MaybeParseCXX11Attributes(DeclAttrs) ||
MaybeParseGNUAttributes(DeclSpecAttrs, &CommonLateParsedAttrs) ||
MaybeParseMicrosoftAttributes(DeclSpecAttrs))
;

SourceLocation DeclStart;
if (DeclAttrs.Range.isValid()) {
DeclStart = DeclSpecAttrs.Range.isInvalid()
? DeclAttrs.Range.getBegin()
: std::min(DeclAttrs.Range.getBegin(),
DeclSpecAttrs.Range.getBegin());
} else {
DeclStart = DeclSpecAttrs.Range.getBegin();
}

// decl-specifier-seq:
// Parse the common declaration-specifiers piece.
ParsingDeclSpec DS(*this, TemplateDiags);
Expand Down Expand Up @@ -3128,6 +3141,9 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
// Turn off colon protection that was set for declspec.
X.restore();

if (DeclStart.isValid())
DS.SetRangeStart(DeclStart);

// If we had a free-standing type definition with a missing semicolon, we
// may get this far before the problem becomes obvious.
if (DS.hasTagDefinition() &&
Expand Down
13 changes: 8 additions & 5 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
}

default: {
if (getLangOpts().CPlusPlus && MaybeParseCXX11Attributes(CXX11Attrs, true))
goto Retry;

bool HaveAttrs = !CXX11Attrs.empty() || !GNUAttrs.empty();
auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); };
bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) &&
Expand All @@ -260,11 +263,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
GNUAttrs);
}
if (CXX11Attrs.Range.getBegin().isValid()) {
// The caller must guarantee that the CXX11Attrs appear before the
// GNUAttrs, and we rely on that here.
assert(GNUAttrs.Range.getBegin().isInvalid() ||
GNUAttrs.Range.getBegin() > CXX11Attrs.Range.getBegin());
DeclStart = CXX11Attrs.Range.getBegin();
// Order of C++11 and GNU attributes is may be arbitrary.
DeclStart = GNUAttrs.Range.getBegin().isInvalid()
? CXX11Attrs.Range.getBegin()
: std::min(CXX11Attrs.Range.getBegin(),
GNUAttrs.Range.getBegin());
} else if (GNUAttrs.Range.getBegin().isValid())
DeclStart = GNUAttrs.Range.getBegin();
return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd);
Expand Down
15 changes: 12 additions & 3 deletions clang/test/AST/ast-dump-template-json-win32-mangler-crash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,18 @@ int main()
// CHECK-NEXT: },
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
// CHECK-NEXT: "offset": 404,
// CHECK-NEXT: "col": 16,
// CHECK-NEXT: "tokLen": 9
// CHECK-NEXT: "spellingLoc": {
// CHECK-NEXT: "offset": 123,
// CHECK-NEXT: "line": 4,
// CHECK-NEXT: "col": 20,
// CHECK-NEXT: "tokLen": 1
// CHECK-NEXT: },
// CHECK-NEXT: "expansionLoc": {
// CHECK-NEXT: "offset": 393,
// CHECK-NEXT: "line": 17,
// CHECK-NEXT: "col": 5,
// CHECK-NEXT: "tokLen": 10
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "end": {
// CHECK-NEXT: "offset": 481,
Expand Down
23 changes: 23 additions & 0 deletions clang/test/Parser/c2x-alignas.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,26 @@

_Alignas(int) struct c1; // expected-warning {{'_Alignas' attribute ignored}}
alignas(int) struct c1; // expected-warning {{'alignas' attribute ignored}}


__attribute__(()) [[]] alignas(int) int a; // expected-none TODO: actually this line should be an error
__attribute__(()) alignas(int) [[]] int b; // expected-error {{an attribute list cannot appear here}}
__attribute__(()) alignas(int) int c; // expected-none
[[]] __attribute__(()) alignas(int) int d; // expected-none
alignas(int) [[]] __attribute__(()) int e; // expected-error {{an attribute list cannot appear here}}

struct C1 {
__attribute__(()) [[]] alignas(int) int a; // expected-error {{an attribute list cannot appear here}}
__attribute__(()) alignas(int) [[]] int b; // expected-error {{an attribute list cannot appear here}}
__attribute__(()) alignas(int) int c; // expected-none
[[]] __attribute__(()) alignas(int) int d; // expected-none
alignas(int) [[]] __attribute__(()) int e; // expected-error {{an attribute list cannot appear here}}
};

void fn_with_decl() {
__attribute__(()) [[]] alignas(int) int a; // expected-error {{an attribute list cannot appear here}}
__attribute__(()) alignas(int) [[]] int b; // expected-error {{an attribute list cannot appear here}}
__attribute__(()) alignas(int) int c; // expected-none
[[]] __attribute__(()) alignas(int) int d; // expected-none
alignas(int) [[]] __attribute__(()) int e; // expected-error {{an attribute list cannot appear here}}
}
24 changes: 24 additions & 0 deletions clang/test/Parser/cxx0x-attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ namespace test_misplacement {
[[]] enum E2 { }; //expected-error{{misplaced attributes}}
}

__attribute__(()) alignas(int) int xx; // expected-none
__attribute__(()) alignas(int) [[]] int yy; // expected-none
[[]] __attribute__(()) alignas(int) int zz; // expected-none
alignas(int) [[]] __attribute__(()) int aa; // expected-none
[[]] alignas(int) __attribute__(()) int bb; // expected-none
__attribute__(()) [[]] alignas(int) int cc; // expected-none

class C1 {
__attribute__(()) alignas(int) int x; // expected-none
__attribute__(()) alignas(int) [[]] int y; // expected-none
[[]] __attribute__(()) alignas(int) int z; // expected-none
alignas(int) [[]] __attribute__(()) int a; // expected-none
[[]] alignas(int) __attribute__(()) int b; // expected-none
__attribute__(()) [[]] alignas(int) int c; // expected-none
};

void fn_with_decl() {
__attribute__(()) alignas(int) int x; // expected-none
__attribute__(()) alignas(int) [[]] int y; // expected-none
[[]] __attribute__(()) alignas(int) int z; // expected-none
alignas(int) [[]] __attribute__(()) int a; // expected-none
[[]] alignas(int) __attribute__(()) int b; // expected-none
__attribute__(()) [[]] alignas(int) int c; // expected-none
}
// Checks attributes placed at wrong syntactic locations of class specifiers.
class [[]] [[]]
attr_after_class_name_decl [[]] [[]]; // expected-error {{an attribute list cannot appear here}}
Expand Down
15 changes: 15 additions & 0 deletions clang/test/SemaCUDA/cuda-attr-order.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Verify that we can parse a simple CUDA file with different attributes order.
// RUN: %clang_cc1 "-triple" "nvptx-nvidia-cuda" -fsyntax-only -verify %s
// expected-no-diagnostics
#include "Inputs/cuda.h"

struct alignas(16) float4 {
float x, y, z, w;
};

__attribute__((device)) float func() {
__shared__ alignas(alignof(float4)) float As[4][4]; // Both combinations
alignas(alignof(float4)) __shared__ float Bs[4][4]; // must be legal

return As[0][0] + Bs[0][0];
}
1 change: 1 addition & 0 deletions clang/test/SemaCXX/warn-thread-safety-analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2464,6 +2464,7 @@ class Foo {
// expected-warning {{declaration does not declare anything}}
exclusive_locks_required(a))); // \
// expected-warning {{attribute exclusive_locks_required ignored}}

};

} // end namespace WarnNoDecl
Expand Down
Loading