[clang] 090dd64 - [Sema] Fold VLAs to constant arrays in a few more contexts
Erik Pilkington via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 4 07:03:55 PST 2020
Author: Erik Pilkington
Date: 2020-12-04T10:03:23-05:00
New Revision: 090dd647d98dc50a56a42fbba0f3f11b10a3a187
URL: https://github.com/llvm/llvm-project/commit/090dd647d98dc50a56a42fbba0f3f11b10a3a187
DIFF: https://github.com/llvm/llvm-project/commit/090dd647d98dc50a56a42fbba0f3f11b10a3a187.diff
LOG: [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
Added:
clang/test/SemaObjC/variable-size-ivar.m
Modified:
clang/include/clang/Sema/DeclSpec.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Sema/SemaDecl.cpp
clang/test/CXX/basic/basic.types/p10.cpp
clang/test/Sema/decl-in-prototype.c
clang/test/Sema/vla.c
clang/test/SemaCXX/vla.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index afcbbaa5cfa7..fb30f95f5914 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1844,6 +1844,9 @@ class Declarator {
/// 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 @@ class Declarator {
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 @@ class Declarator {
Attrs.clear();
AsmLabel = nullptr;
InlineStorageUsed = false;
+ HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
CommaLoc = SourceLocation();
@@ -2574,6 +2578,9 @@ class Declarator {
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
index 277a67f3513f..c3c56ddccb6b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -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);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6da530c245fe..dc11dacb2491 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -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.
diff --git a/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp
index 124a489dfebe..aac07c76bed2 100644
--- a/clang/test/CXX/basic/basic.types/p10.cpp
+++ b/clang/test/CXX/basic/basic.types/p10.cpp
@@ -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 at +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
index 83e35a673390..52970e3e1ba8 100644
--- a/clang/test/Sema/decl-in-prototype.c
+++ b/clang/test/Sema/decl-in-prototype.c
@@ -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);
}
diff --git a/clang/test/Sema/vla.c b/clang/test/Sema/vla.c
index f49e8bbf5f7b..fd66694e8b90 100644
--- a/clang/test/Sema/vla.c
+++ b/clang/test/Sema/vla.c
@@ -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 at +1{{variable length array folded to constant array as an extension}}
+ int a7[ksize] __attribute__((annotate("foo"))) = {1,2,3,4};
+
+ // expected-warning at +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
index 6efb648e7868..f1e75f5dce9a 100644
--- a/clang/test/SemaCXX/vla.cpp
+++ b/clang/test/SemaCXX/vla.cpp
@@ -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];
+}
diff --git a/clang/test/SemaObjC/variable-size-ivar.m b/clang/test/SemaObjC/variable-size-ivar.m
new file mode 100644
index 000000000000..2d6c2f287140
--- /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;
+
+ at 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}}
+}
+ at end
More information about the cfe-commits
mailing list