From: Eric Fiselier Date: Fri, 2 Sep 2016 18:25:29 +0000 (+0000) Subject: Implement __attribute__((require_constant_initialization)) for safe static initializa... X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=92f8935e63f0e5f3820533a9316bc651312b3208;p=platform%2Fupstream%2Fllvm.git Implement __attribute__((require_constant_initialization)) for safe static initialization. Summary: This attribute specifies expectations about the initialization of static and thread local variables. Specifically that the variable has a [constant initializer](http://en.cppreference.com/w/cpp/language/constant_initialization) according to the rules of [basic.start.static]. Failure to meet this expectation will result in an error. Static objects with constant initializers avoid hard-to-find bugs caused by the indeterminate order of dynamic initialization. They can also be safely used by other static constructors across translation units. This attribute acts as a compile time assertion that the requirements for constant initialization have been met. Since these requirements change between dialects and have subtle pitfalls it's important to fail fast instead of silently falling back on dynamic initialization. ```c++ // -std=c++14 #define SAFE_STATIC __attribute__((require_constant_initialization)) static struct T { constexpr T(int) {} ~T(); }; SAFE_STATIC T x = {42}; // OK. SAFE_STATIC T y = 42; // error: variable does not have a constant initializer // copy initialization is not a constant expression on a non-literal type. ``` This attribute can only be applied to objects with static or thread-local storage duration. Reviewers: majnemer, rsmith, aaron.ballman Subscribers: jroelofs, cfe-commits Differential Revision: https://reviews.llvm.org/D23385 llvm-svn: 280516 --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 7da1efe..912d151 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -241,6 +241,7 @@ def MicrosoftExt : LangOpt<"MicrosoftExt">; def Borland : LangOpt<"Borland">; def CUDA : LangOpt<"CUDA">; def COnly : LangOpt<"CPlusPlus", 1>; +def CPlusPlus : LangOpt<"CPlusPlus">; def OpenCL : LangOpt<"OpenCL">; def RenderScript : LangOpt<"RenderScript">; @@ -1380,6 +1381,15 @@ def ReqdWorkGroupSize : InheritableAttr { let Documentation = [Undocumented]; } +def RequireConstantInit : InheritableAttr { + let Spellings = [GNU<"require_constant_initialization">, + CXX11<"clang", "require_constant_initialization">]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag, + "ExpectedStaticOrTLSVar">; + let Documentation = [RequireConstantInitDocs]; + let LangOpts = [CPlusPlus]; +} + def WorkGroupSizeHint : InheritableAttr { let Spellings = [GNU<"work_group_size_hint">]; let Args = [UnsignedArgument<"XDim">, diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 9b1ddca..af494df 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -829,6 +829,43 @@ When one method overrides another, the overriding method can be more widely avai }]; } + +def RequireConstantInitDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +This attribute specifies that the variable to which it is attached is intended +to have a `constant initializer `_ +according to the rules of [basic.start.static]. The variable is required to +have static or thread storage duration. If the initialization of the variable +is not a constant initializer, an error will be produced. This attribute may +only be used in C++. + +Note that in C++03 strict constant expression checking is not done. Instead +the attribute reports if Clang can emit the the variable as a constant, even +if it's not technically a 'constant initializer'. This behavior is non-portable. + +Static storage duration variables with constant initializers avoid hard-to-find +bugs caused by the indeterminate order of dynamic initialization. They can also +be safely used during dynamic initialization across translation units. + +This attribute acts as a compile time assertion that the requirements +for constant initialization have been met. Since these requirements change +between dialects and have subtle pitfalls it's important to fail fast instead +of silently falling back on dynamic initialization. + +.. code-block:: c++ + // -std=c++14 + #define SAFE_STATIC __attribute__((require_constant_initialization)) static + struct T { + constexpr T(int) {} + ~T(); // non-trivial + }; + SAFE_STATIC T x = {42}; // Initialization OK. Doesn't check destructor. + SAFE_STATIC T y = 42; // error: variable does not have a constant initializer + // copy initialization is not a constant expression on a non-literal type. + }]; +} + def WarnMaybeUnusedDocs : Documentation { let Category = DocCatVariable; let Heading = "maybe_unused, unused, gnu::unused"; @@ -845,12 +882,12 @@ variable, a function or method, a function parameter, an enumeration, an enumerator, a non-static data member, or a label. .. code-block: c++ - #include - - [[maybe_unused]] void f([[maybe_unused]] bool thing1, - [[maybe_unused]] bool thing2) { - [[maybe_unused]] bool b = thing1 && thing2; - assert(b); + #include + + [[maybe_unused]] void f([[maybe_unused]] bool thing1, + [[maybe_unused]] bool thing2) { + [[maybe_unused]] bool b = thing1 && thing2; + assert(b); } }]; } @@ -867,15 +904,15 @@ potentially-evaluated discarded-value expression that is not explicitly cast to `void`. .. code-block: c++ - struct [[nodiscard]] error_info { /*...*/ }; - error_info enable_missile_safety_mode(); - - void launch_missiles(); - void test_missiles() { - enable_missile_safety_mode(); // diagnoses - launch_missiles(); - } - error_info &foo(); + struct [[nodiscard]] error_info { /*...*/ }; + error_info enable_missile_safety_mode(); + + void launch_missiles(); + void test_missiles() { + enable_missile_safety_mode(); // diagnoses + launch_missiles(); + } + error_info &foo(); void f() { foo(); } // Does not diagnose, error_info is a reference. }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a70b27b..414a6b4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2572,7 +2572,7 @@ def warn_attribute_wrong_decl_type : Warning< "Objective-C instance methods|init methods of interface or class extension declarations|" "variables, functions and classes|" "functions, variables, classes, and Objective-C interfaces|" - "Objective-C protocols|" + "Objective-C protocols|variables with static or thread storage duration|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" "interface or protocol declarations|kernel functions|non-K&R-style functions|" "variables, enums, fields and typedefs|functions, methods, enums, and classes|" @@ -6839,7 +6839,12 @@ def note_inequality_comparison_to_or_assign : Note< def err_incomplete_type_used_in_type_trait_expr : Error< "incomplete type %0 used in type trait expression">; - + +def err_require_constant_init_failed : Error< + "variable does not have a constant initializer">; +def note_declared_required_constant_init_here : Note< + "required by 'require_constant_initializer' attribute here">; + def err_dimension_expr_not_constant_integer : Error< "dimension expression does not evaluate to a constant unsigned int">; diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index fcddbec..6e3dcd7 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -897,6 +897,7 @@ enum AttributeDeclKind { ExpectedFunctionVariableOrClass, ExpectedFunctionVariableClassOrObjCInterface, ExpectedObjectiveCProtocol, + ExpectedStaticOrTLSVar, ExpectedFunctionGlobalVarMethodOrProperty, ExpectedStructOrUnionOrTypedef, ExpectedStructOrTypedef, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2a514ab..e6381fb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10393,8 +10393,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Diag(var->getLocation(), diag::warn_missing_variable_declarations) << var; } + // Cache the result of checking for constant initialization. + Optional CacheHasConstInit; + const Expr *CacheCulprit; + auto checkConstInit = [&]() mutable { + if (!CacheHasConstInit) + CacheHasConstInit = var->getInit()->isConstantInitializer( + Context, var->getType()->isReferenceType(), &CacheCulprit); + return *CacheHasConstInit; + }; + if (var->getTLSKind() == VarDecl::TLS_Static) { - const Expr *Culprit; if (var->getType().isDestructedType()) { // GNU C++98 edits for __thread, [basic.start.term]p3: // The type of an object with thread storage duration shall not @@ -10402,17 +10411,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Diag(var->getLocation(), diag::err_thread_nontrivial_dtor); if (getLangOpts().CPlusPlus11) Diag(var->getLocation(), diag::note_use_thread_local); - } else if (getLangOpts().CPlusPlus && var->hasInit() && - !var->getInit()->isConstantInitializer( - Context, var->getType()->isReferenceType(), &Culprit)) { - // GNU C++98 edits for __thread, [basic.start.init]p4: - // An object of thread storage duration shall not require dynamic - // initialization. - // FIXME: Need strict checking here. - Diag(Culprit->getExprLoc(), diag::err_thread_dynamic_init) - << Culprit->getSourceRange(); - if (getLangOpts().CPlusPlus11) - Diag(var->getLocation(), diag::note_use_thread_local); + } else if (getLangOpts().CPlusPlus && var->hasInit()) { + if (!checkConstInit()) { + // GNU C++98 edits for __thread, [basic.start.init]p4: + // An object of thread storage duration shall not require dynamic + // initialization. + // FIXME: Need strict checking here. + Diag(CacheCulprit->getExprLoc(), diag::err_thread_dynamic_init) + << CacheCulprit->getSourceRange(); + if (getLangOpts().CPlusPlus11) + Diag(var->getLocation(), diag::note_use_thread_local); + } } } @@ -10486,18 +10495,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (!var->getDeclContext()->isDependentContext() && Init && !Init->isValueDependent()) { - if (IsGlobal && !var->isConstexpr() && - !getDiagnostics().isIgnored(diag::warn_global_constructor, - var->getLocation())) { - // Warn about globals which don't have a constant initializer. Don't - // warn about globals with a non-trivial destructor because we already - // warned about them. - CXXRecordDecl *RD = baseType->getAsCXXRecordDecl(); - if (!(RD && !RD->hasTrivialDestructor()) && - !Init->isConstantInitializer(Context, baseType->isReferenceType())) - Diag(var->getLocation(), diag::warn_global_constructor) - << Init->getSourceRange(); - } if (var->isConstexpr()) { SmallVector Notes; @@ -10521,6 +10518,35 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // initialized by a constant expression if we check later. var->checkInitIsICE(); } + + // Don't emit further diagnostics about constexpr globals since they + // were just diagnosed. + if (!var->isConstexpr() && GlobalStorage && + var->hasAttr()) { + // FIXME: Need strict checking in C++03 here. + bool DiagErr = getLangOpts().CPlusPlus11 + ? !var->checkInitIsICE() : !checkConstInit(); + if (DiagErr) { + auto attr = var->getAttr(); + Diag(var->getLocation(), diag::err_require_constant_init_failed) + << Init->getSourceRange(); + Diag(attr->getLocation(), diag::note_declared_required_constant_init_here) + << attr->getRange(); + } + } + else if (!var->isConstexpr() && IsGlobal && + !getDiagnostics().isIgnored(diag::warn_global_constructor, + var->getLocation())) { + // Warn about globals which don't have a constant initializer. Don't + // warn about globals with a non-trivial destructor because we already + // warned about them. + CXXRecordDecl *RD = baseType->getAsCXXRecordDecl(); + if (!(RD && !RD->hasTrivialDestructor())) { + if (!checkConstInit()) + Diag(var->getLocation(), diag::warn_global_constructor) + << Init->getSourceRange(); + } + } } // Require the destructor. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0471f65..824d68e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5630,6 +5630,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_VecTypeHint: handleVecTypeHint(S, D, Attr); break; + case AttributeList::AT_RequireConstantInit: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_InitPriority: handleInitPriorityAttr(S, D, Attr); break; diff --git a/clang/test/SemaCXX/attr-require-constant-initialization.cpp b/clang/test/SemaCXX/attr-require-constant-initialization.cpp new file mode 100644 index 0000000..73f81cb --- /dev/null +++ b/clang/test/SemaCXX/attr-require-constant-initialization.cpp @@ -0,0 +1,282 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++03 %s +// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_ONE -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_TWO \ +// RUN: -Wglobal-constructors -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -DTEST_THREE -xc %s + +#define ATTR __attribute__((require_constant_initialization)) // expected-note 0+ {{expanded from macro}} + +int ReturnInt(); + +struct PODType { + int value; + int value2; +}; + +#if defined(__cplusplus) + +#if __cplusplus >= 201103L +struct LitType { + constexpr LitType() : value(0) {} + constexpr LitType(int x) : value(x) {} + LitType(void *) : value(-1) {} + int value; +}; +#endif + +struct NonLit { +#if __cplusplus >= 201402L + constexpr NonLit() : value(0) {} + constexpr NonLit(int x) : value(x) {} +#else + NonLit() : value(0) {} + NonLit(int x) : value(x) {} +#endif + NonLit(void *) : value(-1) {} + ~NonLit() {} + int value; +}; + +struct StoresNonLit { +#if __cplusplus >= 201402L + constexpr StoresNonLit() : obj() {} + constexpr StoresNonLit(int x) : obj(x) {} +#else + StoresNonLit() : obj() {} + StoresNonLit(int x) : obj(x) {} +#endif + StoresNonLit(void *p) : obj(p) {} + NonLit obj; +}; + +#endif // __cplusplus + + +#if defined(TEST_ONE) // Test semantics of attribute + +// Test diagnostics when attribute is applied to non-static declarations. +void test_func_local(ATTR int param) { // expected-error {{only applies to variables with static or thread}} + ATTR int x = 42; // expected-error {{only applies to variables with static or thread}} + ATTR extern int y; +} +struct ATTR class_mem { // expected-error {{only applies to variables with static or thread}} + ATTR int x; // expected-error {{only applies to variables with static or thread}} +}; + +// [basic.start.static]p2.1 +// if each full-expression (including implicit conversions) that appears in +// the initializer of a reference with static or thread storage duration is +// a constant expression (5.20) and the reference is bound to a glvalue +// designating an object with static storage duration, to a temporary object +// (see 12.2) or subobject thereof, or to a function; + +// Test binding to a static glvalue +const int glvalue_int = 42; +const int glvalue_int2 = ReturnInt(); +ATTR const int &glvalue_ref ATTR = glvalue_int; +ATTR const int &glvalue_ref2 ATTR = glvalue_int2; +ATTR __thread const int &glvalue_ref_tl = glvalue_int; + +void test_basic_start_static_2_1() { + const int non_global = 42; + ATTR static const int &local_init = non_global; // expected-error {{variable does not have a constant initializer}} + // expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + ATTR static const int &global_init = glvalue_int; + ATTR static const int &temp_init = 42; +} + +ATTR const int &temp_ref = 42; +ATTR const int &temp_ref2 = ReturnInt(); // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR const NonLit &nl_temp_ref = 42; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + +#if __cplusplus >= 201103L +ATTR const LitType &lit_temp_ref = 42; +ATTR const int &subobj_ref = LitType{}.value; +#endif + +ATTR const int &nl_subobj_ref = NonLit().value; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + +struct TT1 { + ATTR static const int &no_init; + ATTR static const int &glvalue_init; + ATTR static const int &temp_init; + ATTR static const int &subobj_init; +#if __cplusplus >= 201103L + ATTR static thread_local const int &tl_glvalue_init; + ATTR static thread_local const int &tl_temp_init; // expected-note {{required by 'require_constant_initializer' attribute here}} +#endif +}; +const int &TT1::glvalue_init = glvalue_int; +const int &TT1::temp_init = 42; +const int &TT1::subobj_init = PODType().value; +#if __cplusplus >= 201103L +thread_local const int &TT1::tl_glvalue_init = glvalue_int; +thread_local const int &TT1::tl_temp_init = 42; // expected-error {{variable does not have a constant initializer}} +#endif + +// [basic.start.static]p2.2 +// if an object with static or thread storage duration is initialized by a +// constructor call, and if the initialization full-expression is a constant +// initializer for the object; + +void test_basic_start_static_2_2() { +#if __cplusplus < 201103L + ATTR static PODType pod; +#else + ATTR static PODType pod; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +#endif + ATTR static PODType pot2 = {ReturnInt()}; // expected-error {{variable does not have a constant initializer}} + // expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + +#if __cplusplus >= 201103L + constexpr LitType l; + ATTR static LitType static_lit = l; + ATTR static LitType static_lit2 = (void *)0; // expected-error {{variable does not have a constant initializer}} + // expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + ATTR static LitType static_lit3 = ReturnInt(); // expected-error {{variable does not have a constant initializer}} + // expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + ATTR thread_local LitType tls = 42; +#endif +} + +struct TT2 { + ATTR static PODType pod_noinit; +#if __cplusplus >= 201103L +// expected-note@-2 {{required by 'require_constant_initializer' attribute here}} +#endif + ATTR static PODType pod_copy_init; // expected-note {{required by 'require_constant_initializer' attribute here}} +#if __cplusplus >= 201402L + ATTR static constexpr LitType lit = {}; + ATTR static const NonLit non_lit; + ATTR static const NonLit non_lit_list_init; + ATTR static const NonLit non_lit_copy_init; // expected-note {{required by 'require_constant_initializer' attribute here}} +#endif +}; +PODType TT2::pod_noinit; +#if __cplusplus >= 201103L +// expected-error@-2 {{variable does not have a constant initializer}} +#endif +PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does not have a constant initializer}} +#if __cplusplus >= 201402L +const NonLit TT2::non_lit(42); +const NonLit TT2::non_lit_list_init = {42}; +const NonLit TT2::non_lit_copy_init = 42; // expected-error {{variable does not have a constant initializer}} +#endif + +#if __cplusplus >= 201103L +ATTR LitType lit_ctor; +ATTR LitType lit_ctor2{}; +ATTR LitType lit_ctor3 = {}; +ATTR __thread LitType lit_ctor_tl = {}; + +#if __cplusplus >= 201402L +ATTR NonLit nl_ctor; +ATTR NonLit nl_ctor2{}; +ATTR NonLit nl_ctor3 = {}; +ATTR thread_local NonLit nl_ctor_tl = {}; +ATTR StoresNonLit snl; +#else +ATTR NonLit nl_ctor; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR NonLit nl_ctor2{}; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR NonLit nl_ctor3 = {}; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR thread_local NonLit nl_ctor_tl = {}; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR StoresNonLit snl; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +#endif + +// Non-literal types cannot appear in the initializer of a non-literal type. +ATTR int nl_in_init = NonLit{42}.value; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR int lit_in_init = LitType{42}.value; +#endif + +// [basic.start.static]p2.3 +// if an object with static or thread storage duration is not initialized by a +// constructor call and if either the object is value-initialized or every +// full-expression that appears in its initializer is a constant expression. +void test_basic_start_static_2_3() { + ATTR static int static_local = 42; + ATTR static int static_local2; // zero-initialization takes place +#if __cplusplus >= 201103L + ATTR thread_local int tl_local = 42; +#endif +} + +ATTR int no_init; // zero initialization takes place +ATTR int arg_init = 42; +ATTR PODType pod_init = {}; +ATTR PODType pod_missing_init = {42 /* should have second arg */}; +ATTR PODType pod_full_init = {1, 2}; +ATTR PODType pod_non_constexpr_init = {1, ReturnInt()}; // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} + +#if __cplusplus >= 201103L +ATTR int val_init{}; +ATTR int brace_init = {}; +#endif + +ATTR __thread int tl_init = 0; +typedef const char *StrType; + +#if __cplusplus >= 201103L + +// Test that the validity of the selected constructor is checked, not just the +// initializer +struct NotC { + constexpr NotC(void *) {} + NotC(int) {} +}; +template +struct TestCtor { + constexpr TestCtor(int x) : value(x) {} + T value; +}; +ATTR TestCtor t(42); // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +#endif + +// Test various array types +ATTR const char *foo[] = {"abc", "def"}; +ATTR PODType bar[] = {{}, {123, 456}}; + +#elif defined(TEST_TWO) // Test for duplicate warnings +struct NotC { + constexpr NotC(void *) {} + NotC(int) {} // expected-note 2 {{declared here}} +}; +template +struct TestCtor { + constexpr TestCtor(int x) : value(x) {} // expected-note 2 {{non-constexpr constructor}} + T value; +}; + +ATTR LitType non_const_lit(nullptr); // expected-error {{variable does not have a constant initializer}} +// expected-note@-1 {{required by 'require_constant_initializer' attribute here}} +ATTR NonLit non_const(nullptr); // expected-error {{variable does not have a constant initializer}} +// expected-warning@-1 {{declaration requires a global destructor}} +// expected-note@-2 {{required by 'require_constant_initializer' attribute here}} +LitType const_init_lit(nullptr); // expected-warning {{declaration requires a global constructor}} +NonLit const_init{42}; // expected-warning {{declaration requires a global destructor}} +constexpr TestCtor inval_constexpr(42); // expected-error {{must be initialized by a constant expression}} +// expected-note@-1 {{in call to 'TestCtor(42)'}} +ATTR constexpr TestCtor inval_constexpr2(42); // expected-error {{must be initialized by a constant expression}} +// expected-note@-1 {{in call to 'TestCtor(42)'}} + +#elif defined(TEST_THREE) +#if defined(__cplusplus) +#error This test requires C +#endif +// Test that using the attribute in C results in a diagnostic +ATTR int x = 0; // expected-warning {{attribute ignored}} +#else +#error No test case specified +#endif // defined(TEST_N) diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 50102af..071e06d 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -2790,8 +2790,10 @@ static std::string GenerateLangOptRequirements(const Record &R, std::string FnName = "check", Test; for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { std::string Part = (*I)->getValueAsString("Name"); - if ((*I)->getValueAsBit("Negated")) + if ((*I)->getValueAsBit("Negated")) { + FnName += "Not"; Test += "!"; + } Test += "S.LangOpts." + Part; if (I + 1 != E) Test += " || ";