Skip to content

Commit 96dd012

Browse files
committed
[clang] Differentiate between identifier and string EnumArgument
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 970152b commit 96dd012

File tree

9 files changed

+119
-61
lines changed

9 files changed

+119
-61
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 47 additions & 35 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/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")) {
@@ -2335,19 +2336,21 @@ static bool isVariadicExprArgument(const Record *Arg) {
23352336
}
23362337

23372338
static bool isStringLiteralArgument(const Record *Arg) {
2338-
return !Arg->getSuperClasses().empty() &&
2339-
llvm::StringSwitch<bool>(
2340-
Arg->getSuperClasses().back().first->getName())
2341-
.Case("StringArgument", true)
2342-
.Default(false);
2339+
if (Arg->getSuperClasses().empty())
2340+
return false;
2341+
StringRef ArgKind = Arg->getSuperClasses().back().first->getName();
2342+
if (ArgKind == "EnumArgument")
2343+
return Arg->getValueAsBit("IsString");
2344+
return ArgKind == "StringArgument";
23432345
}
23442346

23452347
static bool isVariadicStringLiteralArgument(const Record *Arg) {
2346-
return !Arg->getSuperClasses().empty() &&
2347-
llvm::StringSwitch<bool>(
2348-
Arg->getSuperClasses().back().first->getName())
2349-
.Case("VariadicStringArgument", true)
2350-
.Default(false);
2348+
if (Arg->getSuperClasses().empty())
2349+
return false;
2350+
StringRef ArgKind = Arg->getSuperClasses().back().first->getName();
2351+
if (ArgKind == "VariadicEnumArgument")
2352+
return Arg->getValueAsBit("IsString");
2353+
return ArgKind == "VariadicStringArgument";
23512354
}
23522355

23532356
static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
@@ -2370,14 +2373,18 @@ static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
23702373
OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n";
23712374
}
23722375

2376+
static bool GenerateTargetSpecificAttrChecks(const Record *R,
2377+
std::vector<StringRef> &Arches,
2378+
std::string &Test,
2379+
std::string *FnName);
2380+
23732381
// Emits the list of arguments that should be parsed as unevaluated string
23742382
// literals for each attribute.
23752383
static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records,
23762384
raw_ostream &OS) {
23772385
OS << "#if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST)\n";
2378-
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
2379-
for (const auto *Attr : Attrs) {
2380-
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
2386+
2387+
auto MakeMask = [](ArrayRef<Record *> Args) {
23812388
uint32_t Bits = 0;
23822389
assert(Args.size() <= 32 && "unsupported number of arguments in attribute");
23832390
for (uint32_t N = 0; N < Args.size(); ++N) {
@@ -2388,11 +2395,46 @@ static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records,
23882395
break;
23892396
}
23902397
}
2391-
if (!Bits)
2398+
return Bits;
2399+
};
2400+
2401+
auto AddMaskWithTargetCheck = [](const Record *Attr, uint32_t Mask,
2402+
std::string &MaskStr) {
2403+
const Record *T = Attr->getValueAsDef("Target");
2404+
std::vector<StringRef> Arches = T->getValueAsListOfStrings("Arches");
2405+
std::string Test;
2406+
GenerateTargetSpecificAttrChecks(T, Arches, Test, nullptr);
2407+
MaskStr.append(Test + " ? " + std::to_string(Mask) + " : ");
2408+
};
2409+
2410+
ParsedAttrMap Dupes;
2411+
ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes, /*SemaOnly=*/false);
2412+
for (const auto &[AttrName, Attr] : Attrs) {
2413+
std::string MaskStr;
2414+
if (Attr->isSubClassOf("TargetSpecificAttr") &&
2415+
!Attr->isValueUnset("ParseKind")) {
2416+
if (uint32_t Mask = MakeMask(Attr->getValueAsListOfDefs("Args")))
2417+
AddMaskWithTargetCheck(Attr, Mask, MaskStr);
2418+
StringRef ParseKind = Attr->getValueAsString("ParseKind");
2419+
for (const auto &[DupeParseKind, DupAttr] : Dupes) {
2420+
if (DupeParseKind != ParseKind)
2421+
continue;
2422+
if (uint32_t Mask = MakeMask(DupAttr->getValueAsListOfDefs("Args")))
2423+
AddMaskWithTargetCheck(DupAttr, Mask, MaskStr);
2424+
}
2425+
if (!MaskStr.empty())
2426+
MaskStr.append("0");
2427+
} else {
2428+
if (uint32_t Mask = MakeMask(Attr->getValueAsListOfDefs("Args")))
2429+
MaskStr = std::to_string(Mask);
2430+
}
2431+
2432+
if (MaskStr.empty())
23922433
continue;
2434+
23932435
// All these spellings have at least one string literal has argument.
23942436
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
2395-
OS << ".Case(\"" << S.name() << "\", " << Bits << ")\n";
2437+
OS << ".Case(\"" << S.name() << "\", " << MaskStr << ")\n";
23962438
});
23972439
}
23982440
OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n";
@@ -3381,6 +3423,8 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
33813423
OS << " }\n";
33823424
}
33833425

3426+
} // namespace clang
3427+
33843428
// Helper function for GenerateTargetSpecificAttrChecks that alters the 'Test'
33853429
// parameter with only a single check type, if applicable.
33863430
static bool GenerateTargetSpecificAttrCheck(const Record *R, std::string &Test,
@@ -3547,6 +3591,8 @@ static void GenerateHasAttrSpellingStringSwitch(
35473591
OS << " .Default(0);\n";
35483592
}
35493593

3594+
namespace clang {
3595+
35503596
// Emits the list of tokens for regular keyword attributes.
35513597
void EmitClangAttrTokenKinds(RecordKeeper &Records, raw_ostream &OS) {
35523598
emitSourceFileHeader("A list of tokens generated from the attribute"

0 commit comments

Comments
 (0)