Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -1850,6 +1850,9 @@ /// Indicates whether the InlineParams / InlineBindings storage has been used. unsigned InlineStorageUsed : 1; + /// Indicates whether this declarator has an initializer. + unsigned HasInitializer : 1; + /// Attrs - Attributes. ParsedAttributes Attrs; @@ -1897,8 +1900,8 @@ GroupingParens(false), FunctionDefinition(FDK_Declaration), Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), - Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr), - TrailingRequiresClause(nullptr), + HasInitializer(false), Attrs(ds.getAttributePool().getFactory()), + AsmLabel(nullptr), TrailingRequiresClause(nullptr), InventedTemplateParameterList(nullptr) {} ~Declarator() { @@ -1981,6 +1984,7 @@ Attrs.clear(); AsmLabel = nullptr; InlineStorageUsed = false; + HasInitializer = false; ObjCIvar = false; ObjCWeakProperty = false; CommaLoc = SourceLocation(); @@ -2579,6 +2583,9 @@ return (FunctionDefinitionKind)FunctionDefinition; } + void setHasInitializer(bool Val = true) { HasInitializer = Val; } + bool hasInitializer() const { return HasInitializer; } + /// Returns true if this declares a real member and not a friend. bool isFirstDeclarationOfMember() { return getContext() == DeclaratorContext::MemberContext && Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2193,7 +2193,22 @@ } }; - // Inform the current actions module that we just parsed this declarator. + enum class InitKind { Uninitialized, Equal, CXXDirect, CXXBraced }; + InitKind TheInitKind; + // If a '==' or '+=' is found, suggest a fixit to '='. + if (isTokenEqualOrEqualTypo()) + TheInitKind = InitKind::Equal; + else if (Tok.is(tok::l_paren)) + TheInitKind = InitKind::CXXDirect; + else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace) && + (!CurParsedObjCImpl || !D.isFunctionDeclarator())) + TheInitKind = InitKind::CXXBraced; + else + TheInitKind = InitKind::Uninitialized; + if (TheInitKind != InitKind::Uninitialized) + D.setHasInitializer(); + + // Inform Sema that we just parsed this declarator. Decl *ThisDecl = nullptr; Decl *OuterDecl = nullptr; switch (TemplateInfo.Kind) { @@ -2255,9 +2270,9 @@ } } + switch (TheInitKind) { // Parse declarator '=' initializer. - // If a '==' or '+=' is found, suggest a fixit to '='. - if (isTokenEqualOrEqualTypo()) { + case InitKind::Equal: { SourceLocation EqualLoc = ConsumeToken(); if (Tok.is(tok::kw_delete)) { @@ -2312,7 +2327,9 @@ Actions.AddInitializerToDecl(ThisDecl, Init.get(), /*DirectInit=*/false); } - } else if (Tok.is(tok::l_paren)) { + break; + } + case InitKind::CXXDirect: { // Parse C++ direct initializer: '(' expression-list ')' BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -2366,8 +2383,9 @@ Actions.AddInitializerToDecl(ThisDecl, Initializer.get(), /*DirectInit=*/true); } - } else if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace) && - (!CurParsedObjCImpl || !D.isFunctionDeclarator())) { + break; + } + case InitKind::CXXBraced: { // Parse C++0x braced-init-list. Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); @@ -2382,9 +2400,12 @@ Actions.ActOnInitializerError(ThisDecl); } else Actions.AddInitializerToDecl(ThisDecl, Init.get(), /*DirectInit=*/true); - - } else { + break; + } + case InitKind::Uninitialized: { Actions.ActOnUninitializedDecl(ThisDecl); + break; + } } Actions.FinalizeDeclaration(ThisDecl); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -6023,6 +6023,31 @@ return FixedTInfo; } +/// Attempt to fold a variable-sized type to a constant-sized type, returning +/// true if it we were successful. +static bool tryToFixVariablyModifiedVarType(Sema &S, TypeSourceInfo *&TInfo, + QualType &T, SourceLocation Loc, + unsigned FailedFoldDiagID) { + bool SizeIsNegative; + llvm::APSInt Oversized; + TypeSourceInfo *FixedTInfo = TryToFixInvalidVariablyModifiedTypeSourceInfo( + TInfo, S.Context, SizeIsNegative, Oversized); + if (FixedTInfo) { + S.Diag(Loc, diag::ext_vla_folded_to_constant); + TInfo = FixedTInfo; + T = FixedTInfo->getType(); + return true; + } + + if (SizeIsNegative) + S.Diag(Loc, diag::err_typecheck_negative_array_size); + else if (Oversized.getBoolValue()) + S.Diag(Loc, diag::err_array_too_large) << Oversized.toString(10); + else if (FailedFoldDiagID) + S.Diag(Loc, FailedFoldDiagID); + return false; +} + /// Register the given locally-scoped extern "C" declaration so /// that it can be found later for redeclarations. We include any extern "C" /// declaration that is not visible in the translation unit here, not just @@ -6871,6 +6896,10 @@ VarTemplateDecl *NewTemplate = nullptr; TemplateParameterList *TemplateParams = nullptr; if (!getLangOpts().CPlusPlus) { + if (D.hasInitializer() && R->isVariablyModifiedType()) + tryToFixVariablyModifiedVarType(*this, TInfo, R, D.getIdentifierLoc(), + /*DiagID=*/0); + NewVD = VarDecl::Create(Context, DC, D.getBeginLoc(), D.getIdentifierLoc(), II, R, TInfo, SC); @@ -16648,27 +16677,9 @@ // C99 6.7.2.1p8: A member of a structure or union may have any type other // than a variably modified type. if (!InvalidDecl && T->isVariablyModifiedType()) { - bool SizeIsNegative; - llvm::APSInt Oversized; - - TypeSourceInfo *FixedTInfo = - TryToFixInvalidVariablyModifiedTypeSourceInfo(TInfo, Context, - SizeIsNegative, - Oversized); - if (FixedTInfo) { - Diag(Loc, diag::ext_vla_folded_to_constant); - TInfo = FixedTInfo; - T = FixedTInfo->getType(); - } else { - if (SizeIsNegative) - Diag(Loc, diag::err_typecheck_negative_array_size); - else if (Oversized.getBoolValue()) - Diag(Loc, diag::err_array_too_large) - << Oversized.toString(10); - else - Diag(Loc, diag::err_typecheck_field_variable_size); + if (!tryToFixVariablyModifiedVarType( + *this, TInfo, T, Loc, diag::err_typecheck_field_variable_size)) InvalidDecl = true; - } } // Fields can not have abstract class types @@ -16894,8 +16905,9 @@ // C99 6.7.2.1p8: A member of a structure or union may have any type other // than a variably modified type. else if (T->isVariablyModifiedType()) { - Diag(Loc, diag::err_typecheck_ivar_variable_size); - D.setInvalidType(); + if (!tryToFixVariablyModifiedVarType( + *this, TInfo, T, Loc, diag::err_typecheck_ivar_variable_size)) + D.setInvalidType(); } // Get the visibility (access control) for this ivar. Index: clang/test/Sema/decl-in-prototype.c =================================================================== --- clang/test/Sema/decl-in-prototype.c +++ clang/test/Sema/decl-in-prototype.c @@ -49,7 +49,7 @@ // function. enum { BB = 0 }; void enum_in_fun_in_fun(void (*fp)(enum { AA, BB } e)) { // expected-warning {{will not be visible}} - SA(1, AA == 5); // expected-error {{variable-sized object may not be initialized}} + SA(1, AA == 5); // expected-warning{{variable length array folded to constant array as an extension}} SA(2, BB == 0); } Index: clang/test/Sema/vla.c =================================================================== --- clang/test/Sema/vla.c +++ clang/test/Sema/vla.c @@ -100,3 +100,29 @@ typedef struct { char c[pr44406_a]; // expected-warning {{folded to constant array as an extension}} } pr44406_s; + +void test_fold_to_constant_array() { + const int ksize = 4; + + goto jump_over_a1; // expected-error{{cannot jump from this goto statement to its label}} + char a1[ksize]; // expected-note{{variable length array}} + jump_over_a1:; + + char a2[ksize] = "foo"; // expected-warning{{variable length array folded to constant array as an extension}} + + char a3[ksize] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of GNU empty initializer}} + + goto jump_over_a4; // expected-error{{cannot jump from this goto statement to its label}} + char a4[ksize][2]; // expected-note{{variable length array}} + jump_over_a4:; + + char a5[ksize][2] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of GNU empty initializer}} + + int a6[ksize] = {1,2,3,4}; // expected-warning{{variable length array folded to constant array as an extension}} + + // expected-warning@+1{{variable length array folded to constant array as an extension}} + int a7[ksize] __attribute__((annotate("foo"))) = {1,2,3,4}; + + // expected-warning@+1{{variable length array folded to constant array as an extension}} + char a8[2][ksize] = {{1,2,3,4},{4,3,2,1}}; +} Index: clang/test/SemaObjC/variable-size-ivar.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/variable-size-ivar.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only %s -verify + +const int ksize = 42; +int size = 42; + +@interface X +{ + int arr1[ksize]; // expected-warning{{variable length array folded to constant array}} + int arr2[size]; // expected-error{{instance variables must have a constant size}} + int arr3[ksize-43]; // expected-error{{array size is negative}} +} +@end