-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[clang] Differentiate between identifier and string EnumArgument #68550
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
I'll add some test for 'interrupt' attribute that is the only attribute affected by |
@@ -45,7 +45,7 @@ int __attribute__((pcs("aapcs", "aapcs"))) pcs1(void); // expected-error {{'pcs' | |||
int __attribute__((pcs())) pcs2(void); // expected-error {{'pcs' attribute takes one argument}} | |||
int __attribute__((pcs(pcs1))) pcs3(void); // expected-error {{'pcs' attribute requires a string}} \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an example when a string argument is not parsed as unevaluated string literal.
eb100a5
to
7199c73
Compare
@@ -886,7 +893,7 @@ def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> { | |||
// MSP430Interrupt's, MipsInterrupt's and AnyX86Interrupt's spellings | |||
// must match. | |||
let Spellings = [GCC<"interrupt">]; | |||
let Args = [EnumArgument<"Interrupt", "InterruptType", | |||
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears TableGen now supports named arguments (91ccbc6), probably makes sense to use them here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that named arguments can only be specified after all positional arguments.
If I move is_string
before opt
, this can break downstream code because values supplied for opt
will now be passed to is_string
. The other option is to make is_string
optional and to move it to the end of the list, but I really wouldn't want to do this because there is no valid default value for this argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense - silent downstream breakage is not nice. I guess the other option would be to also use named arguments for values
and/or enums
but that will make the diff a bit larger.
// a list of strings to accept, and a list of enumerators to map them to. | ||
class EnumArgument<string name, string type, list<string> values, | ||
// a list of possible values, and a list of enumerators to map them to. | ||
class EnumArgument<string name, string type, bit is_string, list<string> values, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rather than splitting this into doing 2 things, we should instead have an UnevaluatedStringArgument
type here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand, could you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of Modifying EnumArgument
to support a string mode, you should create a UnevaluatedStringArgument
type and leave EnumArgument
alone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for late reply. All String arguments are already unevaluated as made by https://reviews.llvm.org/D156237.
This patch makes Enum arguments unevaluated too, but only those that are effectively strings
Consider:
__attribute__((visibility("default" / "protected" /...)))
__attribute__((enum_extensibility(open / closed)))
).
Both attributes have enum arguments. The first one has a string enum argument (which should be parsed unevaluated), while the second has an identifier enum argument.
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.
7199c73
to
96dd012
Compare
Rebased to resolve merge conflicts in Attr.td. |
df10202
to
f5d2262
Compare
@llvm/pr-subscribers-backend-risc-v @llvm/pr-subscribers-clang Author: Sergei Barannikov (s-barannikov) ChangesEnumArgument 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 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 Patch is 32.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/68550.diff 11 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index a03b0e44e15f7d..6df1f3ba1a822f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -284,12 +284,15 @@ class DefaultIntArgument<string name, int default> : IntArgument<name, 1> {
// This argument is more complex, it includes the enumerator type
// name, whether the enum type is externally defined, a list of
-// strings to accept, and a list of enumerators to map them to.
-class EnumArgument<string name, string type, list<string> values,
+// possible values, and a list of enumerators to map them to.
+class EnumArgument<string name, string type, bit is_string, list<string> values,
list<string> enums, bit opt = 0, bit fake = 0,
bit isExternalType = 0>
: Argument<name, opt, fake> {
string Type = type;
+ // When true, the argument will be parsed as an unevaluated string literal
+ // and otherwise as an identifier.
+ bit IsString = is_string;
list<string> Values = values;
list<string> Enums = enums;
bit IsExternalType = isExternalType;
@@ -297,10 +300,14 @@ class EnumArgument<string name, string type, list<string> values,
// FIXME: There should be a VariadicArgument type that takes any other type
// of argument and generates the appropriate type.
-class VariadicEnumArgument<string name, string type, list<string> values,
- list<string> enums, bit isExternalType = 0>
+class VariadicEnumArgument<string name, string type, bit is_string,
+ list<string> values, list<string> enums,
+ bit isExternalType = 0>
: Argument<name, 1> {
string Type = type;
+ // When true, the argument will be parsed as an unevaluated string literal
+ // and otherwise as an identifier.
+ bit IsString = is_string;
list<string> Values = values;
list<string> Enums = enums;
bit IsExternalType = isExternalType;
@@ -904,7 +911,7 @@ def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
// MSP430Interrupt's, MipsInterrupt's and AnyX86Interrupt's spellings
// must match.
let Spellings = [GCC<"interrupt">];
- let Args = [EnumArgument<"Interrupt", "InterruptType",
+ let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
1>];
@@ -1029,7 +1036,8 @@ def ExternalSourceSymbol : InheritableAttr {
def Blocks : InheritableAttr {
let Spellings = [Clang<"blocks">];
- let Args = [EnumArgument<"Type", "BlockType", ["byref"], ["ByRef"]>];
+ let Args = [EnumArgument<"Type", "BlockType", /*is_string=*/true,
+ ["byref"], ["ByRef"]>];
let Documentation = [Undocumented];
}
@@ -1611,7 +1619,7 @@ def FlagEnum : InheritableAttr {
def EnumExtensibility : InheritableAttr {
let Spellings = [Clang<"enum_extensibility">];
let Subjects = SubjectList<[Enum]>;
- let Args = [EnumArgument<"Extensibility", "Kind",
+ let Args = [EnumArgument<"Extensibility", "Kind", /*is_string=*/false,
["closed", "open"], ["Closed", "Open"]>];
let Documentation = [EnumExtensibilityDocs];
}
@@ -1777,7 +1785,7 @@ def MipsInterrupt : InheritableAttr, TargetSpecificAttr<TargetMips32> {
// must match.
let Spellings = [GCC<"interrupt">];
let Subjects = SubjectList<[Function]>;
- let Args = [EnumArgument<"Interrupt", "InterruptType",
+ let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
["vector=sw0", "vector=sw1", "vector=hw0",
"vector=hw1", "vector=hw2", "vector=hw3",
"vector=hw4", "vector=hw5", "eic", ""],
@@ -1965,7 +1973,7 @@ def NoMicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
def RISCVInterrupt : InheritableAttr, TargetSpecificAttr<TargetRISCV> {
let Spellings = [GCC<"interrupt">];
let Subjects = SubjectList<[Function]>;
- let Args = [EnumArgument<"Interrupt", "InterruptType",
+ let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
["supervisor", "machine"],
["supervisor", "machine"],
1>];
@@ -2336,7 +2344,7 @@ def ObjCException : InheritableAttr {
def ObjCMethodFamily : InheritableAttr {
let Spellings = [Clang<"objc_method_family">];
let Subjects = SubjectList<[ObjCMethod], ErrorDiag>;
- let Args = [EnumArgument<"Family", "FamilyKind",
+ let Args = [EnumArgument<"Family", "FamilyKind", /*is_string=*/false,
["none", "alloc", "copy", "init", "mutableCopy", "new"],
["OMF_None", "OMF_alloc", "OMF_copy", "OMF_init",
"OMF_mutableCopy", "OMF_new"]>];
@@ -2510,7 +2518,7 @@ def IntelOclBicc : DeclOrTypeAttr {
def Pcs : DeclOrTypeAttr {
let Spellings = [GCC<"pcs">];
- let Args = [EnumArgument<"PCS", "PCSType",
+ let Args = [EnumArgument<"PCS", "PCSType", /*is_string=*/true,
["aapcs", "aapcs-vfp"],
["AAPCS", "AAPCS_VFP"]>];
// let Subjects = [Function, ObjCMethod];
@@ -2619,7 +2627,7 @@ def SwiftObjCMembers : Attr {
def SwiftError : InheritableAttr {
let Spellings = [GNU<"swift_error">];
let Args = [
- EnumArgument<"Convention", "ConventionKind",
+ EnumArgument<"Convention", "ConventionKind", /*is_string=*/false,
["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"],
["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>
];
@@ -2635,7 +2643,7 @@ def SwiftName : InheritableAttr {
def SwiftNewType : InheritableAttr {
let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">];
- let Args = [EnumArgument<"NewtypeKind", "NewtypeKind",
+ let Args = [EnumArgument<"NewtypeKind", "NewtypeKind", /*is_string=*/false,
["struct", "enum"], ["NK_Struct", "NK_Enum"]>];
let Subjects = SubjectList<[TypedefName], ErrorDiag>;
let Documentation = [SwiftNewTypeDocs];
@@ -2746,7 +2754,7 @@ def PragmaClangTextSection : InheritableAttr {
def CodeModel : InheritableAttr, TargetSpecificAttr<TargetLoongArch> {
let Spellings = [GCC<"model">];
- let Args = [EnumArgument<"Model", "llvm::CodeModel::Model",
+ let Args = [EnumArgument<"Model", "llvm::CodeModel::Model", /*is_string=*/1,
["normal", "medium", "extreme"], ["Small", "Medium", "Large"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>];
let Subjects = SubjectList<[NonTLSGlobalVar], ErrorDiag>;
@@ -2802,7 +2810,7 @@ def SwiftIndirectResult : ParameterABIAttr {
def SwiftAsync : InheritableAttr {
let Spellings = [Clang<"swift_async">];
let Subjects = SubjectList<[Function, ObjCMethod]>;
- let Args = [EnumArgument<"Kind", "Kind",
+ let Args = [EnumArgument<"Kind", "Kind", /*is_string=*/false,
["none", "swift_private", "not_swift_private"],
["None", "SwiftPrivate", "NotSwiftPrivate"]>,
ParamIdxArgument<"CompletionHandlerIndex", /*opt=*/1>];
@@ -2812,7 +2820,7 @@ def SwiftAsync : InheritableAttr {
def SwiftAsyncError : InheritableAttr {
let Spellings = [Clang<"swift_async_error">];
let Subjects = SubjectList<[Function, ObjCMethod]>;
- let Args = [EnumArgument<"Convention", "ConventionKind",
+ let Args = [EnumArgument<"Convention", "ConventionKind", /*is_string=*/false,
["none", "nonnull_error", "zero_argument", "nonzero_argument"],
["None", "NonNullError", "ZeroArgument", "NonZeroArgument"]>,
UnsignedArgument<"HandlerParamIdx", /*opt=*/1>];
@@ -2850,7 +2858,7 @@ def ZeroCallUsedRegs : InheritableAttr {
let Spellings = [GCC<"zero_call_used_regs">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Args = [
- EnumArgument<"ZeroCallUsedRegs", "ZeroCallUsedRegsKind",
+ EnumArgument<"ZeroCallUsedRegs", "ZeroCallUsedRegsKind", /*is_string=*/true,
["skip", "used-gpr-arg", "used-gpr", "used-arg", "used",
"all-gpr-arg", "all-gpr", "all-arg", "all"],
["Skip", "UsedGPRArg", "UsedGPR", "UsedArg", "Used",
@@ -3016,7 +3024,7 @@ def TransparentUnion : InheritableAttr {
def Unavailable : InheritableAttr {
let Spellings = [Clang<"unavailable">];
let Args = [StringArgument<"Message", 1>,
- EnumArgument<"ImplicitReason", "ImplicitReason",
+ EnumArgument<"ImplicitReason", "ImplicitReason", /*is_string=*/0, // FIXME
["", "", "", ""],
["IR_None",
"IR_ARCForbiddenType",
@@ -3036,8 +3044,8 @@ def DiagnoseIf : InheritableAttr {
let Spellings = [GNU<"diagnose_if">];
let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>;
let Args = [ExprArgument<"Cond">, StringArgument<"Message">,
- EnumArgument<"DiagnosticType",
- "DiagnosticType",
+ EnumArgument<"DiagnosticType", "DiagnosticType",
+ /*is_string=*/true,
["error", "warning"],
["DT_Error", "DT_Warning"]>,
BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
@@ -3139,7 +3147,7 @@ def MatrixType : TypeAttr {
def Visibility : InheritableAttr {
let Clone = 0;
let Spellings = [GCC<"visibility">];
- let Args = [EnumArgument<"Visibility", "VisibilityType",
+ let Args = [EnumArgument<"Visibility", "VisibilityType", /*is_string=*/true,
["default", "hidden", "internal", "protected"],
["Default", "Hidden", "Hidden", "Protected"]>];
let MeaningfulToClassTemplateDefinition = 1;
@@ -3149,7 +3157,7 @@ def Visibility : InheritableAttr {
def TypeVisibility : InheritableAttr {
let Clone = 0;
let Spellings = [Clang<"type_visibility">];
- let Args = [EnumArgument<"Visibility", "VisibilityType",
+ let Args = [EnumArgument<"Visibility", "VisibilityType", /*is_string=*/true,
["default", "hidden", "internal", "protected"],
["Default", "Hidden", "Hidden", "Protected"]>];
// let Subjects = [Tag, ObjCInterface, Namespace];
@@ -3547,7 +3555,7 @@ def Consumable : InheritableAttr {
// FIXME: should this attribute have a CPlusPlus language option?
let Spellings = [Clang<"consumable", 0>];
let Subjects = SubjectList<[CXXRecord]>;
- let Args = [EnumArgument<"DefaultState", "ConsumedState",
+ let Args = [EnumArgument<"DefaultState", "ConsumedState", /*is_string=*/false,
["unknown", "consumed", "unconsumed"],
["Unknown", "Consumed", "Unconsumed"]>];
let Documentation = [ConsumableDocs];
@@ -3580,6 +3588,7 @@ def CallableWhen : InheritableAttr {
let Spellings = [Clang<"callable_when", 0>];
let Subjects = SubjectList<[CXXMethod]>;
let Args = [VariadicEnumArgument<"CallableStates", "ConsumedState",
+ /*is_string=*/true,
["unknown", "consumed", "unconsumed"],
["Unknown", "Consumed", "Unconsumed"]>];
let Documentation = [CallableWhenDocs];
@@ -3591,7 +3600,7 @@ def ParamTypestate : InheritableAttr {
// FIXME: should this attribute have a CPlusPlus language option?
let Spellings = [Clang<"param_typestate", 0>];
let Subjects = SubjectList<[ParmVar]>;
- let Args = [EnumArgument<"ParamState", "ConsumedState",
+ let Args = [EnumArgument<"ParamState", "ConsumedState", /*is_string=*/false,
["unknown", "consumed", "unconsumed"],
["Unknown", "Consumed", "Unconsumed"]>];
let Documentation = [ParamTypestateDocs];
@@ -3603,7 +3612,7 @@ def ReturnTypestate : InheritableAttr {
// FIXME: should this attribute have a CPlusPlus language option?
let Spellings = [Clang<"return_typestate", 0>];
let Subjects = SubjectList<[Function, ParmVar]>;
- let Args = [EnumArgument<"State", "ConsumedState",
+ let Args = [EnumArgument<"State", "ConsumedState", /*is_string=*/false,
["unknown", "consumed", "unconsumed"],
["Unknown", "Consumed", "Unconsumed"]>];
let Documentation = [ReturnTypestateDocs];
@@ -3615,7 +3624,7 @@ def SetTypestate : InheritableAttr {
// FIXME: should this attribute have a CPlusPlus language option?
let Spellings = [Clang<"set_typestate", 0>];
let Subjects = SubjectList<[CXXMethod]>;
- let Args = [EnumArgument<"NewState", "ConsumedState",
+ let Args = [EnumArgument<"NewState", "ConsumedState", /*is_string=*/false,
["unknown", "consumed", "unconsumed"],
["Unknown", "Consumed", "Unconsumed"]>];
let Documentation = [SetTypestateDocs];
@@ -3627,7 +3636,7 @@ def TestTypestate : InheritableAttr {
// FIXME: should this attribute have a CPlusPlus language option?
let Spellings = [Clang<"test_typestate", 0>];
let Subjects = SubjectList<[CXXMethod]>;
- let Args = [EnumArgument<"TestState", "ConsumedState",
+ let Args = [EnumArgument<"TestState", "ConsumedState", /*is_string=*/false,
["consumed", "unconsumed"],
["Consumed", "Unconsumed"]>];
let Documentation = [TestTypestateDocs];
@@ -3704,7 +3713,8 @@ def CFGuard : InheritableAttr, TargetSpecificAttr<TargetWindows> {
// we might also want to support __declspec(guard(suppress)).
let Spellings = [Declspec<"guard">, Clang<"guard">];
let Subjects = SubjectList<[Function]>;
- let Args = [EnumArgument<"Guard", "GuardArg", ["nocf"], ["nocf"]>];
+ let Args = [EnumArgument<"Guard", "GuardArg", /*is_string=*/false,
+ ["nocf"], ["nocf"]>];
let Documentation = [CFGuardDocs];
}
@@ -3860,7 +3870,7 @@ def LoopHint : Attr {
Pragma<"", "nounroll_and_jam">];
/// State of the loop optimization specified by the spelling.
- let Args = [EnumArgument<"Option", "OptionType",
+ let Args = [EnumArgument<"Option", "OptionType", /*is_string=*/false,
["vectorize", "vectorize_width", "interleave", "interleave_count",
"unroll", "unroll_count", "unroll_and_jam", "unroll_and_jam_count",
"pipeline", "pipeline_initiation_interval", "distribute",
@@ -3869,7 +3879,7 @@ def LoopHint : Attr {
"Unroll", "UnrollCount", "UnrollAndJam", "UnrollAndJamCount",
"PipelineDisabled", "PipelineInitiationInterval", "Distribute",
"VectorizePredicate"]>,
- EnumArgument<"State", "LoopHintState",
+ EnumArgument<"State", "LoopHintState", /*is_string=*/false,
["enable", "disable", "numeric", "fixed_width",
"scalable_width", "assume_safety", "full"],
["Enable", "Disable", "Numeric", "FixedWidth",
@@ -3958,7 +3968,7 @@ def OMPDeclareSimdDecl : Attr {
let HasCustomParsing = 1;
let Documentation = [OMPDeclareSimdDocs];
let Args = [
- EnumArgument<"BranchState", "BranchStateTy",
+ EnumArgument<"BranchState", "BranchStateTy", /*is_string=*/false,
[ "", "inbranch", "notinbranch" ],
[ "BS_Undefined", "BS_Inbranch", "BS_Notinbranch" ]>,
ExprArgument<"Simdlen">, VariadicExprArgument<"Uniforms">,
@@ -3978,10 +3988,10 @@ def OMPDeclareTargetDecl : InheritableAttr {
let Subjects = SubjectList<[Function, SharedVar]>;
let Documentation = [OMPDeclareTargetDocs];
let Args = [
- EnumArgument<"MapType", "MapTypeTy",
+ EnumArgument<"MapType", "MapTypeTy", /*is_string=*/false,
[ "to", "enter", "link" ],
[ "MT_To", "MT_Enter", "MT_Link" ]>,
- EnumArgument<"DevType", "DevTypeTy",
+ EnumArgument<"DevType", "DevTypeTy", /*is_string=*/false,
[ "host", "nohost", "any" ],
[ "DT_Host", "DT_NoHost", "DT_Any" ]>,
ExprArgument<"IndirectExpr">,
@@ -4003,7 +4013,7 @@ def OMPAllocateDecl : InheritableAttr {
let Spellings = [];
let SemaHandler = 0;
let Args = [
- EnumArgument<"AllocatorType", "AllocatorTypeTy",
+ EnumArgument<"AllocatorType", "AllocatorTypeTy", /*is_string=*/false,
[
"omp_null_allocator", "omp_default_mem_alloc",
"omp_large_cap_mem_alloc", "omp_const_mem_alloc",
@@ -4252,7 +4262,7 @@ def HLSLShader : InheritableAttr {
let Subjects = SubjectList<[HLSLEntry]>;
let LangOpts = [HLSL];
let Args = [
- EnumArgument<"Type", "ShaderType",
+ EnumArgument<"Type", "ShaderType", /*is_string=*/true,
["pixel", "vertex", "geometry", "hull", "domain", "compute",
"raygeneration", "intersection", "anyhit", "closesthit",
"miss", "callable", "mesh", "amplification"],
@@ -4268,10 +4278,12 @@ def HLSLResource : InheritableAttr {
let Subjects = SubjectList<[Struct]>;
let LangOpts = [HLSL];
let Args = [EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass",
+ /*is_string=*/0,
["SRV", "UAV", "CBuffer", "Sampler"],
["SRV", "UAV", "CBuffer", "Sampler"],
/*opt=*/0, /*fake=*/0, /*isExternalType=*/1>,
EnumArgument<"ResourceKind", "llvm::hlsl::ResourceKind",
+ /*is_string=*/0,
["Texture1D", "Texture2D", "Texture2DMS",
"Texture3D", "TextureCube", "Texture1DArray",
"Texture2DArray", "Texture2DMSArray",
@@ -4328,7 +4340,7 @@ def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
def FunctionReturnThunks : InheritableAttr,
TargetSpecificAttr<TargetAnyX86> {
let Spellings = [GCC<"function_return">];
- let Args = [EnumArgument<"ThunkType", "Kind",
+ let Args = [EnumArgument<"ThunkType", "Kind", /*is_string=*/true,
["keep", "thunk-extern"],
["Keep", "Extern"]
>];
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ed684c5d57b1ee..e069e4350ff70e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -291,7 +291,7 @@ static bool attributeHasIdentifierArg(const IdentifierInfo &II) {
/// Determine whether the given attribute has an identifier argument.
static ParsedAttributeArgumentsProperties
-attributeStringLiteralListArg(const IdentifierInfo &II) {
+attributeStringLiteralListArg(const llvm::Triple &T, const IdentifierInfo &II) {
#define CLANG_ATTR_STRING_LITERAL_ARG_LIST
return llvm::StringSwitch<uint32_t>(normalizeAttrName(II.getName()))
#include "clang/Parse/AttrParserStringSwitches.inc"
@@ -550,7 +550,7 @@ unsigned Parser::ParseAttributeArgsCommon(
ExprVector ParsedExprs;
ParsedAttributeArgumentsProperties ArgProperties =
- attributeStringLiteralListArg(*AttrName);
+ attributeStringLiteralListArg(getTargetInfo().getTriple(), *AttrName);
if (ParseAttributeArgumentList(*AttrName, ParsedExprs, ArgProperties)) {
SkipUntil(tok::r_paren, StopAtSemi);
return 0;
diff --git a/clang/test/Sema/attr-function-return.c b/clang/test/Sema/attr-function-return.c
index c6fe88b821e35f..d2c9156da7ab61 100644
--- a/clang/test/Sema/attr-function-return.c
+++ b/clang/test/Sema/attr-function-return.c
@@ -13,7 +13,7 @@ __attribute__((function_return("thunk-extern"))) void w(void) {}
// expected-warning@+1 {{'function_return' attribute argument not supported: invalid}}
__attribute__((function_return("invalid"))) void v(void) {}
-// expected-error@+1 {{'function_return' attribute requires a string}}
+// expected-error@+1 {{expected string literal as argument of 'function_return' attribute}}
__attribute__((function_return(5))) void a(void) {}
// expected-error@+1 {{'function_return' attribute takes one argument}}
diff --git a/clang/test/Sema/callingconv-iamcu.c b/clang/test/Sema/callingconv-iamcu.c
index 2874a8164545aa..d6b8f8f011d0f3 100644
--- a/clang/test/Sema/callingconv-iamcu.c
+++ b/clang/test/Sema/callingconv-...
[truncated]
|
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.