瀏覽代碼

Objective-C++11: Support static_assert() in @interface/@implementation ivar lists and method declarations

This adds support for static_assert() (and _Static_assert()) in
@interface/@implementation ivar lists and in @interface method declarations.

It was already supported in @implementation blocks outside of the ivar lists.

The assert AST nodes are added at file scope, matching where other
(non-Objective-C) declarations at @interface / @implementation level go (cf
`allTUVariables`).

Also add a `__has_feature(objc_c_static_assert)` that's true in C11 (and
`__has_extension(objc_c_static_assert)` that's always true) and
`__has_feature(objc_cxx_static_assert)` that's true in C++11 modea fter this
patch, so it's possible to check if this is supported.

Differential Revision: https://reviews.llvm.org/D59223


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@356148 91177308-0d34-0410-b5e6-96231b3b80d8
Nico Weber 6 年之前
父節點
當前提交
e615fa35aa

+ 3 - 0
include/clang/Basic/Features.def

@@ -116,6 +116,9 @@ FEATURE(objc_bridge_id_on_typedefs, true)
 FEATURE(objc_generics, LangOpts.ObjC)
 FEATURE(objc_generics, LangOpts.ObjC)
 FEATURE(objc_generics_variance, LangOpts.ObjC)
 FEATURE(objc_generics_variance, LangOpts.ObjC)
 FEATURE(objc_class_property, LangOpts.ObjC)
 FEATURE(objc_class_property, LangOpts.ObjC)
+FEATURE(objc_c_static_assert, LangOpts.C11)
+FEATURE(objc_cxx_static_assert, LangOpts.CPlusPlus11)
+EXTENSION(objc_c_static_assert, true)
 // C11 features
 // C11 features
 FEATURE(c_alignas, LangOpts.C11)
 FEATURE(c_alignas, LangOpts.C11)
 FEATURE(c_alignof, LangOpts.C11)
 FEATURE(c_alignof, LangOpts.C11)

+ 3 - 0
lib/Parse/ParseDecl.cpp

@@ -3889,6 +3889,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
 /// ParseStructDeclaration - Parse a struct declaration without the terminating
 /// ParseStructDeclaration - Parse a struct declaration without the terminating
 /// semicolon.
 /// semicolon.
 ///
 ///
+/// Note that a struct declaration refers to a declaration in a struct,
+/// not to the declaration of a struct.
+///
 ///       struct-declaration:
 ///       struct-declaration:
 /// [C2x]   attributes-specifier-seq[opt]
 /// [C2x]   attributes-specifier-seq[opt]
 ///           specifier-qualifier-list struct-declarator-list
 ///           specifier-qualifier-list struct-declarator-list

+ 24 - 0
lib/Parse/ParseObjc.cpp

@@ -623,6 +623,8 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
     }
     }
     // Ignore excess semicolons.
     // Ignore excess semicolons.
     if (Tok.is(tok::semi)) {
     if (Tok.is(tok::semi)) {
+      // FIXME: This should use ConsumeExtraSemi() for extraneous semicolons,
+      // to make -Wextra-semi diagnose them.
       ConsumeToken();
       ConsumeToken();
       continue;
       continue;
     }
     }
@@ -646,7 +648,19 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
       // erroneous r_brace would cause an infinite loop if not handled here.
       // erroneous r_brace would cause an infinite loop if not handled here.
       if (Tok.is(tok::r_brace))
       if (Tok.is(tok::r_brace))
         break;
         break;
+
       ParsedAttributesWithRange attrs(AttrFactory);
       ParsedAttributesWithRange attrs(AttrFactory);
+
+      // Since we call ParseDeclarationOrFunctionDefinition() instead of
+      // ParseExternalDeclaration() below (so that this doesn't parse nested
+      // @interfaces), this needs to duplicate some code from the latter.
+      if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+        SourceLocation DeclEnd;
+        allTUVariables.push_back(
+            ParseDeclaration(DeclaratorContext::FileContext, DeclEnd, attrs));
+        continue;
+      }
+
       allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs));
       allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs));
       continue;
       continue;
     }
     }
@@ -1875,6 +1889,7 @@ void Parser::HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocatio
 ///     ';'
 ///     ';'
 ///     objc-instance-variable-decl-list objc-visibility-spec
 ///     objc-instance-variable-decl-list objc-visibility-spec
 ///     objc-instance-variable-decl-list objc-instance-variable-decl ';'
 ///     objc-instance-variable-decl-list objc-instance-variable-decl ';'
+///     objc-instance-variable-decl-list static_assert-declaration
 ///     objc-instance-variable-decl-list ';'
 ///     objc-instance-variable-decl-list ';'
 ///
 ///
 ///   objc-visibility-spec:
 ///   objc-visibility-spec:
@@ -1945,6 +1960,15 @@ void Parser::ParseObjCClassInstanceVariables(Decl *interfaceDecl,
       return cutOffParsing();
       return cutOffParsing();
     }
     }
 
 
