Skip to content

Commit 84a3aad

Browse files
committed
Diagnose use of VLAs in C++ by default
Reapplication of 7339c0f with a fix for a crash involving arrays without a size expression. Clang supports VLAs in C++ as an extension, but we currently only warn on their use when you pass -Wvla, -Wvla-extension, or -pedantic. However, VLAs as they're expressed in C have been considered by WG21 and rejected, are easy to use accidentally to the surprise of users (e.g., https://ddanilov.me/default-non-standard-features/), and they have potential security implications beyond constant-size arrays (https://wiki.sei.cmu.edu/confluence/display/c/ARR32-C.+Ensure+size+arguments+for+variable+length+arrays+are+in+a+valid+range). C++ users should strongly consider using other functionality such as std::vector instead. This seems like sufficiently compelling evidence to warn users about VLA use by default in C++ modes. This patch enables the -Wvla-extension diagnostic group in C++ language modes by default, and adds the warning group to -Wall in GNU++ language modes. The warning is still opt-in in C language modes, where support for VLAs is somewhat less surprising to users. RFC: https://discourse.llvm.org/t/rfc-diagnosing-use-of-vlas-in-c/73109 Fixes #62836 Differential Revision: https://reviews.llvm.org/D156565
1 parent 7b9fa21 commit 84a3aad

File tree

170 files changed

+2654
-2489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+2654
-2489
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ Improvements to Clang's diagnostics
306306
- Clang now displays an improved diagnostic and a note when a defaulted special
307307
member is marked ``constexpr`` in a class with a virtual base class
308308
(`#64843: <https://github.com/llvm/llvm-project/issues/64843>`_).
309+
<<<<<<< ours
309310
- ``-Wfixed-enum-extension`` and ``-Wmicrosoft-fixed-enum`` diagnostics are no longer
310311
emitted when building as C23, since C23 standardizes support for enums with a
311312
fixed underlying type.
@@ -346,6 +347,11 @@ Improvements to Clang's diagnostics
346347
| ~~~~~~~~~^~~~~~~~
347348
- Clang now always diagnoses when using non-standard layout types in ``offsetof`` .
348349
(`#64619: <https://github.com/llvm/llvm-project/issues/64619>`_)
350+
- Clang now diagnoses use of variable-length arrays in C++ by default (and
351+
under ``-Wall`` in GNU++ mode). This is an extension supported by Clang and
352+
GCC, but is very easy to accidentally use without realizing it's a
353+
nonportable construct that has different semantics from a constant-sized
354+
array. (`#62836 <https://github.com/llvm/llvm-project/issues/62836>`_)
349355

350356
Bug Fixes in This Version
351357
-------------------------

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,8 @@ def OverridingMethodMismatch : DiagGroup<"overriding-method-mismatch">;
848848
def VariadicMacros : DiagGroup<"variadic-macros">;
849849
def VectorConversion : DiagGroup<"vector-conversion">; // clang specific
850850
def VexingParse : DiagGroup<"vexing-parse">;
851-
def VLAExtension : DiagGroup<"vla-extension">;
851+
def VLAUseStaticAssert : DiagGroup<"vla-extension-static-assert">;
852+
def VLAExtension : DiagGroup<"vla-extension", [VLAUseStaticAssert]>;
852853
def VLA : DiagGroup<"vla", [VLAExtension]>;
853854
def VolatileRegisterVar : DiagGroup<"volatile-register-var">;
854855
def Visibility : DiagGroup<"visibility">;
@@ -1085,7 +1086,7 @@ def Consumed : DiagGroup<"consumed">;
10851086
// warning should be active _only_ when -Wall is passed in, mark it as
10861087
// DefaultIgnore in addition to putting it here.
10871088
def All : DiagGroup<"all", [Most, Parentheses, Switch, SwitchBool,
1088-
MisleadingIndentation, PackedNonPod]>;
1089+
MisleadingIndentation, PackedNonPod, VLAExtension]>;
10891090

10901091
// Warnings that should be in clang-cl /w4.
10911092
def : DiagGroup<"CL4", [All, Extra]>;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,18 @@ def err_half_const_requires_fp16 : Error<
137137
// C99 variable-length arrays
138138
def ext_vla : Extension<"variable length arrays are a C99 feature">,
139139
InGroup<VLAExtension>;
140+
// In C++ language modes, we warn by default as an extension, while in GNU++
141+
// language modes, we warn as an extension but add the warning group to -Wall.
142+
def ext_vla_cxx : ExtWarn<
143+
"variable length arrays in C++ are a Clang extension">,
144+
InGroup<VLAExtension>;
145+
def ext_vla_cxx_in_gnu_mode : Extension<ext_vla_cxx.Summary>,
146+
InGroup<VLAExtension>;
147+
def ext_vla_cxx_static_assert : ExtWarn<
148+
"variable length arrays in C++ are a Clang extension; did you mean to use "
149+
"'static_assert'?">, InGroup<VLAUseStaticAssert>;
150+
def ext_vla_cxx_in_gnu_mode_static_assert : Extension<
151+
ext_vla_cxx_static_assert.Summary>, InGroup<VLAUseStaticAssert>;
140152
def warn_vla_used : Warning<"variable length array used">,
141153
InGroup<VLA>, DefaultIgnore;
142154
def err_vla_in_sfinae : Error<

