Skip to content

Commit

Permalink
parse: process GNU and standard attributes on top-level decls
Browse files Browse the repository at this point in the history
We would previously reject valid input where GNU attributes preceded the
standard attributes on top-level declarations. A previous attribute
handling change had begun rejecting this whilst GCC does honour this
layout. In practice, this breaks use of `extern "C"` attributed
functions which use both standard and GNU attributes as experienced by
the Swift runtime.

Objective-C deserves an honourable mention for requiring some additional
special casing. Because attributes on declarations and definitions
differ in semantics, we need to replicate some of the logic for
detecting attributes to declarations to which they appertain cannot be
attributed. This should match the existing case for the application of
GNU attributes to interfaces, protocols, and implementations.

Take the opportunity to split out the tooling tests into two cases: ones
which process macros and ones which do not.

Special thanks to Aaron Ballman for the many hints and extensive rubber
ducking that was involved in identifying the various places where we
accidentally dropped attributes.

Differential Revision: https://reviews.llvm.org/D137979
Fixes: #58229
Reviewed By: aaron.ballman, arphaman
  • Loading branch information
compnerd committed Nov 21, 2022
1 parent 26068c6 commit b78d538
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 71 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ Bug Fixes
and Clang 15 accidentally stopped predeclaring those functions in that
language mode. Clang 16 now predeclares those functions again. This fixes
`Issue 56607 <https://github.com/llvm/llvm-project/issues/56607>`_.
- GNU attributes being applied prior to standard attributes would be handled
improperly, which was corrected to match the behaviour exhibited by GCC.
`Issue 58229 <https://github.com/llvm/llvm-project/issues/58229>`_

Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
14 changes: 8 additions & 6 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1602,15 +1602,16 @@ class Parser : public CodeCompletionHandler {

//===--------------------------------------------------------------------===//
// C99 6.9: External Definitions.
DeclGroupPtrTy ParseExternalDeclaration(ParsedAttributes &Attrs,
DeclGroupPtrTy ParseExternalDeclaration(ParsedAttributes &DeclAttrs,
ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec *DS = nullptr);
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
DeclGroupPtrTy
ParseDeclarationOrFunctionDefinition(ParsedAttributes &Attrs,
ParsingDeclSpec *DS = nullptr,
AccessSpecifier AS = AS_none);
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
ParsedAttributes &DeclAttrs, ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec *DS = nullptr, AccessSpecifier AS = AS_none);
DeclGroupPtrTy ParseDeclOrFunctionDefInternal(ParsedAttributes &Attrs,
ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec &DS,
AccessSpecifier AS);

Expand All @@ -1625,7 +1626,8 @@ class Parser : public CodeCompletionHandler {

// Objective-C External Declarations
void MaybeSkipAttributes(tok::ObjCKeywordKind Kind);
DeclGroupPtrTy ParseObjCAtDirectives(ParsedAttributes &Attrs);
DeclGroupPtrTy ParseObjCAtDirectives(ParsedAttributes &DeclAttrs,
ParsedAttributes &DeclSpecAttrs);
DeclGroupPtrTy ParseObjCAtClassDeclaration(SourceLocation atLoc);
Decl *ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
ParsedAttributes &prefixAttrs);
Expand Down
30 changes: 17 additions & 13 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,10 @@ void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs,
if (index == InnerNSs.size()) {
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
Tok.isNot(tok::eof)) {
ParsedAttributes Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ParseExternalDeclaration(Attrs);
ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
}

// The caller is what called check -- we are simply calling
Expand Down Expand Up @@ -359,6 +360,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {

ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);

if (Tok.isNot(tok::l_brace)) {
// Reset the source range in DS, as the leading "extern"
Expand All @@ -367,7 +369,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
DS.SetRangeEnd(SourceLocation());
// ... but anyway remember that such an "extern" was seen.
DS.setExternInLinkageSpec(true);
ParseExternalDeclaration(DeclAttrs, &DS);
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs, &DS);
return LinkageSpec ? Actions.ActOnFinishLinkageSpecification(
getCurScope(), LinkageSpec, SourceLocation())
: nullptr;
Expand Down Expand Up @@ -407,9 +409,9 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
break;
[[fallthrough]];
default:
ParsedAttributes Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ParseExternalDeclaration(Attrs);
ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
continue;
}

Expand Down Expand Up @@ -439,9 +441,10 @@ Decl *Parser::ParseExportDeclaration() {

if (Tok.isNot(tok::l_brace)) {
// FIXME: Factor out a ParseExternalDeclarationWithAttrs.
ParsedAttributes Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ParseExternalDeclaration(Attrs);
ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
SourceLocation());
}
Expand All @@ -458,9 +461,10 @@ Decl *Parser::ParseExportDeclaration() {

while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
Tok.isNot(tok::eof)) {
ParsedAttributes Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ParseExternalDeclaration(Attrs);
ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
}

