Skip to content

Commit 2d0137d

Browse files
authored
[clang] Differentiate between identifier and string EnumArgument (#68550)
EnumArgument may be a string or an identifier. If it is a string, it should be parsed as unevaluated string literal. Add IsString flag to EnumArgument so that the parser can choose the correct parsing method. Target-specific attributes that share spelling may have different attribute "prototypes". For example, ARM's version of "interrupt" attribute accepts a string enum, while MSP430's version accepts an unsigned integer. Adjust ClangAttrEmitter so that the generated `attributeStringLiteralListArg` returns the correct mask depending on target triple. It is worth noting that even after this change some string arguments are still parsed as identifiers or, worse, as expressions. This is because of some special logic in `ParseAttributeArgsCommon`. Fixing it is out of scope of this patch.
1 parent 3496927 commit 2d0137d

File tree

11 files changed

+122
-62
lines changed

11 files changed

+122
-62
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 48 additions & 36 deletions
Large diffs are not rendered by default.

clang/lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ static bool attributeHasIdentifierArg(const IdentifierInfo &II) {
291291

292292
/// Determine whether the given attribute has an identifier argument.
293293
static ParsedAttributeArgumentsProperties
294-
attributeStringLiteralListArg(const IdentifierInfo &II) {
294+
attributeStringLiteralListArg(const llvm::Triple &T, const IdentifierInfo &II) {
295295
#define CLANG_ATTR_STRING_LITERAL_ARG_LIST
296296
return llvm::StringSwitch<uint32_t>(normalizeAttrName(II.getName()))
297297
#include "clang/Parse/AttrParserStringSwitches.inc"
@@ -550,7 +550,7 @@ unsigned Parser::ParseAttributeArgsCommon(
550550

551551
ExprVector ParsedExprs;
552552
ParsedAttributeArgumentsProperties ArgProperties =
553-
attributeStringLiteralListArg(*AttrName);
553+
attributeStringLiteralListArg(getTargetInfo().getTriple(), *AttrName);
554554
if (ParseAttributeArgumentList(*AttrName, ParsedExprs, ArgProperties)) {
555555
SkipUntil(tok::r_paren, StopAtSemi);
556556
return 0;

clang/test/Sema/attr-function-return.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ __attribute__((function_return("thunk-extern"))) void w(void) {}
1313
// expected-warning@+1 {{'function_return' attribute argument not supported: invalid}}
1414
__attribute__((function_return("invalid"))) void v(void) {}
1515

16-
// expected-error@+1 {{'function_return' attribute requires a string}}
16+
// expected-error@+1 {{expected string literal as argument of 'function_return' attribute}}
1717
__attribute__((function_return(5))) void a(void) {}
1818

1919
// expected-error@+1 {{'function_return' attribute takes one argument}}

clang/test/Sema/callingconv-iamcu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ int __attribute__((pcs("aapcs", "aapcs"))) pcs1(void); // expected-error {{'pcs'
3636
int __attribute__((pcs())) pcs2(void); // expected-error {{'pcs' attribute takes one argument}}
3737
int __attribute__((pcs(pcs1))) pcs3(void); // expected-error {{'pcs' attribute requires a string}} \
3838
// expected-error {{invalid PCS type}}
39-
int __attribute__((pcs(0))) pcs4(void); // expected-error {{'pcs' attribute requires a string}}
39+
int __attribute__((pcs(0))) pcs4(void); // expected-error {{expected string literal as argument of 'pcs' attribute}}
4040
/* These are ignored because the target is i386 and not ARM */
4141
int __attribute__((pcs("aapcs"))) pcs5(void); // expected-warning {{'pcs' calling convention is not supported for this target}}
4242
int __attribute__((pcs("aapcs-vfp"))) pcs6(void); // expected-warning {{'pcs' calling convention is not supported for this target}}

clang/test/Sema/callingconv.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ int __attribute__((pcs("aapcs", "aapcs"))) pcs1(void); // expected-error {{'pcs'
4545
int __attribute__((pcs())) pcs2(void); // expected-error {{'pcs' attribute takes one argument}}
4646
int __attribute__((pcs(pcs1))) pcs3(void); // expected-error {{'pcs' attribute requires a string}} \
4747
// expected-error {{invalid PCS type}}
48-
int __attribute__((pcs(0))) pcs4(void); // expected-error {{'pcs' attribute requires a string}}
48+
int __attribute__((pcs(0))) pcs4(void); // expected-error {{expected string literal as argument of 'pcs' attribute}}
4949
/* These are ignored because the target is i386 and not ARM */
5050
int __attribute__((pcs("aapcs"))) pcs5(void); // expected-warning {{'pcs' calling convention is not supported for this target}}
5151
int __attribute__((pcs("aapcs-vfp"))) pcs6(void); // expected-warning {{'pcs' calling convention is not supported for this target}}

clang/test/Sema/mips-interrupt-attr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ struct a { int b; };
33

44
struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to functions and methods}}
55

6+
__attribute((interrupt(42))) void foo0(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
67
__attribute__((interrupt("EIC"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: 'EIC'}}
78

89
__attribute__((interrupt("eic", 1))) void foo2(void) {} // expected-error {{'interrupt' attribute takes no more than 1 argument}}

clang/test/Sema/riscv-interrupt-attr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct a { int b; };
2525

2626
struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to functions}}
2727

28+
__attribute__((interrupt(42))) void foo0(void) {} // expected-error {{expected string literal as argument of 'interrupt' attribute}}
2829
__attribute__((interrupt("USER"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: USER}}
2930
__attribute__((interrupt("user"))) void foo1b(void) {} // expected-warning {{'interrupt' attribute argument not supported: user}}
3031
__attribute__((interrupt("MACHINE"))) void foo1c(void) {} // expected-warning {{'interrupt' attribute argument not supported: MACHINE}}

clang/test/Sema/zero_call_used_regs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
void failure1(void) _zero_call_used_regs(); // expected-error {{takes one argument}}
66
void failure2(void) _zero_call_used_regs("used", "used-gpr"); // expected-error {{takes one argument}}
7-
void failure3(void) _zero_call_used_regs(0); // expected-error {{requires a string}}
7+
void failure3(void) _zero_call_used_regs(0); // expected-error {{expected string literal}}
88
void failure4(void) _zero_call_used_regs("hello"); // expected-warning {{argument not supported: hello}}
99

1010
void success1(void) _zero_call_used_regs("skip");

clang/test/SemaCXX/warn-consumed-parsing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void function3() CONSUMABLE(consumed); // expected-warning {{'consumable' attrib
3535

3636
class CONSUMABLE(unknown) AttrTester1 {
3737
void callableWhen0() CALLABLE_WHEN("unconsumed");
38-
void callableWhen1() CALLABLE_WHEN(42); // expected-error {{'callable_when' attribute requires a string}}
38+
void callableWhen1() CALLABLE_WHEN(42); // expected-error {{expected string literal as argument of 'callable_when' attribute}}
3939
void callableWhen2() CALLABLE_WHEN("foo"); // expected-warning {{'callable_when' attribute argument not supported: foo}}
4040
void callableWhen3() CALLABLE_WHEN(unconsumed);
4141
void consumes() SET_TYPESTATE(consumed);

clang/test/SemaHLSL/shader_type_attr.hlsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ int forwardDecl() {
4747

4848
// expected-error@+1 {{'shader' attribute takes one argument}}
4949
[shader()]
50-
// expected-error@+1 {{'shader' attribute takes one argument}}
50+
// expected-error@+1 {{expected string literal as argument of 'shader' attribute}}
5151
[shader(1, 2)]
52-
// expected-error@+1 {{'shader' attribute requires a string}}
52+
// expected-error@+1 {{expected string literal as argument of 'shader' attribute}}
5353
[shader(1)]
5454
// expected-warning@+1 {{'shader' attribute argument not supported: cs}}
5555
[shader("cs")]

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,13 @@ static StringRef NormalizeGNUAttrSpelling(StringRef AttrSpelling) {
171171
typedef std::vector<std::pair<std::string, const Record *>> ParsedAttrMap;
172172

173173
static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records,
174-
ParsedAttrMap *Dupes = nullptr) {
174+
ParsedAttrMap *Dupes = nullptr,
175+
bool SemaOnly = true) {
175176
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
176177
std::set<std::string> Seen;
177178
ParsedAttrMap R;
178179
for (const auto *Attr : Attrs) {
179-
if (Attr->getValueAsBit("SemaHandler")) {
180+
if (!SemaOnly || Attr->getValueAsBit("SemaHandler")) {
180181
std::string AN;
181182
if (Attr->isSubClassOf("TargetSpecificAttr") &&
182183
!Attr->isValueUnset("ParseKind")) {
@@ -2358,19 +2359,21 @@ static bool isVariadicExprArgument(const Record *Arg) {
23582359
}
23592360

23602361
static bool isStringLiteralArgument(const Record *Arg) {
2361-
return !Arg->getSuperClasses().empty() &&
2362-
llvm::StringSwitch<bool>(
2363-
Arg->getSuperClasses().back().first->getName())
2364-
.Case("StringArgument", true)
2365-
.Default(false);
2362+
if (Arg->getSuperClasses().empty())
2363+
return false;
2364+
StringRef ArgKind = Arg->getSuperClasses().back().first->getName();
2365+
if (ArgKind == "EnumArgument")
2366+
return Arg->getValueAsBit("IsString");
2367+
return ArgKind == "StringArgument";
23662368
}
23672369

23682370
static bool isVariadicStringLiteralArgument(const Record *Arg) {
2369-
return !Arg->getSuperClasses().empty() &&
2370-
llvm::StringSwitch<bool>(
2371-
Arg->getSuperClasses().back().first->getName())
2372-
.Case("VariadicStringArgument", true)
2373-
.Default(false);
2371+
if (Arg->getSuperClasses().empty())
2372+
return false;
2373+
StringRef ArgKind = Arg->getSuperClasses().back().first->getName();
2374+
if (ArgKind == "VariadicEnumArgument")
2375+
return Arg->getValueAsBit("IsString");
2376+
return ArgKind == "VariadicStringArgument";
23742377
}
23752378

23762379
static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
@@ -2393,14 +2396,18 @@ static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
23932396
OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n";
23942397
}
23952398

2399+
static bool GenerateTargetSpecificAttrChecks(const Record *R,
2400+
std::vector<StringRef> &Arches,
2401+
std::string &Test,
2402+
std::string *FnName);
2403+
23962404
// Emits the list of arguments that should be parsed as unevaluated string
23972405
// literals for each attribute.
23982406
static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records,
23992407
raw_ostream &OS) {
24002408
OS << "#if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST)\n";
2401-
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
2402-
for (const auto *Attr : Attrs) {
2403-
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
2409+
2410+
auto MakeMask = [](ArrayRef<Record *> Args) {
24042411
uint32_t Bits = 0;
24052412
assert(Args.size() <= 32 && "unsupported number of arguments in attribute");
24062413
for (uint32_t N = 0; N < Args.size(); ++N) {
@@ -2411,11 +2418,46 @@ static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records,
24112418
break;
24122419
}
24132420
}
2414-
if (!Bits)
2421+
return Bits;
2422+
};
2423+
2424+
auto AddMaskWithTargetCheck = [](const Record *Attr, uint32_t Mask,
2425+
std::string &MaskStr) {
2426+
const Record *T = Attr->getValueAsDef("Target");
2427+
std::vector<StringRef> Arches = T->getValueAsListOfStrings("Arches");
2428+
std::string Test;
2429+
GenerateTargetSpecificAttrChecks(T, Arches, Test, nullptr);
2430+
MaskStr.append(Test + " ? " + std::to_string(Mask) + " : ");
2431+
};
2432+
2433+
ParsedAttrMap Dupes;
2434+
ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes, /*SemaOnly=*/false);
2435+
for (const auto &[AttrName, Attr] : Attrs) {
2436+
std::string MaskStr;
2437+
if (Attr->isSubClassOf("TargetSpecificAttr") &&
2438+
!Attr->isValueUnset("ParseKind")) {
2439+
if (uint32_t Mask = MakeMask(Attr->getValueAsListOfDefs("Args")))
2440+
AddMaskWithTargetCheck(Attr, Mask, MaskStr);
2441+
StringRef ParseKind = Attr->getValueAsString("ParseKind");
2442+
for (const auto &[DupeParseKind, DupAttr] : Dupes) {
2443+
if (DupeParseKind != ParseKind)
2444+
continue;
2445+
if (uint32_t Mask = MakeMask(DupAttr->getValueAsListOfDefs("Args")))
2446+
AddMaskWithTargetCheck(DupAttr, Mask, MaskStr);
2447+
}
2448+
if (!MaskStr.empty())
2449+
MaskStr.append("0");
2450+
} else {
2451+
if (uint32_t Mask = MakeMask(Attr->getValueAsListOfDefs("Args")))
2452+
MaskStr = std::to_string(Mask);
2453+
}
2454+
2455+
if (MaskStr.empty())
24152456
continue;
2457+
24162458
// All these spellings have at least one string literal has argument.
24172459
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
2418-
OS << ".Case(\"" << S.name() << "\", " << Bits << ")\n";
2460+
OS << ".Case(\"" << S.name() << "\", " << MaskStr << ")\n";
24192461
});
24202462
}
24212463
OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n";
@@ -3404,6 +3446,8 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
34043446
OS << " }\n";
34053447
}
34063448

3449+
} // namespace clang
3450+
34073451
// Helper function for GenerateTargetSpecificAttrChecks that alters the 'Test'
34083452
// parameter with only a single check type, if applicable.
34093453
static bool GenerateTargetSpecificAttrCheck(const Record *R, std::string &Test,
@@ -3570,6 +3614,8 @@ static void GenerateHasAttrSpellingStringSwitch(
35703614
OS << " .Default(0);\n";
35713615
}
35723616

3617+
namespace clang {
3618+
35733619
// Emits list of regular keyword attributes with info about their arguments.
35743620
void EmitClangRegularKeywordAttributeInfo(RecordKeeper &Records,
35753621
raw_ostream &OS) {

0 commit comments

Comments
 (0)