clang/lib/Sema/SemaType.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,27 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
25822582
return QualType();
25832583
}
25842584

2585+
auto IsStaticAssertLike = [](const Expr *ArraySize, ASTContext &Context) {
2586+
if (!ArraySize)
2587+
return false;
2588+
2589+
// If the array size expression is a conditional expression whose branches
2590+
// are both integer constant expressions, one negative and one positive,
2591+
// then it's assumed to be like an old-style static assertion. e.g.,
2592+
// int old_style_assert[expr ? 1 : -1];
2593+
// We will accept any integer constant expressions instead of assuming the
2594+
// values 1 and -1 are always used.
2595+
if (const auto *CondExpr = dyn_cast_if_present<ConditionalOperator>(
2596+
ArraySize->IgnoreParenImpCasts())) {
2597+
std::optional<llvm::APSInt> LHS =
2598+
CondExpr->getLHS()->getIntegerConstantExpr(Context);
2599+
std::optional<llvm::APSInt> RHS =
2600+
CondExpr->getRHS()->getIntegerConstantExpr(Context);
2601+
return LHS && RHS && LHS->isNegative() != RHS->isNegative();
2602+
}
2603+
return false;
2604+
};
2605+
25852606
// VLAs always produce at least a -Wvla diagnostic, sometimes an error.
25862607
unsigned VLADiag;
25872608
bool VLAIsError;
@@ -2598,6 +2619,15 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
25982619
} else if (getLangOpts().OpenMP && isInOpenMPTaskUntiedContext()) {
25992620
VLADiag = diag::err_openmp_vla_in_task_untied;
26002621
VLAIsError = true;
2622+
} else if (getLangOpts().CPlusPlus) {
2623+
if (getLangOpts().CPlusPlus11 && IsStaticAssertLike(ArraySize, Context))
2624+
VLADiag = getLangOpts().GNUMode
2625+
? diag::ext_vla_cxx_in_gnu_mode_static_assert
2626+
: diag::ext_vla_cxx_static_assert;
2627+
else
2628+
VLADiag = getLangOpts().GNUMode ? diag::ext_vla_cxx_in_gnu_mode
2629+
: diag::ext_vla_cxx;
2630+
VLAIsError = false;
26012631
} else {
26022632
VLADiag = diag::ext_vla;
26032633
VLAIsError = false;

clang/test/AST/Interp/literals.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fms-extensions -std=c++11 -verify %s
2-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fms-extensions -std=c++20 -verify %s
3-
// RUN: %clang_cc1 -std=c++11 -fms-extensions -verify=ref %s
4-
// RUN: %clang_cc1 -std=c++20 -fms-extensions -verify=ref %s
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -Wno-vla -fms-extensions -std=c++11 -verify %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -Wno-vla -fms-extensions -std=c++20 -verify %s
3+
// RUN: %clang_cc1 -std=c++11 -fms-extensions -Wno-vla -verify=ref %s
4+
// RUN: %clang_cc1 -std=c++20 -fms-extensions -Wno-vla -verify=ref %s
55

66
#define INT_MIN (~__INT_MAX__)
77
#define INT_MAX __INT_MAX__

clang/test/Analysis/lambdas.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,9 @@ void testFunctionPointerCapture() {
194194
// Captured variable-length array.
195195

196196
void testVariableLengthArrayCaptured() {
197-
int n = 2;
198-
int array[n];
197+
int n = 2; // expected-note {{declared here}}
198+
int array[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}} \
199+
expected-note {{read of non-const variable 'n' is not allowed in a constant expression}}
199200
array[0] = 7;
200201

201202
int i = [&]{

clang/test/CXX/basic/basic.types/p10.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ constexpr int f2(S &) { return 0; }
2020

2121
struct BeingDefined;
2222
extern BeingDefined beingdefined;
23-
struct BeingDefined {
23+
struct BeingDefined {
2424
static constexpr BeingDefined& t = beingdefined;
2525
};
2626

@@ -136,11 +136,15 @@ struct ArrBad {
136136
};
137137
constexpr int f(ArrBad) { return 0; } // expected-error {{1st parameter type 'ArrBad' is not a literal type}}
138138

139-
constexpr int arb(int n) {
140-
int a[n]; // expected-error {{variable of non-literal type 'int[n]' cannot be defined in a constexpr function}}
139+
constexpr int arb(int n) { // expected-note {{declared here}}
140+
int a[n]; // expected-error {{variable of non-literal type 'int[n]' cannot be defined in a constexpr function}} \
141+
expected-warning {{variable length arrays in C++ are a Clang extension}} \
142+
expected-note {{function parameter 'n' with unknown value cannot be used in a constant expression}}
141143
}
142-
// expected-warning@+1 {{variable length array folded to constant array as an extension}}
143-
constexpr long Overflow[(1 << 30) << 2]{}; // expected-warning {{requires 34 bits to represent}}
144+
constexpr long Overflow[(1 << 30) << 2]{}; // expected-warning {{requires 34 bits to represent}} \
145+
expected-warning {{variable length array folded to constant array as an extension}} \
146+
expected-warning {{variable length arrays in C++ are a Clang extension}} \
147+
expected-note {{signed left shift discards bits}}
144148

145149
namespace inherited_ctor {
146150
struct A { constexpr A(int); };

clang/test/CXX/dcl.dcl/dcl.spec/dcl.typedef/p2-0x.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,22 @@ namespace IllegalSyntax {
3434
namespace VariableLengthArrays {
3535
using T = int[42]; // ok
3636

37-
int n = 32;
38-
using T = int[n]; // expected-error {{variable length array declaration not allowed at file scope}}
37+
int n = 32; // expected-note {{declared here}}
38+
using T = int[n]; // expected-error {{variable length array declaration not allowed at file scope}} \
39+
expected-warning {{variable length arrays in C++ are a Clang extension}} \
40+
expected-note {{read of non-const variable 'n' is not allowed in a constant expression}}
3941

4042
const int m = 42;
4143
using U = int[m];
4244
using U = int[42]; // expected-note {{previous definition}}
4345
using U = int; // expected-error {{type alias redefinition with different types ('int' vs 'int[42]')}}
4446

4547
void f() {
46-
int n = 42;
48+
int n = 42; // expected-note {{declared here}}
4749
goto foo; // expected-error {{cannot jump}}
48-
using T = int[n]; // expected-note {{bypasses initialization of VLA type alias}}
50+
using T = int[n]; // expected-note {{bypasses initialization of VLA type alias}} \
51+
expected-warning {{variable length arrays in C++ are a Clang extension}} \
52+
expected-note {{read of non-const variable 'n' is not allowed in a constant expression}}
4953
foo: ;
5054
}
5155
}

clang/test/CXX/expr/expr.prim/expr.prim.lambda/p4.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,23 @@ X infer_X_return_type_2(X x) {
4444
}
4545

4646
struct Incomplete; // expected-note 2{{forward declaration of 'Incomplete'}}
47-
void test_result_type(int N) {
47+
void test_result_type(int N) { // expected-note {{declared here}}
4848
auto l1 = [] () -> Incomplete { }; // expected-error{{incomplete result type 'Incomplete' in lambda expression}}
4949

50-
typedef int vla[N];
50+
typedef int vla[N]; // expected-warning {{variable length arrays in C++ are a Clang extension}} \
51+
expected-note {{function parameter 'N' with unknown value cannot be used in a constant expression}}
5152
auto l2 = [] () -> vla { }; // expected-error{{function cannot return array type 'vla' (aka 'int[N]')}}
5253
}
5354

5455
template <typename T>
55-
void test_result_type_tpl(int N) {
56+
void test_result_type_tpl(int N) { // expected-note 2{{declared here}}
5657
auto l1 = []() -> T {}; // expected-error{{incomplete result type 'Incomplete' in lambda expression}}
5758
// expected-note@-1{{while substituting into a lambda expression here}}
58-
typedef int vla[N];
59+
typedef int vla[N]; // expected-warning 2{{variable length arrays in C++ are a Clang extension}} \
60+
expected-note 2{{function parameter 'N' with unknown value cannot be used in a constant expression}}
5961
auto l2 = []() -> vla {}; // expected-error{{function cannot return array type 'vla' (aka 'int[N]')}}
6062
}
6163

6264
void test_result_type_call() {
63-
test_result_type_tpl<Incomplete>(10); // expected-note {{requested here}}
65+
test_result_type_tpl<Incomplete>(10); // expected-note 2{{requested here}}
6466
}

clang/test/CXX/temp/temp.arg/temp.arg.type/p2.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fsyntax-only -verify -Wvla %s
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
33
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
44

@@ -19,13 +19,17 @@ enum {e};
1919
// expected-note@-2 {{unnamed type used in template argument was declared here}}
2020
#endif
2121

22-
void test_f0(int n) {
22+
void test_f0(int n) { // #here
2323
int i = f0(0, e);
2424
#if __cplusplus <= 199711L
2525
// expected-warning@-2 {{template argument uses unnamed type}}
2626
#endif
2727

28-
int vla[n];
28+
int vla[n]; // expected-warning {{variable length arrays in C++ are a Clang extension}}
29+
#if __cplusplus > 199711L
30+
// expected-note@-2 {{function parameter 'n' with unknown value cannot be used in a constant expression}}
31+
// expected-note@#here {{declared here}}
32+
#endif
2933
f0(0, vla); // expected-error{{no matching function for call to 'f0'}}
3034
}
3135

0 commit comments

Comments
 (0)