[Sema] Fold VLAs to constant arrays in a few more contexts
552c6c2
removed support for promoting VLAs to constant arrays when the bounds
isn't an ICE, since this can result in miscompiling a conforming program that
assumes that the array is a VLA. Promoting VLAs for fields is still supported,
since clang doesn't support VLAs in fields, so no conforming program could have
a field VLA.
This change is really disruptive, so this commit carves out two more cases
where we promote VLAs which can't miscompile a conforming program:
- When the VLA appears in an ivar -- this seems like a corollary to the field thing
- When the VLA has an initializer -- VLAs can't have an initializer
Differential revision: https://reviews.llvm.org/D90871
This commit is contained in:
parent
c8ec685ca5
commit
090dd647d9
|
@ -1844,6 +1844,9 @@ private:
|
|||
/// 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 @@ public:
|
|||
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 @@ public:
|
|||
Attrs.clear();
|
||||
AsmLabel = nullptr;
|
||||
InlineStorageUsed = false;
|
||||
HasInitializer = false;
|
||||
ObjCIvar = false;
|
||||
ObjCWeakProperty = false;
|
||||
CommaLoc = SourceLocation();
|
||||
|
@ -2574,6 +2578,9 @@ public:
|
|||
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 &&
|
||||
|
|
|
@ -2192,7 +2192,22 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
}
|
||||
};
|
||||
|
||||
// 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 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
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 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
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 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
|
|||
Actions.ActOnInitializerError(ThisDecl);
|
||||
} else
|
||||
Actions.AddInitializerToDecl(ThisDecl, Init.get(), /*DirectInit=*/true);
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case InitKind::Uninitialized: {
|
||||
Actions.ActOnUninitializedDecl(ThisDecl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Actions.FinalizeDeclaration(ThisDecl);
|
||||
|
|
|
@ -6023,6 +6023,31 @@ TryToFixInvalidVariablyModifiedTypeSourceInfo(TypeSourceInfo *TInfo,
|
|||
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 @@ NamedDecl *Sema::ActOnVariableDeclarator(
|
|||
}
|
||||
}
|
||||
|
||||
// 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 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
|
|||
// 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 @@ Decl *Sema::ActOnIvar(Scope *S,
|
|||
// 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.
|
||||
|
|
|
@ -140,8 +140,7 @@ constexpr int arb(int n) {
|
|||
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); };
|
||||
|
|
|
@ -49,7 +49,7 @@ void f(struct q *, struct __attribute__((aligned(4))) q *); // expected-warning
|
|||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,3 +100,33 @@ const int pr44406_a = 32;
|
|||
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}};
|
||||
}
|
||||
|
|
|
@ -20,3 +20,9 @@ namespace PR18581 {
|
|||
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue