diff options
author | Douglas Gregor <dgregor@apple.com> | 2010-05-22 16:17:30 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2010-05-22 16:17:30 +0000 |
commit | 0fddb97901dbe36a8253dee29961cba8e0a87cf6 (patch) | |
tree | 2744155a000f164f9b0b3a59211a8272d2c5ac4d | |
parent | e229b9dd546e7654e9b23664cc1b16be887b7770 (diff) |
Implement support for variable length arrays in C++. VLAs are limited
in several important ways:
- VLAs of non-POD types are not permitted.
- VLAs cannot be used in conjunction with C++ templates.
These restrictions are intended to keep VLAs out of the parts of the
C++ type system where they cause the most trouble. Fixes PR5678 and
<rdar://problem/8013618>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@104443 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 35 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 18 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 25 | ||||
-rw-r--r-- | test/CodeGenCXX/c99-variable-length-array.cpp | 27 | ||||
-rw-r--r-- | test/Sema/c89.c | 2 | ||||
-rw-r--r-- | test/SemaCXX/c99-variable-length-array.cpp | 80 | ||||
-rw-r--r-- | test/SemaCXX/c99.cpp | 5 | ||||
-rw-r--r-- | test/SemaCXX/offsetof.cpp | 2 | ||||
-rw-r--r-- | test/SemaObjCXX/vla.mm | 2 | ||||
-rw-r--r-- | test/SemaTemplate/instantiate-declref-ice.cpp | 3 | ||||
-rw-r--r-- | www/cxx_compatibility.html | 26 |
11 files changed, 187 insertions, 38 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1b79b75a1e..c566e909aa 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -36,6 +36,28 @@ def warn_float_underflow : Warning< "magnitude of floating-point constant too small for type %0; minimum is %1">, InGroup<LiteralRange>; +// C99 variable-length arrays +def ext_vla : Extension< + "variable length arrays are a C99 feature, accepted as an extension">; +def err_vla_non_pod : Error<"variable length array of non-POD element type %0">; +def err_vla_in_template : Error< + "variable length array cannot be used in a template %select{definition|" + "instantiation}0">; +def err_array_star_in_function_definition : Error< + "variable length array must be bound in function definition">; +def err_vla_decl_in_file_scope : Error< + "variable length array declaration not allowed at file scope">; +def err_vla_decl_has_static_storage : Error< + "variable length array declaration can not have 'static' storage duration">; +def err_vla_decl_has_extern_linkage : Error< + "variable length array declaration can not have 'extern' linkage">; + +// C99 variably modified types +def err_variably_modified_template_arg : Error< + "variably modified type %0 cannot be used as a template argument">; +def err_variably_modified_nontype_template_param : Error< + "non-type template parameter of variably modified type %0">; + // C99 Designated Initializers def err_array_designator_negative : Error< "array designator value '%0' is negative">; @@ -71,11 +93,6 @@ def ext_flexible_array_init : Extension< "flexible array initialization is a GNU extension">, InGroup<GNU>; // Declarations. -def ext_vla : Extension< - "variable length arrays are a C99 feature, accepted as an extension">; -def err_vla_cxx : Error< - "variable length arrays are not permitted in C++">; - def ext_anon_param_requires_type_specifier : Extension< "type specifier required for unnamed parameter, defaults to int">; def err_bad_variable_name : Error< @@ -89,8 +106,6 @@ def warn_unused_exception_param : Warning<"unused exception parameter %0">, InGroup<UnusedExceptionParameter>, DefaultIgnore; def warn_decl_in_param_list : Warning< "declaration of %0 will not be visible outside of this function">; -def err_array_star_in_function_definition : Error< - "variable length array must be bound in function definition">; def warn_unused_function : Warning<"unused function %0">, InGroup<UnusedFunction>, DefaultIgnore; @@ -1666,12 +1681,6 @@ def warn_enumerator_too_large : Warning< def warn_illegal_constant_array_size : Extension< "size of static array must be an integer constant expression">; -def err_vla_decl_in_file_scope : Error< - "variable length array declaration not allowed at file scope">; -def err_vla_decl_has_static_storage : Error< - "variable length array declaration can not have 'static' storage duration">; -def err_vla_decl_has_extern_linkage : Error< - "variable length array declaration can not have 'extern' linkage">; def err_vm_decl_in_file_scope : Error< "variably modified type declaration not allowed at file scope">; def err_vm_decl_has_extern_linkage : Error< diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 8ff637f2d1..ac77c11b95 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -553,6 +553,13 @@ Sema::CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc) { // assume that it is well-formed. T->isDependentType()) return T; + // We don't allow variably-modified types as the type of non-type template + // parameters. + else if (T->isVariablyModifiedType()) { + Diag(Loc, diag::err_variably_modified_nontype_template_param) + << T; + return QualType(); + } // C++ [temp.param]p8: // // A non-type template-parameter of type "array of T" or @@ -564,7 +571,7 @@ Sema::CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc) { else if (T->isFunctionType()) // FIXME: Keep the type prior to promotion? return Context.getPointerType(T); - + Diag(Loc, diag::err_template_nontype_parm_bad_type) << T; @@ -2354,7 +2361,8 @@ bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param, // compounded from any of these types shall not be used as a // template-argument for a template type-parameter. // - // FIXME: Perform the recursive and no-linkage type checks. + // FIXME: Perform the unnamed type check. + SourceRange SR = ArgInfo->getTypeLoc().getSourceRange(); const TagType *Tag = 0; if (const EnumType *EnumT = Arg->getAs<EnumType>()) Tag = EnumT; @@ -2366,12 +2374,14 @@ bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param, << QualType(Tag, 0) << SR; } else if (Tag && !Tag->getDecl()->getDeclName() && !Tag->getDecl()->getTypedefForAnonDecl()) { - SourceRange SR = ArgInfo->getTypeLoc().getSourceRange(); Diag(SR.getBegin(), diag::err_template_arg_unnamed_type) << SR; Diag(Tag->getDecl()->getLocation(), diag::note_template_unnamed_type_here); return true; + } else if (Arg->isVariablyModifiedType()) { + Diag(SR.getBegin(), diag::err_variably_modified_template_arg) + << Arg; + return true; } else if (Context.hasSameUnqualifiedType(Arg, Context.OverloadTy)) { - SourceRange SR = ArgInfo->getTypeLoc().getSourceRange(); return Diag(SR.getBegin(), diag::err_template_arg_overload_type) << SR; } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 80fafb6ad2..b25d076359 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -709,11 +709,26 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, } // If this is not C99, extwarn about VLA's and C99 array size modifiers. if (!getLangOptions().C99) { - if (ArraySize && !ArraySize->isTypeDependent() && - !ArraySize->isValueDependent() && - !ArraySize->isIntegerConstantExpr(Context)) - Diag(Loc, getLangOptions().CPlusPlus? diag::err_vla_cxx : diag::ext_vla); - else if (ASM != ArrayType::Normal || Quals != 0) + if (T->isVariableArrayType()) { + // Prohibit the use of non-POD types in VLAs. + if (!Context.getBaseElementType(T)->isPODType()) { + Diag(Loc, diag::err_vla_non_pod) + << Context.getBaseElementType(T); + return QualType(); + } + // Prohibit the use of VLAs in template instantiations, since we don't + // want them to accidentally be used. This means that we handle VLAs in + // C-like contexts, but still ban them from C++-specific contexts. + // And, since we check template definitions early, prohibit them there, + // too. + else if (CurContext->isDependentContext() || + ActiveTemplateInstantiations.size()) + Diag(Loc, diag::err_vla_in_template) + << !CurContext->isDependentContext(); + // Just extwarn about VLAs. + else + Diag(Loc, diag::ext_vla); + } else if (ASM != ArrayType::Normal || Quals != 0) Diag(Loc, getLangOptions().CPlusPlus? diag::err_c99_array_usage_cxx : diag::ext_c99_array_usage); diff --git a/test/CodeGenCXX/c99-variable-length-array.cpp b/test/CodeGenCXX/c99-variable-length-array.cpp new file mode 100644 index 0000000000..66c14ff579 --- /dev/null +++ b/test/CodeGenCXX/c99-variable-length-array.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s +struct X { + X(); + ~X(); +}; + +struct Y { + Y(); + ~Y(); +}; + +// CHECK: define void @_Z1fiPPKc( +void f(int argc, const char* argv[]) { + // CHECK: call void @_ZN1XC1Ev + X x; + // CHECK: call i8* @llvm.stacksave( + const char *argv2[argc]; + // CHECK: call void @_ZN1YC1Ev + Y y; + for (int i = 0; i != argc; ++i) + argv2[i] = argv[i]; + + // CHECK: call void @_ZN1YD1Ev + // CHECK: call void @llvm.stackrestore + // CHECK: call void @_ZN1XD1Ev + // CHECK: ret void +} diff --git a/test/Sema/c89.c b/test/Sema/c89.c index 8a9e622d87..038f7e537d 100644 --- a/test/Sema/c89.c +++ b/test/Sema/c89.c @@ -61,7 +61,7 @@ void foo(T); /* typedef for void is allowed */ void foo(void) {} /* PR2759 */ -void test10 (int x[*]); /* expected-warning {{use of C99-specific array features}} */ +void test10 (int x[*]); /* expected-warning {{variable length arrays are a C99 feature, accepted as an extension}} */ void test11 (int x[static 4]); /* expected-warning {{use of C99-specific array features}} */ void test12 (int x[const 4]) { /* expected-warning {{use of C99-specific array features}} */ diff --git a/test/SemaCXX/c99-variable-length-array.cpp b/test/SemaCXX/c99-variable-length-array.cpp new file mode 100644 index 0000000000..a792951252 --- /dev/null +++ b/test/SemaCXX/c99-variable-length-array.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +struct NonPOD { + NonPOD(); +}; + +struct NonPOD2 { + NonPOD np; +}; + +struct POD { + int x; + int y; +}; + +// We allow VLAs of POD types, only. +void vla(int N) { + int array1[N]; + POD array2[N]; + NonPOD array3[N]; // expected-error{{variable length array of non-POD element type 'NonPOD'}} + NonPOD2 array4[N][3]; // expected-error{{variable length array of non-POD element type 'NonPOD2'}} +} + +// We disallow VLAs in templates +template<typename T> +void vla_in_template(int N, T t) { + int array1[N]; // expected-error{{variable length array cannot be used in a template definition}} +} + +struct HasConstantValue { + static const unsigned int value = 2; +}; + +struct HasNonConstantValue { + static unsigned int value; +}; + +template<typename T> +void vla_in_template(T t) { + int array2[T::value]; // expected-error{{variable length array cannot be used in a template instantiation}} +} + +template void vla_in_template<HasConstantValue>(HasConstantValue); +template void vla_in_template<HasNonConstantValue>(HasNonConstantValue); // expected-note{{instantiation of}} + +template<typename T> struct X0 { }; + +// Cannot use any variably-modified type with a template parameter or +// argument. +void inst_with_vla(int N) { + int array[N]; + X0<__typeof__(array)> x0a; // expected-error{{variably modified type 'typeof (array)' (aka 'int [N]') cannot be used as a template argument}} +} + +template<typename T> +struct X1 { + template<int (&Array)[T::value]> // expected-error{{variable length array cannot be used in a template instantiation}} + struct Inner { + + }; +}; + +X1<HasConstantValue> x1a; +X1<HasNonConstantValue> x1b; // expected-note{{in instantiation of}} + +// Template argument deduction does not allow deducing a size from a VLA. +template<typename T, unsigned N> +void accept_array(T (&array)[N]); // expected-note{{candidate template ignored: failed template argument deduction}} + +void test_accept_array(int N) { + int array[N]; + accept_array(array); // expected-error{{no matching function for call to 'accept_array'}} +} + +// Variably-modified types cannot be used in local classes. +void local_classes(int N) { + struct X { + int size; + int array[N]; // expected-error{{fields must have a constant size: 'variable length array in structure' extension will never be supported}} + }; +} diff --git a/test/SemaCXX/c99.cpp b/test/SemaCXX/c99.cpp index f4c3639e16..b0bd45dea3 100644 --- a/test/SemaCXX/c99.cpp +++ b/test/SemaCXX/c99.cpp @@ -1,8 +1,3 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s - -void f0(int i) { - char array[i]; // expected-error{{variable length arrays}} -} - void f1(int i[static 5]) { // expected-error{{C99}} } diff --git a/test/SemaCXX/offsetof.cpp b/test/SemaCXX/offsetof.cpp index 47c3f22a0e..639d7faa8e 100644 --- a/test/SemaCXX/offsetof.cpp +++ b/test/SemaCXX/offsetof.cpp @@ -26,7 +26,7 @@ struct HasArray { // Constant and non-constant offsetof expressions void test_ice(int i) { int array0[__builtin_offsetof(HasArray, array[5])]; - int array1[__builtin_offsetof(HasArray, array[i])]; // expected-error{{variable length arrays are not permitted in C++}} + int array1[__builtin_offsetof(HasArray, array[i])]; } // Bitfields diff --git a/test/SemaObjCXX/vla.mm b/test/SemaObjCXX/vla.mm index 9c6fc54f81..d6da1c0cf4 100644 --- a/test/SemaObjCXX/vla.mm +++ b/test/SemaObjCXX/vla.mm @@ -6,7 +6,7 @@ @end void test(Data *d) { - char buffer[[d length]]; // expected-error{{variable length arrays are not permitted in C++}} + char buffer[[d length]]; [d getData:buffer]; } diff --git a/test/SemaTemplate/instantiate-declref-ice.cpp b/test/SemaTemplate/instantiate-declref-ice.cpp index e88b49447c..0f3c08b056 100644 --- a/test/SemaTemplate/instantiate-declref-ice.cpp +++ b/test/SemaTemplate/instantiate-declref-ice.cpp @@ -31,5 +31,4 @@ struct X1 { template<typename T> const unsigned X1<T>::value = sizeof(T); -int array3[X1<int>::value == sizeof(int)? 1 : -1]; // expected-error{{variable length arrays are not permitted in C++}} \ -// expected-error{{variable length array declaration not allowed at file scope}} +int array3[X1<int>::value == sizeof(int)? 1 : -1]; // expected-error{{variable length array declaration not allowed at file scope}} diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index bd48c6c09d..cfe3c0a60c 100644 --- a/www/cxx_compatibility.html +++ b/www/cxx_compatibility.html @@ -42,14 +42,28 @@ C++-conformance bug in your code and how you can fix it.</p> <h2 id="vla">Variable-length arrays</h2> <!-- ======================================================================= --> -<p>GCC allows an array's size to be determined at run time. This, -however, is not standard C++. Furthermore, it is a potential security -hole as an incorrect array size may overflow the stack. If Clang tells -you <tt>"variable length arrays are not permitted in C++"</tt>, here -are some ways in which you can fix it:</p> +<p>GCC and C99 allow an array's size to be determined at run +time. This extension is not permitted in standard C++. However, Clang +supports such variable length arrays in very limited circumstances for +compatibility with GNU C and C99 programs:</p> + +<ul> + <li>The element type of a variable length array must be a POD + ("plain old data") type, which means that it cannot have any + user-declared constructors or destructors, base classes, or any + members if non-POD type. All C types are POD types.</li> + + <li>Variable length arrays cannot be used in conjunction with + templates. For example, one cannot use a variable length array + inside a template or use a variable length array type in a template + argument.</li> +</ul> + +<p>If your code uses variable length arrays in a manner that Clang doesn't support, there are several ways to fix your code: <ol> -<li>replace it with a fixed-size array if you can determine a +<li>replace the variable length array with a fixed-size array if you can + determine a reasonable upper bound at compile time; sometimes this is as simple as changing <tt>int size = ...;</tt> to <tt>const int size = ...;</tt> (if the definition of <tt>size</tt> is a compile-time |