T.consumeClose();
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Parse/ParseHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {

while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
// FIXME: support attribute on constants inside cbuffer/tbuffer.
ParsedAttributes Attrs(AttrFactory);
ParsedAttributes DeclAttrs(AttrFactory);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);

DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs);
DeclGroupPtrTy Result =
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
*this)) {
T.skipToEnd();
Expand Down
44 changes: 32 additions & 12 deletions clang/lib/Parse/ParseObjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ void Parser::MaybeSkipAttributes(tok::ObjCKeywordKind Kind) {
/// [OBJC] objc-protocol-definition
/// [OBJC] objc-method-definition
/// [OBJC] '@' 'end'
Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives(ParsedAttributes &Attrs) {
Parser::DeclGroupPtrTy
Parser::ParseObjCAtDirectives(ParsedAttributes &DeclAttrs,
ParsedAttributes &DeclSpecAttrs) {
DeclAttrs.takeAllFrom(DeclSpecAttrs);

SourceLocation AtLoc = ConsumeToken(); // the "@"

if (Tok.is(tok::code_completion)) {
Expand All @@ -55,17 +59,29 @@ Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives(ParsedAttributes &Attrs) {
return nullptr;
}

switch (Tok.getObjCKeywordID()) {
case tok::objc_interface:
case tok::objc_protocol:
case tok::objc_implementation:
break;
default:
llvm::for_each(DeclAttrs, [this](const auto &Attr) {
if (Attr.isGNUAttribute())
Diag(Tok.getLocation(), diag::err_objc_unexpected_attr);
});
}

Decl *SingleDecl = nullptr;
switch (Tok.getObjCKeywordID()) {
case tok::objc_class:
return ParseObjCAtClassDeclaration(AtLoc);
case tok::objc_interface:
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, Attrs);
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, DeclAttrs);
break;
case tok::objc_protocol:
return ParseObjCAtProtocolDeclaration(AtLoc, Attrs);
return ParseObjCAtProtocolDeclaration(AtLoc, DeclAttrs);
case tok::objc_implementation:
return ParseObjCAtImplementationDeclaration(AtLoc, Attrs);
return ParseObjCAtImplementationDeclaration(AtLoc, DeclAttrs);
case tok::objc_end:
return ParseObjCAtEndDeclaration(AtLoc);
case tok::objc_compatibility_alias:
Expand Down Expand Up @@ -651,21 +667,23 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
if (Tok.is(tok::r_brace))
break;

ParsedAttributes EmptyAttrs(AttrFactory);
ParsedAttributes EmptyDeclAttrs(AttrFactory);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);

// Since we call ParseDeclarationOrFunctionDefinition() instead of
// ParseExternalDeclaration() below (so that this doesn't parse nested
// @interfaces), this needs to duplicate some code from the latter.
if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
allTUVariables.push_back(ParseDeclaration(
DeclaratorContext::File, DeclEnd, EmptyAttrs, EmptyDeclSpecAttrs));
allTUVariables.push_back(ParseDeclaration(DeclaratorContext::File,
DeclEnd, EmptyDeclAttrs,
EmptyDeclSpecAttrs));
continue;
}

allTUVariables.push_back(
ParseDeclarationOrFunctionDefinition(EmptyAttrs));
allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(
EmptyDeclAttrs, EmptyDeclSpecAttrs));
continue;
}

