[PATCH] Generalized attribute support
Alp Toker
alp at nuanti.com
Tue Jan 14 17:23:39 PST 2014
This patch generalizes C++11 attributes for use in C and C-like
dialects, and additionally enables the new syntax as an extension to C11.
All features are carried forward from C++11, including usage on
declarations, attributed statements, scoped attribute names, GNU
attribute aliases and the clang-specific attribute namespace.
A new feature detection macro is provided, breaking from the usual c/cxx
prefix convention in order to facilitate portable detection in C++ and C
modes:
__has_feature(attributes) - 1 in C++11, otherwise 0.
__has_extension(attributes) - 1 in C++11 and C11, otherwise 0.
The new warning flag -W(no-)generalized-attributes suppresses the new
extension warning in C. The same flag can also be used to selectively
disable attribute compatibility warnings produced by the pre-existing
-Wc++98-compat option.
Newly added tests have been shared with C++11 where possible to ensure
consistency between language modes.
Alp.
--
http://www.nuanti.com
the browser experts
-------------- next part --------------
diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst
index 682cc98..6312d11 100644
--- a/docs/LanguageExtensions.rst
+++ b/docs/LanguageExtensions.rst
@@ -925,6 +925,13 @@ C11 ``_Thread_local``
Use ``__has_feature(c_thread_local)`` or ``__has_extension(c_thread_local)``
to determine if support for ``_Thread_local`` variables is enabled.
+Generalized attributes
+^^^^^^^^^^^^^^^^^^^^^^
+
+Use ``__has_feature(attributes)`` or ``__has_extension(attributes)`` to
+determine support for parsing and handling attributes with the C++11-style
+square bracket notation. Available as an extension in C.
+
Checks for Type Traits
======================
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index bf77259..edafbc7 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -115,6 +115,10 @@ def CXXPre1yCompat : DiagGroup<"c++98-c++11-compat">;
def CXXPre1yCompatPedantic : DiagGroup<"c++98-c++11-compat-pedantic",
[CXXPre1yCompat]>;
+// A group for warnings about use of C++11-style generalized attributes as an
+// extension in C and earlier C++ versions.
+def CXXAttributes : DiagGroup<"generalized-attributes">;
+
def CXX98CompatBindToTemporaryCopy :
DiagGroup<"c++98-compat-bind-to-temporary-copy">;
def CXX98CompatLocalTypeTemplateArgs :
@@ -126,6 +130,7 @@ def CXX98Compat : DiagGroup<"c++98-compat",
[CXX98CompatBindToTemporaryCopy,
CXX98CompatLocalTypeTemplateArgs,
CXX98CompatUnnamedTypeTemplateArgs,
+ CXXAttributes,
CXXPre1yCompat]>;
// Warnings for C++11 features which are Extensions in C++98 mode.
def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic",
@@ -553,7 +558,7 @@ def NonGCC : DiagGroup<"non-gcc",
// A warning group for warnings about using C++11 features as extensions in
// earlier C++ versions.
-def CXX11 : DiagGroup<"c++11-extensions", [CXX11ExtraSemi, CXX11LongLong]>;
+def CXX11 : DiagGroup<"c++11-extensions", [CXX11ExtraSemi, CXX11LongLong, CXXAttributes]>;
// A warning group for warnings about using C++1y features as extensions in
// earlier C++ versions.
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 79e1aeb..a8ae884 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -518,7 +518,9 @@ def warn_cxx98_compat_alignas : Warning<"'alignas' is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def warn_cxx98_compat_attribute : Warning<
"generalized attributes are incompatible with C++98">,
- InGroup<CXX98Compat>, DefaultIgnore;
+ InGroup<CXXAttributes>, DefaultIgnore;
+def ext_cxx11_attribute_in_c : ExtWarn<
+ "generalized attributes are a non-standard C extension">, InGroup<CXXAttributes>;
def err_cxx11_attribute_forbids_arguments : Error<
"attribute '%0' cannot have an argument list">;
def err_cxx11_attribute_forbids_ellipsis : Error<
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index 34dfea2..b5d63b8 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -62,6 +62,7 @@ LANGOPT(Trigraphs , 1, 0,"trigraphs")
LANGOPT(LineComment , 1, 0, "'//' comments")
LANGOPT(Bool , 1, 0, "bool, true, and false keywords")
LANGOPT(WChar , 1, CPlusPlus, "wchar_t keyword")
+LANGOPT(CXXAttributes , 1, 0, "generalized attributes")
BENIGN_LANGOPT(DollarIdents , 1, 1, "'$' in identifiers")
BENIGN_LANGOPT(AsmPreprocessor, 1, 0, "preprocessor in asm mode")
BENIGN_LANGOPT(GNUMode , 1, 1, "GNU extensions")
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 9d0d1b6..04f318e 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1930,14 +1930,14 @@ private:
// an attribute is not allowed.
bool CheckProhibitedCXX11Attribute() {
assert(Tok.is(tok::l_square));
- if (!getLangOpts().CPlusPlus11 || NextToken().isNot(tok::l_square))
+ if (!getLangOpts().CXXAttributes || NextToken().isNot(tok::l_square))
return false;
return DiagnoseProhibitedCXX11Attribute();
}
bool DiagnoseProhibitedCXX11Attribute();
void CheckMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
SourceLocation CorrectLocation) {
- if (!getLangOpts().CPlusPlus11)
+ if (!getLangOpts().CXXAttributes)
return;
if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
Tok.isNot(tok::kw_alignas))
@@ -1993,7 +1993,7 @@ private:
IdentifierLoc *ParseIdentifierLoc();
void MaybeParseCXX11Attributes(Declarator &D) {
- if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+ if (getLangOpts().CXXAttributes && isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrs(AttrFactory);
SourceLocation endLoc;
ParseCXX11Attributes(attrs, &endLoc);
@@ -2002,7 +2002,7 @@ private:
}
void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
SourceLocation *endLoc = 0) {
- if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+ if (getLangOpts().CXXAttributes && isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrsWithRange(AttrFactory);
ParseCXX11Attributes(attrsWithRange, endLoc);
attrs.takeAllFrom(attrsWithRange);
@@ -2011,7 +2011,7 @@ private:
void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
SourceLocation *endLoc = 0,
bool OuterMightBeMessageSend = false) {
- if (getLangOpts().CPlusPlus11 &&
+ if (getLangOpts().CXXAttributes &&
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
ParseCXX11Attributes(attrs, endLoc);
}
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index bb20ae7..987225f 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1106,6 +1106,9 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
// C++ has wchar_t keyword.
Opts.WChar = Opts.CPlusPlus;
+ // C++11 generalized attributes, also available as a C11 extension.
+ Opts.CXXAttributes = Opts.CPlusPlus11 || Opts.C11;
+
Opts.GNUKeywords = Opts.GNUMode;
Opts.CXXOperatorNames = Opts.CPlusPlus;
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index c6559b4..51317bc 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -921,6 +921,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("cxx_alignas", LangOpts.CPlusPlus11)
.Case("cxx_atomic", LangOpts.CPlusPlus11)
.Case("cxx_attributes", LangOpts.CPlusPlus11)
+ .Case("attributes", LangOpts.CPlusPlus11)
.Case("cxx_auto_type", LangOpts.CPlusPlus11)
.Case("cxx_constexpr", LangOpts.CPlusPlus11)
.Case("cxx_decltype", LangOpts.CPlusPlus11)
@@ -1027,6 +1028,7 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("c_thread_local", PP.getTargetInfo().isTLSSupported())
// C++11 features supported by other languages as extensions.
.Case("cxx_atomic", LangOpts.CPlusPlus)
+ .Case("attributes", LangOpts.CXXAttributes)
.Case("cxx_deleted_functions", LangOpts.CPlusPlus)
.Case("cxx_explicit_conversions", LangOpts.CPlusPlus)
.Case("cxx_inline_namespaces", LangOpts.CPlusPlus)
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index febf9e6..18e280b 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1454,8 +1454,8 @@ bool Parser::MightBeDeclarator(unsigned Context) {
return getLangOpts().CPlusPlus;
case tok::l_square: // Might be an attribute on an unnamed bit-field.
- return Context == Declarator::MemberContext && getLangOpts().CPlusPlus11 &&
- NextToken().is(tok::l_square);
+ return Context == Declarator::MemberContext &&
+ getLangOpts().CXXAttributes && NextToken().is(tok::l_square);
case tok::colon: // Might be a typo for '::' or an unnamed bit-field.
return Context == Declarator::MemberContext || getLangOpts().CPlusPlus;
@@ -2508,7 +2508,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
case tok::l_square:
case tok::kw_alignas:
- if (!getLangOpts().CPlusPlus11 || !isCXX11AttributeSpecifier())
+ if (!getLangOpts().CXXAttributes || !isCXX11AttributeSpecifier())
goto DoneWithDeclSpec;
ProhibitAttributes(attrs);
@@ -4299,7 +4299,7 @@ bool Parser::isConstructorDeclarator() {
// A C++11 attribute here signals that we have a constructor, and is an
// attribute on the first constructor parameter.
- if (getLangOpts().CPlusPlus11 &&
+ if (getLangOpts().CXXAttributes &&
isCXX11AttributeSpecifier(/*Disambiguate*/ false,
/*OuterMightBeMessageSend*/ true)) {
TPA.Revert();
@@ -4376,7 +4376,7 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS,
bool CXX11AttributesAllowed,
bool AtomicAllowed,
bool IdentifierRequired) {
- if (getLangOpts().CPlusPlus11 && CXX11AttributesAllowed &&
+ if (getLangOpts().CXXAttributes && CXX11AttributesAllowed &&
isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrs(AttrFactory);
ParseCXX11Attributes(attrs);
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 4968631..8f945ca 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -1075,7 +1075,7 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
// C++11 attributes
case tok::l_square: // enum E [[]] x
// Note, no tok::kw_alignas here; alignas cannot appertain to a type.
- return getLangOpts().CPlusPlus11 && NextToken().is(tok::l_square);
+ return getLangOpts().CXXAttributes && NextToken().is(tok::l_square);
case tok::greater:
// template<class T = class X>
return getLangOpts().CPlusPlus;
@@ -3198,7 +3198,9 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)
&& "Not a C++11 attribute list");
- Diag(Tok.getLocation(), diag::warn_cxx98_compat_attribute);
+ Diag(Tok.getLocation(), getLangOpts().CPlusPlus
+ ? diag::warn_cxx98_compat_attribute
+ : diag::ext_cxx11_attribute_in_c);
ConsumeBracket();
ConsumeBracket();
@@ -3218,6 +3220,15 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
// Break out to the "expected ']'" diagnostic.
break;
+ // In C, contextually convert two adjacent ':' tokens to '::'.
+ if (!getLangOpts().CPlusPlus && Tok.is(tok::colon) &&
+ NextToken().is(tok::colon) && areTokensAdjacent(Tok, NextToken())) {
+ SourceLocation ColonLoc = ConsumeToken();
+ Tok.setKind(tok::coloncolon);
+ Tok.setLocation(ColonLoc);
+ Tok.setLength(2);
+ }
+
// scoped attribute
if (TryConsumeToken(tok::coloncolon)) {
ScopeName = AttrName;
@@ -3281,7 +3292,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
/// attribute-specifier-seq[opt] attribute-specifier
void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
SourceLocation *endLoc) {
- assert(getLangOpts().CPlusPlus11);
+ assert(getLangOpts().CXXAttributes);
SourceLocation StartLoc = Tok.getLocation(), Loc;
if (!endLoc)
diff --git a/test/Lexer/has_feature_c1x.c b/test/Lexer/has_feature_c1x.c
index e26e309..a6e6204 100644
--- a/test/Lexer/has_feature_c1x.c
+++ b/test/Lexer/has_feature_c1x.c
@@ -46,6 +46,15 @@ int no_thread_local();
// CHECK-1X: has_thread_local
// CHECK-NO-1X: no_thread_local
+#if __has_extension(attributes)
+int has_attributes();
+#else
+int no_attributes();
+#endif
+
+// CHECK-1X: has_attributes
+// CHECK-NO-1X: no_attributes
+
#if __STDC_VERSION__ > 199901L
int is_c1x();
#else
diff --git a/test/Lexer/has_feature_cxx0x.cpp b/test/Lexer/has_feature_cxx0x.cpp
index b2fe842..36a17b8 100644
--- a/test/Lexer/has_feature_cxx0x.cpp
+++ b/test/Lexer/has_feature_cxx0x.cpp
@@ -78,8 +78,17 @@ int no_trailing_return();
// CHECK-11: has_trailing_return
// CHECK-NO-11: no_trailing_return
-
#if __has_feature(cxx_attributes)
+int has_cxx_attributes();
+#else
+int no_cxx_attributes();
+#endif
+
+// CHECK-1Y: has_cxx_attributes
+// CHECK-11: has_cxx_attributes
+// CHECK-NO-11: no_cxx_attributes
+
+#if __has_feature(attributes)
int has_attributes();
#else
int no_attributes();
@@ -89,7 +98,6 @@ int no_attributes();
// CHECK-11: has_attributes
// CHECK-NO-11: no_attributes
-
#if __has_feature(cxx_static_assert)
int has_static_assert();
#else
diff --git a/test/Parser/cxx11-stmt-attributes.cpp b/test/Parser/cxx11-stmt-attributes.cpp
index 9374b58..962abcb 100644
--- a/test/Parser/cxx11-stmt-attributes.cpp
+++ b/test/Parser/cxx11-stmt-attributes.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 -Wno-generalized-attributes -x c %s
void foo(int i) {
@@ -20,14 +21,16 @@ void foo(int i) {
[[unknown_attribute]] goto here; // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
[[unknown_attribute]] here: // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
+ [[unknown_attribute]] return; // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
+
+#ifdef __cplusplus
[[unknown_attribute]] try { // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
} catch (...) {
}
- [[unknown_attribute]] return; // expected-warning {{unknown attribute 'unknown_attribute' ignored}}
-
-
alignas(8) ; // expected-error {{'alignas' attribute cannot be applied to a statement}}
+#endif
+
[[noreturn]] { } // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[noreturn]] if (0) { } // expected-error {{'noreturn' attribute cannot be applied to a statement}}
[[noreturn]] for (;;); // expected-error {{'noreturn' attribute cannot be applied to a statement}}
@@ -48,9 +51,11 @@ void foo(int i) {
[[fastcall]] goto there; // expected-warning {{unknown attribute 'fastcall' ignored}}
[[noinline]] there: // expected-warning {{unknown attribute 'noinline' ignored}}
+#ifdef __cplusplus
[[lock_returned]] try { // expected-warning {{unknown attribute 'lock_returned' ignored}}
} catch (...) {
}
+#endif
[[weakref]] return; // expected-warning {{unknown attribute 'weakref' ignored}}
@@ -71,9 +76,11 @@ void foo(int i) {
[[carries_dependency]] goto here; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
+#ifdef __cplusplus
[[carries_dependency]] try { // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
} catch (...) {
}
+#endif
[[carries_dependency]] return; // expected-error {{'carries_dependency' attribute cannot be applied to a statement}}
diff --git a/test/Parser/generalized-attributes.c b/test/Parser/generalized-attributes.c
new file mode 100644
index 0000000..ce95dcc
--- /dev/null
+++ b/test/Parser/generalized-attributes.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -Wno-generalized-attributes -Werror %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -Wno-c++11-extensions -Werror %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c11 %s
+
+[[noreturn]] void f4(); // expected-warning {{generalized attributes are a non-standard C extension}}
diff --git a/test/Parser/generalized-attributes.cpp b/test/Parser/generalized-attributes.cpp
new file mode 100644
index 0000000..ca681a6
--- /dev/null
+++ b/test/Parser/generalized-attributes.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat -Wno-generalized-attributes -Werror %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat -Wno-c++11-extensions -Werror %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wc++98-compat -verify %s
+
+int with_attribute [[ ]]; // expected-warning {{generalized attributes are incompatible with C++98}}
diff --git a/test/SemaCXX/cxx11-attr-print.cpp b/test/SemaCXX/cxx11-attr-print.cpp
index 01325d3..a42c3b2 100644
--- a/test/SemaCXX/cxx11-attr-print.cpp
+++ b/test/SemaCXX/cxx11-attr-print.cpp
@@ -1,5 +1,6 @@
-// RUN: %clang_cc1 -std=c++11 -ast-print -fms-extensions %s | FileCheck %s
-//
+// RUN: %clang_cc1 -ast-print -fms-extensions -std=c++11 %s | FileCheck -check-prefix=CHECK -check-prefix=CHECK-CXX %s
+// RUN: %clang_cc1 -ast-print -fms-extensions -std=c11 -Wno-generalized-attributes -x c %s | FileCheck -check-prefix=CHECK -check-prefix=CHECK-C %s
+
// CHECK: int x __attribute__((aligned(4)));
int x __attribute__((aligned(4)));
@@ -16,11 +17,16 @@ int a __attribute__((deprecated("warning")));
// CHECK: int b {{\[}}[gnu::deprecated("warning")]];
int b [[gnu::deprecated("warning")]];
-// CHECK: int cxx11_alignas alignas(4);
+#ifdef __cplusplus
+// CHECK-CXX: int cxx11_alignas alignas(4);
alignas(4) int cxx11_alignas;
-// CHECK: int c11_alignas _Alignas(alignof(int));
+// CHECK-CXX: int c11_alignas _Alignas(alignof(int));
+_Alignas(int) int c11_alignas;
+#else
+// CHECK-C: int c11_alignas _Alignas(_Alignof(int));
_Alignas(int) int c11_alignas;
+#endif
// CHECK: void foo() __attribute__((const));
void foo() __attribute__((const));
@@ -55,10 +61,12 @@ inline void f7 [[gnu::gnu_inline]] ();
// CHECK: __attribute__((format(printf, 2, 3)));
void f8 (void *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
-// CHECK: int m __attribute__((aligned(4
-// CHECK: int n alignas(4
-// CHECK: static int f() __attribute__((pure))
-// CHECK: static int g() {{\[}}[gnu::pure]]
+#ifdef __cplusplus
+
+// CHECK-CXX: int m __attribute__((aligned(4
+// CHECK-CXX: int n alignas(4
+// CHECK-CXX: static int f() __attribute__((pure))
+// CHECK-CXX: static int g() {{\[}}[gnu::pure]]
template <typename T> struct S {
__attribute__((aligned(4))) int m;
alignas(4) int n;
@@ -70,11 +78,13 @@ template <typename T> struct S {
}
};
-// CHECK: int m __attribute__((aligned(4
-// CHECK: int n alignas(4
-// CHECK: static int f() __attribute__((pure))
-// CHECK: static int g() {{\[}}[gnu::pure]]
+// CHECK-CXX: int m __attribute__((aligned(4
+// CHECK-CXX: int n alignas(4
+// CHECK-CXX: static int f() __attribute__((pure))
+// CHECK-CXX: static int g() {{\[}}[gnu::pure]]
template struct S<int>;
-// CHECK: using Small2 {{\[}}[gnu::mode(byte)]] = int;
+// CHECK-CXX: using Small2 {{\[}}[gnu::mode(byte)]] = int;
using Small2 [[gnu::mode(byte)]] = int;
+
+#endif
More information about the cfe-commits
mailing list