diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1844,6 +1844,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; @@ -1892,8 +1895,8 @@ FunctionDefinitionKind::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() { @@ -1976,6 +1979,7 @@ Attrs.clear(); AsmLabel = nullptr; InlineStorageUsed = false; + HasInitializer = false; ObjCIvar = false; ObjCWeakProperty = false; CommaLoc = SourceLocation(); @@ -2574,6 +2578,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::Member && diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2192,7 +2192,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) { @@ -2254,9 +2269,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)) { @@ -2311,7 +2326,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(); @@ -2365,8 +2382,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); @@ -2381,9 +2399,12 @@ Actions.ActOnInitializerError(ThisDecl); } else Actions.AddInitializerToDecl(ThisDecl, Init.get(), /*DirectInit=*/true); - - } else { + break; + } + case InitKind::Uninitialized: { Actions.ActOnUninitializedDecl(ThisDecl); + break; + } } Actions.FinalizeDeclaration(ThisDecl); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/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 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 @@ -6861,6 +6886,12 @@ } } + // If this variable has a variable-modified type and an initializer, try to + // fold to a constant-sized type. This is otherwise invalid. + if (D.hasInitializer() && R->isVariablyModifiedType()) + tryToFixVariablyModifiedVarType(*this, TInfo, R, D.getIdentifierLoc(), + /*DiagID=*/0); + bool IsMemberSpecialization = false; bool IsVariableTemplateSpecialization = false; bool IsPartialSpecialization = false; @@ -16658,27 +16689,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 @@ -16904,8 +16917,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. diff --git a/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp --- a/clang/test/CXX/basic/basic.types/p10.cpp +++ b/clang/test/CXX/basic/basic.types/p10.cpp @@ -140,8 +140,7 @@ int a[n]; // expected-error {{variable of non-literal type 'int [n]' cannot be defined in a constexpr function}} } // expected-warning@+1 {{variable length array folded to constant array as an extension}} -constexpr long Overflow[ // expected-error {{constexpr variable cannot have non-literal type 'long const[(1 << 30) << 2]'}} - (1 << 30) << 2]{}; // expected-warning {{requires 34 bits to represent}} +constexpr long Overflow[(1 << 30) << 2]{}; // expected-warning {{requires 34 bits to represent}} namespace inherited_ctor { struct A { constexpr A(int); }; diff --git a/clang/test/Sema/decl-in-prototype.c b/clang/test/Sema/decl-in-prototype.c --- a/clang/test/Sema/decl-in-prototype.c +++ b/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); } diff --git a/clang/test/Sema/vla.c b/clang/test/Sema/vla.c --- a/clang/test/Sema/vla.c +++ b/clang/test/Sema/vla.c @@ -100,3 +100,33 @@ 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:; + + goto jump_over_a2; + char a2[ksize] = "foo"; // expected-warning{{variable length array folded to constant array as an extension}} + jump_over_a2:; + + goto jump_over_a3; + char a3[ksize] = {}; // expected-warning {{variable length array folded to constant array as an extension}} expected-warning{{use of GNU empty initializer}} + jump_over_a3:; + + 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}}; +} diff --git a/clang/test/SemaCXX/vla.cpp b/clang/test/SemaCXX/vla.cpp --- a/clang/test/SemaCXX/vla.cpp +++ b/clang/test/SemaCXX/vla.cpp @@ -20,3 +20,9 @@ void pr23151(int (&)[*]) { // expected-error {{variable length array must be bound in function definition}} } + +void test_fold() { + char a1[(unsigned long)(int *)0+1]{}; // expected-warning{{variable length array folded to constant array as an extension}} + char a2[(unsigned long)(int *)0+1] = {}; // expected-warning{{variable length array folded to constant array as an extension}} + char a3[(unsigned long)(int *)0+1]; +} diff --git a/clang/test/SemaObjC/variable-size-ivar.m b/clang/test/SemaObjC/variable-size-ivar.m new file mode 100644 --- /dev/null +++ b/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