+    // This needs to duplicate a small amount of code from
+    // ParseStructUnionBody() for things that should work in both
+    // C struct and in Objective-C class instance variables.
+    if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
+      SourceLocation DeclEnd;
+      ParseStaticAssertDeclaration(DeclEnd);
+      continue;
+    }
+
     auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
     auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
       Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
       Actions.ActOnObjCContainerStartDefinition(interfaceDecl);
       // Install the declarator into the interface decl.
       // Install the declarator into the interface decl.

+ 1 - 1
lib/Sema/SemaExpr.cpp

@@ -2998,7 +2998,6 @@ ExprResult Sema::BuildDeclarationNameExpr(
 
 
     // These shouldn't make it here.
     // These shouldn't make it here.
     case Decl::ObjCAtDefsField:
     case Decl::ObjCAtDefsField:
-    case Decl::ObjCIvar:
       llvm_unreachable("forming non-member reference to ivar?");
       llvm_unreachable("forming non-member reference to ivar?");
 
 
     // Enum constants are always r-values and never references.
     // Enum constants are always r-values and never references.
@@ -3016,6 +3015,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
     // exist in the high-level semantics.
     // exist in the high-level semantics.
     case Decl::Field:
     case Decl::Field:
     case Decl::IndirectField:
     case Decl::IndirectField:
+    case Decl::ObjCIvar:
       assert(getLangOpts().CPlusPlus &&
       assert(getLangOpts().CPlusPlus &&
              "building reference to field in C?");
              "building reference to field in C?");
 
 

+ 54 - 0
test/Parser/objc-static-assert.m

@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -fobjc-runtime=macosx-fragile -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -std=c89 -fobjc-runtime=macosx-fragile -fsyntax-only -verify -Wno-objc-root-class %s
+
+
+#if __STDC_VERSION__ >= 201112L
+
+#if !__has_feature(objc_c_static_assert)
+#error failed
+#endif
+
+#if !__has_extension(objc_c_static_assert)
+#error failed
+#endif
+
+@interface A {
+  int a;
+  _Static_assert(1, "");
+  _Static_assert(0, ""); // expected-error {{static_assert failed}}
+
+  _Static_assert(a, ""); // expected-error {{use of undeclared identifier 'a'}}
+  _Static_assert(sizeof(a), ""); // expected-error {{use of undeclared identifier 'a'}}
+}
+
+_Static_assert(1, "");
+
+@end
+
+struct S {
+  @defs(A);
+};
+
+#else
+
+// _Static_assert is available before C11 as an extension, but -pedantic
+// warns on it.
+#if __has_feature(objc_c_static_assert)
+#error failed
+#endif
+
+#if !__has_extension(objc_c_static_assert)
+#error failed
+#endif
+
+@interface A {
+  int a;
+  _Static_assert(1, "");
+  _Static_assert(0, ""); // expected-error {{static_assert failed}}
+}
+
+_Static_assert(1, "");
+
+@end
+
+#endif

+ 74 - 0
test/Parser/objc-static-assert.mm

@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify -Wno-objc-root-class %s
+
+#if !__has_feature(objc_c_static_assert)
+#error failed
+#endif
+
+#if __cplusplus >= 201103L
+
+#if !__has_feature(objc_cxx_static_assert)
+#error failed
+#endif
+
+// C++11
+
+@interface A {
+  int a;
+  static_assert(1, "");
+  _Static_assert(1, "");
+
+  static_assert(0, ""); // expected-error {{static_assert failed}}
+  _Static_assert(0, ""); // expected-error {{static_assert failed}}
+
+  static_assert(a, ""); // expected-error {{static_assert expression is not an integral constant expression}}
+  static_assert(sizeof(a) == 4, "");
+  static_assert(sizeof(a) == 3, ""); // expected-error {{static_assert failed}}
+}
+
+static_assert(1, "");
+_Static_assert(1, "");
+
+- (void)f;
+@end
+
+@implementation A {
+  int b;
+  static_assert(1, "");
+  _Static_assert(1, "");
+  static_assert(sizeof(b) == 4, "");
+  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}}
+}
+
+static_assert(1, "");
+
+- (void)f {
+  static_assert(1, "");
+}
+@end
+
+@interface B
+@end
+
+@interface B () {
+  int b;
+  static_assert(sizeof(b) == 4, "");
+  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}}
+}
+@end
+
+#else
+
+#if __has_feature(objc_cxx_static_assert)
+#error failed
+#endif
+
+// C++98
+@interface A {
+  int a;
+  static_assert(1, ""); // expected-error {{type name requires a specifier or qualifier}} expected-error{{expected parameter declarator}} expected-error {{expected ')'}} expected-note {{to match this '('}}
+  _Static_assert(1, "");
+  _Static_assert(0, ""); // expected-error {{static_assert failed}}
+}
+@end
+#endif