diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 5f024b4b5fd78..cdf9dcaff7508 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -191,6 +191,12 @@ class AttributeCommonInfo { /// __gnu__::__attr__ will be normalized to gnu::attr). std::string getNormalizedFullName() const; + /// Generate a normalized full name, with syntax, scope and name. + static std::string + normalizeFullNameWithSyntax(const IdentifierInfo *Name, + const IdentifierInfo *Scope, + AttributeCommonInfo::Syntax SyntaxUsed); + bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; } diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index 867d241a2cf84..a39eb85f7e8fa 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -153,6 +153,40 @@ std::string AttributeCommonInfo::getNormalizedFullName() const { normalizeName(getAttrName(), getScopeName(), getSyntax())); } +static StringRef getSyntaxName(AttributeCommonInfo::Syntax SyntaxUsed) { + switch (SyntaxUsed) { + case AttributeCommonInfo::AS_GNU: + return "GNU"; + case AttributeCommonInfo::AS_CXX11: + return "CXX11"; + case AttributeCommonInfo::AS_C23: + return "C23"; + case AttributeCommonInfo::AS_Declspec: + return "Declspec"; + case AttributeCommonInfo::AS_Microsoft: + return "Microsoft"; + case AttributeCommonInfo::AS_Keyword: + return "Keyword"; + case AttributeCommonInfo::AS_Pragma: + return "Pragma"; + case AttributeCommonInfo::AS_ContextSensitiveKeyword: + return "ContextSensitiveKeyword"; + case AttributeCommonInfo::AS_HLSLAnnotation: + return "HLSLAnnotation"; + case AttributeCommonInfo::AS_Implicit: + return "Implicit"; + } + llvm_unreachable("Invalid attribute syntax"); +} + +std::string AttributeCommonInfo::normalizeFullNameWithSyntax( + const IdentifierInfo *Name, const IdentifierInfo *ScopeName, + Syntax SyntaxUsed) { + return (Twine(getSyntaxName(SyntaxUsed)) + + "::" + normalizeName(Name, ScopeName, SyntaxUsed)) + .str(); +} + unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const { // Both variables will be used in tablegen generated // attribute spell list index matching code. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7ce9a9cea1c7a..2c3ea3271a761 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -314,64 +314,92 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs, } /// Determine whether the given attribute has an identifier argument. -static bool attributeHasIdentifierArg(const IdentifierInfo &II) { +static bool attributeHasIdentifierArg(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_IDENTIFIER_ARG_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_IDENTIFIER_ARG_LIST } /// Determine whether the given attribute has an identifier argument. static ParsedAttributeArgumentsProperties -attributeStringLiteralListArg(const llvm::Triple &T, const IdentifierInfo &II) { +attributeStringLiteralListArg(const llvm::Triple &T, const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_STRING_LITERAL_ARG_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" .Default(0); #undef CLANG_ATTR_STRING_LITERAL_ARG_LIST } /// Determine whether the given attribute has a variadic identifier argument. -static bool attributeHasVariadicIdentifierArg(const IdentifierInfo &II) { +static bool attributeHasVariadicIdentifierArg(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST } /// Determine whether the given attribute treats kw_this as an identifier. -static bool attributeTreatsKeywordThisAsIdentifier(const IdentifierInfo &II) { +static bool attributeTreatsKeywordThisAsIdentifier(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST } /// Determine if an attribute accepts parameter packs. -static bool attributeAcceptsExprPack(const IdentifierInfo &II) { +static bool attributeAcceptsExprPack(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_ACCEPTS_EXPR_PACK - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" .Default(false); #undef CLANG_ATTR_ACCEPTS_EXPR_PACK } /// Determine whether the given attribute parses a type argument. -static bool attributeIsTypeArgAttr(const IdentifierInfo &II) { +static bool attributeIsTypeArgAttr(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_TYPE_ARG_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_TYPE_ARG_LIST } /// Determine whether the given attribute takes identifier arguments. -static bool attributeHasStrictIdentifierArgs(const IdentifierInfo &II) { +static bool attributeHasStrictIdentifierArgs(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST - return (llvm::StringSwitch(normalizeAttrName(II.getName())) + return (llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" .Default(0)) != 0; #undef CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST @@ -380,9 +408,13 @@ static bool attributeHasStrictIdentifierArgs(const IdentifierInfo &II) { /// Determine whether the given attribute takes an identifier argument at a /// specific index static bool attributeHasStrictIdentifierArgAtIndex(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName, size_t argIndex) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST - return (llvm::StringSwitch(normalizeAttrName(II.getName())) + return (llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" .Default(0)) & (1ull << argIndex); @@ -391,11 +423,15 @@ static bool attributeHasStrictIdentifierArgAtIndex(const IdentifierInfo &II, /// Determine whether the given attribute requires parsing its arguments /// in an unevaluated context or not. -static bool attributeParsedArgsUnevaluated(const IdentifierInfo &II) { +static bool attributeParsedArgsUnevaluated(const IdentifierInfo &II, + ParsedAttr::Syntax Syntax, + IdentifierInfo *ScopeName) { + std::string FullName = + AttributeCommonInfo::normalizeFullNameWithSyntax(&II, ScopeName, Syntax); #define CLANG_ATTR_ARG_CONTEXT_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(FullName) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_ARG_CONTEXT_LIST } @@ -523,10 +559,12 @@ unsigned Parser::ParseAttributeArgsCommon( // Ignore the left paren location for now. ConsumeParen(); - bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName); - bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName); + bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier( + *AttrName, Form.getSyntax(), ScopeName); + bool AttributeIsTypeArgAttr = + attributeIsTypeArgAttr(*AttrName, Form.getSyntax(), ScopeName); bool AttributeHasVariadicIdentifierArg = - attributeHasVariadicIdentifierArg(*AttrName); + attributeHasVariadicIdentifierArg(*AttrName, Form.getSyntax(), ScopeName); // Interpret "kw_this" as an identifier if the attributed requests it. if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) @@ -535,8 +573,9 @@ unsigned Parser::ParseAttributeArgsCommon( ArgsVector ArgExprs; if (Tok.is(tok::identifier)) { // If this attribute wants an 'identifier' argument, make it so. - bool IsIdentifierArg = AttributeHasVariadicIdentifierArg || - attributeHasIdentifierArg(*AttrName); + bool IsIdentifierArg = + AttributeHasVariadicIdentifierArg || + attributeHasIdentifierArg(*AttrName, Form.getSyntax(), ScopeName); ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(AttrName, ScopeName, Form.getSyntax()); @@ -568,7 +607,8 @@ unsigned Parser::ParseAttributeArgsCommon( if (T.isUsable()) TheParsedType = T.get(); } else if (AttributeHasVariadicIdentifierArg || - attributeHasStrictIdentifierArgs(*AttrName)) { + attributeHasStrictIdentifierArgs(*AttrName, Form.getSyntax(), + ScopeName)) { // Parse variadic identifier arg. This can either consume identifiers or // expressions. Variadic identifier args do not support parameter packs // because those are typically used for attributes with enumeration @@ -579,8 +619,9 @@ unsigned Parser::ParseAttributeArgsCommon( if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) Tok.setKind(tok::identifier); - if (Tok.is(tok::identifier) && attributeHasStrictIdentifierArgAtIndex( - *AttrName, ArgExprs.size())) { + if (Tok.is(tok::identifier) && + attributeHasStrictIdentifierArgAtIndex( + *AttrName, Form.getSyntax(), ScopeName, ArgExprs.size())) { ArgExprs.push_back(ParseIdentifierLoc()); continue; } @@ -589,7 +630,8 @@ unsigned Parser::ParseAttributeArgsCommon( if (Tok.is(tok::identifier)) { ArgExprs.push_back(ParseIdentifierLoc()); } else { - bool Uneval = attributeParsedArgsUnevaluated(*AttrName); + bool Uneval = attributeParsedArgsUnevaluated( + *AttrName, Form.getSyntax(), ScopeName); EnterExpressionEvaluationContext Unevaluated( Actions, Uneval ? Sema::ExpressionEvaluationContext::Unevaluated @@ -610,7 +652,8 @@ unsigned Parser::ParseAttributeArgsCommon( } while (TryConsumeToken(tok::comma)); } else { // General case. Parse all available expressions. - bool Uneval = attributeParsedArgsUnevaluated(*AttrName); + bool Uneval = attributeParsedArgsUnevaluated(*AttrName, Form.getSyntax(), + ScopeName); EnterExpressionEvaluationContext Unevaluated( Actions, Uneval ? Sema::ExpressionEvaluationContext::Unevaluated @@ -621,7 +664,8 @@ unsigned Parser::ParseAttributeArgsCommon( ExprVector ParsedExprs; ParsedAttributeArgumentsProperties ArgProperties = - attributeStringLiteralListArg(getTargetInfo().getTriple(), *AttrName); + attributeStringLiteralListArg(getTargetInfo().getTriple(), *AttrName, + Form.getSyntax(), ScopeName); if (ParseAttributeArgumentList(*AttrName, ParsedExprs, ArgProperties)) { SkipUntil(tok::r_paren, StopAtSemi); return 0; @@ -632,7 +676,7 @@ unsigned Parser::ParseAttributeArgsCommon( if (!isa(ParsedExprs[I])) continue; - if (!attributeAcceptsExprPack(*AttrName)) { + if (!attributeAcceptsExprPack(*AttrName, Form.getSyntax(), ScopeName)) { Diag(Tok.getLocation(), diag::err_attribute_argument_parm_pack_not_supported) << AttrName; @@ -696,7 +740,7 @@ void Parser::ParseGNUAttributeArgs( ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Form); return; - } else if (attributeIsTypeArgAttr(*AttrName)) { + } else if (attributeIsTypeArgAttr(*AttrName, Form.getSyntax(), ScopeName)) { ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, Form); return; diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 7f452d177c16f..bc15504e87487 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -594,13 +594,6 @@ static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A, unsigned UnrollFactor = 0; if (A.getNumArgs() == 1) { - - if (A.isArgIdent(0)) { - S.Diag(A.getLoc(), diag::err_attribute_argument_type) - << A << AANT_ArgumentIntegerConstant << A.getRange(); - return nullptr; - } - Expr *E = A.getArgAsExpr(0); if (S.CheckLoopHintExpr(E, St->getBeginLoc(), diff --git a/clang/test/SemaHLSL/Loops/unroll.hlsl b/clang/test/SemaHLSL/Loops/unroll.hlsl index 2e2be319e4666..c94dc5857c2e8 100644 --- a/clang/test/SemaHLSL/Loops/unroll.hlsl +++ b/clang/test/SemaHLSL/Loops/unroll.hlsl @@ -1,7 +1,10 @@ // RUN: %clang_cc1 -O0 -finclude-default-header -fsyntax-only -triple dxil-pc-shadermodel6.6-library %s -verify void unroll_no_vars() { + // expected-note@+1 {{declared here}} int I = 3; - [unroll(I)] // expected-error {{'unroll' attribute requires an integer constant}} + // expected-error@+2 {{expression is not an integral constant expression}} + // expected-note@+1 {{read of non-const variable 'I' is not allowed in a constant expression}} + [unroll(I)] while (I--); } diff --git a/clang/test/TableGen/attrs-parser-string-switches.td b/clang/test/TableGen/attrs-parser-string-switches.td new file mode 100644 index 0000000000000..c15ab104e0ccd --- /dev/null +++ b/clang/test/TableGen/attrs-parser-string-switches.td @@ -0,0 +1,232 @@ +// RUN: clang-tblgen -gen-clang-attr-parser-string-switches -I%p/../../include %s -o - 2>&1 | FileCheck %s + +// Tests that the tablegen can support attributes with the same spellings but +// different argument types. + +include "clang/Basic/Attr.td" + +// Test attributeParsedArgsUnevaluated : different ParseArgumentsAsUnevaluated +def TestUnEvalOne : InheritableAttr { + let Spellings = [Clang<"test_uneval">]; + let Args = [ExprArgument<"Count">]; + let Subjects = SubjectList<[Function]>; + let ParseArgumentsAsUnevaluated = 1; + let Documentation = [Undocumented]; +} + +def TestUnEvalTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_uneval">]; + let Args = [ExprArgument<"Count">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_ARG_CONTEXT_LIST) +// CHECK-NOT: .Case("Pragma::test_uneval", true) +// CHECK: .Case("GNU::test_uneval", true) +// CHECK-NOT: .Case("Pragma::test_uneval", true) +// CHECK: .Case("CXX11::clang::test_uneval", true) +// CHECK-NOT: .Case("Pragma::test_uneval", true) +// CHECK: .Case("C23::clang::test_uneval", true) +// CHECK-NOT: .Case("Pragma::test_uneval", true) +// CHECK: #endif // CLANG_ATTR_ARG_CONTEXT_LIST + +// Test attributeHasIdentifierArg: Same spelling, one with and one without +// an IdentifierArg. +def TestIdentOne : Attr { + let Spellings = [Clang<"test_ident">]; + let Args = [EnumArgument<"Option", "OptionType", /*is_string=*/false, + ["optA", "optB"], ["OPTA", "OPTB"]>]; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + +def TestIdentTwo : StmtAttr { + let Spellings = [Pragma<"", "test_ident">]; + let Args = [UnsignedArgument<"val", /*opt*/1>]; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST) +// CHECK-NOT: .Case("Pragma::test_ident", true) +// CHECK: .Case("GNU::test_ident", true) +// CHECK-NOT: .Case("Pragma::test_ident", true) +// CHECK: .Case("CXX11::clang::test_ident", true) +// CHECK-NOT: .Case("Pragma::test_ident", true) +// CHECK: .Case("C23::clang::test_ident", true) +// CHECK-NOT: .Case("Pragma::test_ident", true) +// CHECK: #endif // CLANG_ATTR_IDENTIFIER_ARG_LIST + +// Test attributeStringLiteralListArg : Same spelling, some with a +// StringArgument, some without, some in different locations. +def TestStringOne : DeclOrTypeAttr { + let Spellings = [Clang<"test_string">]; + let Args = [StringArgument<"strarg">]; + let Subjects = SubjectList<[Function, TypedefName, ParmVar]>; + let Documentation = [AcquireHandleDocs]; +} + +def TestStringTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_string">]; + let Args = [UnsignedArgument<"unsarg">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// In a different position +def TestStringThree : Attr { + let Spellings = [Declspec<"test_string">]; + let Args = [UnsignedArgument<"uarg">, StringArgument<"strarg">]; + let Subjects = SubjectList<[Function, TypedefName, ParmVar]>; + let Documentation = [AcquireHandleDocs]; +} + +// CHECK: #if defined(CLANG_ATTR_STRING_LITERAL_ARG_LIST) +// CHECK-NOT: .Case("Pragma::test_string" +// CHECK: .Case("GNU::test_string", 1) +// CHECK: .Case("CXX11::clang::test_string", 1) +// CHECK: .Case("C23::clang::test_string", 1) +// CHECK-NOT: .Case("Pragma::test_string" +// CHECK: .Case("Declspec::test_string", 2) +// CHECK-NOT: .Case("Pragma::test_string" +// CHECK: #endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST + +// Test attributeHasVariadicIdentifierArg : One with VariadicIdentifierArgument +// and one without. +def TestVariadicIdentOne : InheritableAttr { + let Spellings = [Clang<"test_var_ident">]; + let Args = [VariadicIdentifierArgument<"iargs">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def TestVariadicIdentTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_var_ident">]; + let Args = [UnsignedArgument<"Hint">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST) +// CHECK-NOT: .Case("Pragma::"test_var_ident", true) +// CHECK: .Case("GNU::test_var_ident", true) +// CHECK-NOT: .Case("Pragma::test_var_ident", true) +// CHECK: .Case("CXX11::clang::test_var_ident", true) +// CHECK-NOT: .Case("Pragma::test_var_ident", true) +// CHECK: .Case("C23::clang::test_var_ident", true) +// CHECK-NOT: .Case("Pragma::test_var_ident", true) +// CHECK: #endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST + +// Test attributeTreatsKeywordThisAsIdentifier : Same spelling, one with and +// one without VariadicParamOrParamIdxArgument. +def TestVarOrIdxOne : InheritableAttr { + let Spellings = [Clang<"test_var_idx">]; + let Args = [VariadicParamOrParamIdxArgument<"arg">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def TestVarOrIdxTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_var_idx">]; + let Args = [UnsignedArgument<"Hint">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST) +// CHECK-NOT: .Case("Pragma::test_var_idx", true) +// CHECK: .Case("GNU::test_var_idx", true) +// CHECK-NOT: .Case("Pragma::test_var_idx", true) +// CHECK: .Case("CXX11::clang::test_var_idx", true) +// CHECK-NOT: .Case("Pragma::test_var_idx", true) +// CHECK: .Case("C23::clang::test_var_idx", true) +// CHECK-NOT: .Case("Pragma::test_var_idx", true) +// CHECK: #endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST + +// Test attributeAcceptsExprPack : One with, one without. +def TestExprPackOne : InheritableAttr { + let Spellings = [Clang<"test_expr_pack">]; + let Args = [StringArgument<"str">, VariadicExprArgument<"args">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let AcceptsExprPack = 1; + let Documentation = [Undocumented]; +} + +def TestExprPackTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_expr_pack">]; + let Args = [StringArgument<"str">, VariadicExprArgument<"args">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK) +// CHECK-NOT: .Case("Pragma::test_expr_pack", true) +// CHECK: .Case("GNU::test_expr_pack", true) +// CHECK-NOT: .Case("Pragma::test_expr_pack", true) +// CHECK: .Case("CXX11::clang::test_expr_pack", true) +// CHECK-NOT: .Case("Pragma::test_expr_pack", true) +// CHECK: .Case("C23::clang::test_expr_pack", true) +// CHECK-NOT: .Case("Pragma::test_expr_pack", true) +// CHECK: #endif // CLANG_ATTR_ACCEPTS_EXPR_PACK + + +// Test attributeIsTypeArgAttr : Same spelling, one with TypeArgument and one +// without. +def TestTypeOne : InheritableAttr { + let Spellings = [Clang<"test_type">]; + let Args = [TypeArgument<"Hint">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def TestTypeTwo : InheritableAttr { + let Spellings = [Pragma<"", "test_type">]; + let Args = [UnsignedArgument<"Hint">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_TYPE_ARG_LIST) +// CHECK-NOT: .Case("Pragma::test_type", true) +// CHECK: .Case("GNU::test_type", true) +// CHECK-NOT: .Case("Pragma::test_type", true) +// CHECK: .Case("CXX11::clang::test_type", true) +// CHECK-NOT: .Case("Pragma::test_type", true) +// CHECK: .Case("C23::clang::test_type", true) +// CHECK-NOT: .Case("Pragma::test_type", true) +// CHECK: #endif // CLANG_ATTR_TYPE_ARG_LIST + +// Test attributeHasStrictIdentifierArgs and +// attributeHasStrictIdentifierArgAtIndex, one used StrictEnumParameters, the +// other does not. +def TestStrictEnumOne : InheritableAttr { + let Spellings = [Clang<"strict_enum">]; + let StrictEnumParameters = 1; + let Args = [EnumArgument<"One", "OneType", /*is_string=*/true, + ["a", "b", "c", "d"], + ["A", "B", "C", "D"]>, + IntArgument<"Other", 1>, + EnumArgument<"Two", "TwoType", /*is_string=*/true, + ["e", "f", "g", "h"], + ["E", "F", "G", "H"]>]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def TestStrictEnumTwo : InheritableAttr { + let Spellings = [Pragma<"", "strict_enum">]; + let Args = [VariadicExprArgument<"Args">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +// CHECK: #if defined(CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST) +// CHECK-NOT: .Case("Pragma::strict_enum", 5ull) +// CHECK: .Case("GNU::strict_enum", 5ull) +// CHECK-NOT: .Case("Pragma::strict_enum", 5ull) +// CHECK: .Case("CXX11::clang::strict_enum", 5ull) +// CHECK-NOT: .Case("Pragma::strict_enum", 5ull) +// CHECK: .Case("C23::clang::strict_enum", 5ull) +// CHECK-NOT: .Case("Pragma::strict_enum", 5ull) +// CHECK: #endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 1f83440680330..f504b1de144b7 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2372,13 +2372,10 @@ void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { OS << "}\n\n"; } -template -static void forEachUniqueSpelling(const Record &Attr, Fn &&F) { +template static void forEachSpelling(const Record &Attr, Fn &&F) { std::vector Spellings = GetFlattenedSpellings(Attr); - SmallDenseSet Seen; for (const FlattenedSpelling &S : Spellings) { - if (Seen.insert(S.name()).second) - F(S); + F(S); } } @@ -2402,8 +2399,11 @@ static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) { continue; // All these spellings take a single type argument. - forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + forEachSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_TYPE_ARG_LIST\n\n"; @@ -2421,8 +2421,11 @@ static void emitClangAttrArgContextList(RecordKeeper &Records, raw_ostream &OS) continue; // All these spellings take are parsed unevaluated. - forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + forEachSpelling(Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n"; @@ -2483,10 +2486,11 @@ static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, continue; // All these spellings take an identifier argument. - forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " - << "true" - << ")\n"; + forEachSpelling(*A, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n"; @@ -2552,8 +2556,11 @@ static void emitClangAttrUnevaluatedStringLiteralList(RecordKeeper &Records, continue; // All these spellings have at least one string literal has argument. - forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " << MaskStr << ")\n"; + forEachSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", " << MaskStr << ")\n"; }); } OS << "#endif // CLANG_ATTR_STRING_LITERAL_ARG_LIST\n\n"; @@ -2571,8 +2578,11 @@ static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &O continue; // All these spellings take an identifier argument. - forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n"; + forEachSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n"; @@ -2587,18 +2597,20 @@ static void emitClangAttrStrictIdentifierArgAtIndexList(RecordKeeper &Records, for (const auto *Attr : Attrs) { if (!Attr->getValueAsBit("StrictEnumParameters")) continue; - // Determine whether the first argument is an identifier. + // Determine whether each argument is an identifier. std::vector Args = Attr->getValueAsListOfDefs("Args"); uint64_t enumAtIndex = 0; - for (size_t i = 0; i < Args.size(); i++) { - enumAtIndex |= ((uint64_t)isIdentifierArgument(Args[0])) << i; - } + for (size_t I = 0; I < Args.size(); I++) + enumAtIndex |= ((uint64_t)isIdentifierArgument(Args[I])) << I; if (!enumAtIndex) continue; // All these spellings take an identifier argument. - forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " << enumAtIndex << "ull)\n"; + forEachSpelling(*Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", " << enumAtIndex << "ull)\n"; }); } OS << "#endif // CLANG_ATTR_STRICT_IDENTIFIER_ARG_AT_INDEX_LIST\n\n"; @@ -2623,10 +2635,11 @@ static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records, continue; // All these spellings take an identifier argument. - forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", " - << "true" - << ")\n"; + forEachSpelling(*A, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; @@ -2642,8 +2655,11 @@ static void emitClangAttrAcceptsExprPack(RecordKeeper &Records, if (!Attr.getValueAsBit("AcceptsExprPack")) continue; - forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) { - OS << ".Case(\"" << S.name() << "\", true)\n"; + forEachSpelling(Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.variety(); + if (S.nameSpace().length()) + OS << "::" << S.nameSpace(); + OS << "::" << S.name() << "\", true)\n"; }); } OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n";