[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:
Erik Pilkington 2020-12-03 10:01:53 -05:00
parent c8ec685ca5
commit 090dd647d9
8 changed files with 124 additions and 35 deletions

View File

@ -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 &&

View File

@ -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);

View File

@ -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.

View File

@ -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); };

View File

@ -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);
}

View File

@ -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}};
}

View File

@ -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];
}

View File

@ -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