[C] Fix parsing of [[clang::assume]] (#141747)

The assumption attribute is exposed with a Clang spelling, which means
we support __attribute__((assume)) as well as [[clang::assume]] as
spellings for the attribute.

In C++, the [[clang::assume]] spelling worked as expected. In C, that
spelling would emit an "unknown attribute ignored" diagnostic. This was
happening because we were failing to pass in the scope name and source
location when creating the attribute. In C++, this worked by chance
because [[assume]] is a known attribute in C++. But in C, where there is
thankfully no [[assume]] standard attribute, the lack of a scope meant
we would set the attribute kind to "unknown".
This commit is contained in:
Aaron Ballman
2025-05-28 10:07:14 -04:00
committed by GitHub
parent 2b1ebef8b8
commit 5e28af04f3
5 changed files with 45 additions and 15 deletions

View File

@@ -226,6 +226,9 @@ C Language Changes
- Added the existing ``-Wduplicate-decl-specifier`` diagnostic, which is on by
default, to ``-Wc++-compat`` because duplicated declaration specifiers are
not valid in C++.
- The ``[[clang::assume()]]`` attribute is now correctly recognized in C. The
``__attribute__((assume()))`` form has always been supported, so the fix is
specific to the attribute syntax used.
C2y Feature Support
^^^^^^^^^^^^^^^^^^^

View File

@@ -3037,11 +3037,11 @@ private:
/// Parse the argument to C++23's [[assume()]] attribute. Returns true on
/// error.
bool ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
SourceLocation *EndLoc,
ParsedAttr::Form Form);
bool
ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs, IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
SourceLocation *EndLoc, ParsedAttr::Form Form);
/// Try to parse an 'identifier' which appears within an attribute-token.
///

View File

@@ -676,7 +676,8 @@ void Parser::ParseGNUAttributeArgs(
Form);
return;
} else if (AttrKind == ParsedAttr::AT_CXXAssume) {
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form);
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, ScopeName,
ScopeLoc, EndLoc, Form);
return;
}
@@ -734,7 +735,8 @@ unsigned Parser::ParseClangAttributeArgs(
break;
case ParsedAttr::AT_CXXAssume:
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form);
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, ScopeName,
ScopeLoc, EndLoc, Form);
break;
}
return !Attrs.empty() ? Attrs.begin()->getNumArgs() : 0;

View File

@@ -4452,11 +4452,10 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
}
}
bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
IdentifierInfo *AttrName,
SourceLocation AttrNameLoc,
SourceLocation *EndLoc,
ParsedAttr::Form Form) {
bool Parser::ParseCXXAssumeAttributeArg(
ParsedAttributes &Attrs, IdentifierInfo *AttrName,
SourceLocation AttrNameLoc, IdentifierInfo *ScopeName,
SourceLocation ScopeLoc, SourceLocation *EndLoc, ParsedAttr::Form Form) {
assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
@@ -4498,8 +4497,8 @@ bool Parser::ParseCXXAssumeAttributeArg(ParsedAttributes &Attrs,
ArgsUnion Assumption = Res.get();
auto RParen = Tok.getLocation();
T.consumeClose();
Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), nullptr,
SourceLocation(), &Assumption, 1, Form);
Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), ScopeName, ScopeLoc,
&Assumption, 1, Form);
if (EndLoc)
*EndLoc = RParen;
@@ -4565,7 +4564,8 @@ bool Parser::ParseCXX11AttributeArgs(
ScopeName, ScopeLoc, Form);
// So does C++23's assume() attribute.
else if (!ScopeName && AttrName->isStr("assume")) {
if (ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form))
if (ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, nullptr,
SourceLocation{}, EndLoc, Form))
return true;
NumArgs = 1;
} else

25
clang/test/Sema/assume.c Normal file
View File

@@ -0,0 +1,25 @@
// RUN: %clang_cc1 -std=c23 %s -verify
// Validate that the attribute works in C.
static_assert(!__has_c_attribute(assume));
static_assert(__has_c_attribute(clang::assume));
static_assert(__has_attribute(assume));
void test(int n) {
// Smoke test.
__attribute__((assume(true)));
[[clang::assume(true)]];
// Test diagnostics
__attribute__((assume)); // expected-error {{'assume' attribute takes one argument}}
__attribute__((assume())); // expected-error {{expected expression}}
[[clang::assume]]; // expected-error {{'assume' attribute takes one argument}}
[[clang::assume()]]; // expected-error {{expected expression}}
__attribute__((assume(n++))); // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
[[clang::assume(n++)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
[[clang::assume(true)]] int x; // expected-error {{'assume' attribute cannot be applied to a declaration}}
__attribute__((assume(true))) int y; // expected-error {{'assume' attribute cannot be applied to a declaration}}
}