Expand Down Expand Up @@ -2236,9 +2254,11 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc,
{
ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl);
while (!ObjCImplParsing.isFinished() && !isEofOrEom()) {
ParsedAttributes attrs(AttrFactory);
MaybeParseCXX11Attributes(attrs);
if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) {
ParsedAttributes DeclAttrs(AttrFactory);
MaybeParseCXX11Attributes(DeclAttrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
if (DeclGroupPtrTy DGP =
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs)) {
DeclGroupRef DG = DGP.get();
DeclsInGroup.append(DG.begin(), DG.end());
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2304,9 +2304,10 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
// Here we expect to see some function declaration.
if (AS == AS_none) {
assert(TagType == DeclSpec::TST_unspecified);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ParsingDeclSpec PDS(*this);
Ptr = ParseExternalDeclaration(Attrs, &PDS);
Ptr = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs, &PDS);
} else {
Ptr =
ParseCXXClassMemberDeclarationWithPragmas(AS, Attrs, TagType, Tag);
Expand Down
64 changes: 40 additions & 24 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,10 +731,17 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
break;
}

ParsedAttributes attrs(AttrFactory);
MaybeParseCXX11Attributes(attrs);

Result = ParseExternalDeclaration(attrs);
ParsedAttributes DeclAttrs(AttrFactory);
ParsedAttributes DeclSpecAttrs(AttrFactory);
// GNU attributes are applied to the declaration specification while the
// standard attributes are applied to the declaration. We parse the two
// attribute sets into different containters so we can apply them during
// the regular parsing process.
while (MaybeParseCXX11Attributes(DeclAttrs) ||
MaybeParseGNUAttributes(DeclSpecAttrs))
;

Result = ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs);
// An empty Result might mean a line with ';' or some parsing error, ignore
// it.
if (Result) {
Expand Down Expand Up @@ -777,8 +784,10 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
///
/// [Modules-TS] module-import-declaration
///
Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
ParsingDeclSpec *DS) {
Parser::DeclGroupPtrTy
Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec *DS) {
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
ParenBraceBracketBalancer BalancerRAIIObj(*this);

Expand Down Expand Up @@ -866,7 +875,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
// __extension__ silences extension warnings in the subexpression.
ExtensionRAIIObject O(Diags); // Use RAII to do this.
ConsumeToken();
return ParseExternalDeclaration(Attrs);
return ParseExternalDeclaration(Attrs, DeclSpecAttrs);
}
case tok::kw_asm: {
ProhibitAttributes(Attrs);
Expand Down Expand Up @@ -894,7 +903,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
break;
}
case tok::at:
return ParseObjCAtDirectives(Attrs);
return ParseObjCAtDirectives(Attrs, DeclSpecAttrs);
case tok::minus:
case tok::plus:
if (!getLangOpts().ObjC) {
Expand Down Expand Up @@ -942,18 +951,16 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
// A function definition cannot start with any of these keywords.
{
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
DeclSpecAttrs);
}

case tok::kw_cbuffer:
case tok::kw_tbuffer:
if (getLangOpts().HLSL) {
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
DeclSpecAttrs);
}
goto dont_know;

Expand All @@ -964,9 +971,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
<< 0;
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
DeclSpecAttrs);
}
goto dont_know;

Expand All @@ -977,9 +983,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
// Inline namespaces. Allowed as an extension even in C++03.
if (NextKind == tok::kw_namespace) {
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
DeclSpecAttrs);
}

// Parse (then ignore) 'inline' prior to a template instantiation. This is
Expand All @@ -988,9 +993,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
<< 1;
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
DeclSpecAttrs);
}
}
goto dont_know;
Expand Down Expand Up @@ -1026,7 +1030,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
return nullptr;
}
// We can't tell whether this is a function-definition or declaration yet.
return ParseDeclarationOrFunctionDefinition(Attrs, DS);
return ParseDeclarationOrFunctionDefinition(Attrs, DeclSpecAttrs, DS);
}

// This routine returns a DeclGroup, if the thing we parsed only contains a
Expand Down Expand Up @@ -1091,7 +1095,17 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
/// [OMP] allocate-directive [TODO]
///
Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
ParsedAttributes &Attrs, ParsingDeclSpec &DS, AccessSpecifier AS) {
ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec &DS, AccessSpecifier AS) {
// Because we assume that the DeclSpec has not yet been initialised, we simply
// overwrite the source range and attribute the provided leading declspec
// attributes.
assert(DS.getSourceRange().isInvalid() &&
"expected uninitialised source range");
DS.SetRangeStart(DeclSpecAttrs.Range.getBegin());
DS.SetRangeEnd(DeclSpecAttrs.Range.getEnd());
DS.takeAttributesFrom(DeclSpecAttrs);

MaybeParseMicrosoftAttributes(DS.getAttributes());
// Parse the common declaration-specifiers piece.
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS,
Expand Down Expand Up @@ -1190,17 +1204,18 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
}

Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition(
ParsedAttributes &Attrs, ParsingDeclSpec *DS, AccessSpecifier AS) {
ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
ParsingDeclSpec *DS, AccessSpecifier AS) {
if (DS) {
return ParseDeclOrFunctionDefInternal(Attrs, *DS, AS);
return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, *DS, AS);
} else {
ParsingDeclSpec PDS(*this);
// Must temporarily exit the objective-c container scope for
// parsing c constructs and re-enter objc container scope
// afterwards.
ObjCDeclContextSwitch ObjCDC(*this);

return ParseDeclOrFunctionDefInternal(Attrs, PDS, AS);
return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, PDS, AS);
}
}

Expand Down Expand Up @@ -2342,7 +2357,8 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
ParsedAttributes Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs);
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs);
if (Result && !getCurScope()->getParent())
Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
}
Expand Down
Loading

0 comments on commit b78d538

Please sign in to comment.