C++: Reject variably modified types in operator new

From-SVN: r188948
This commit is contained in:
Florian Weimer 2012-06-25 19:03:10 +02:00 committed by Florian Weimer
parent 9e94a7fc5a
commit 4ebc46e969
10 changed files with 317 additions and 37 deletions

View File

@ -1,3 +1,11 @@
2012-06-25 Florian Weimer <fweimer@redhat.com>
* init.c (build_new_1): Warn about (T[N]) for variable N, and
reject T[M][N].
* parser.c (cp_parser_direct_new_declarator): Accept non-constant
expressions. Handled now in build_new_1.
2012-06-25 Jason Merrill <jason@redhat.com>
PR c++/53202

View File

@ -2175,6 +2175,7 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
tree pointer_type;
tree non_const_pointer_type;
tree outer_nelts = NULL_TREE;
bool outer_nelts_from_type = false;
tree alloc_call, alloc_expr;
/* The address returned by the call to "operator new". This node is
a VAR_DECL and is therefore reusable. */
@ -2209,10 +2210,14 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
}
else if (TREE_CODE (type) == ARRAY_TYPE)
{
/* Transforms new (T[N]) to new T[N]. The former is a GNU
extension for variable N. (This also covers new T where T is
a VLA typedef.) */
array_p = true;
nelts = array_type_nelts_top (type);
outer_nelts = nelts;
type = TREE_TYPE (type);
outer_nelts_from_type = true;
}
/* If our base type is an array, then make sure we know how many elements
@ -2220,10 +2225,46 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
for (elt_type = type;
TREE_CODE (elt_type) == ARRAY_TYPE;
elt_type = TREE_TYPE (elt_type))
nelts = cp_build_binary_op (input_location,
MULT_EXPR, nelts,
array_type_nelts_top (elt_type),
complain);
{
tree inner_nelts = array_type_nelts_top (elt_type);
tree inner_nelts_cst = maybe_constant_value (inner_nelts);
if (!TREE_CONSTANT (inner_nelts_cst))
{
if (complain & tf_error)
{
error_at (EXPR_LOC_OR_HERE (inner_nelts),
"array size in operator new must be constant");
cxx_constant_value(inner_nelts);
}
nelts = error_mark_node;
}
if (nelts != error_mark_node)
nelts = cp_build_binary_op (input_location,
MULT_EXPR, nelts,
inner_nelts_cst,
complain);
}
if (variably_modified_type_p (elt_type, NULL_TREE) && (complain & tf_error))
{
error ("variably modified type not allowed in operator new");
return error_mark_node;
}
if (nelts == error_mark_node)
return error_mark_node;
/* Warn if we performed the (T[N]) to T[N] transformation and N is
variable. */
if (outer_nelts_from_type
&& !TREE_CONSTANT (maybe_constant_value (outer_nelts)))
{
if (complain & tf_warning_or_error)
pedwarn(EXPR_LOC_OR_HERE (outer_nelts), OPT_Wvla,
"ISO C++ does not support variable-length array types");
else
return error_mark_node;
}
if (TREE_CODE (elt_type) == VOID_TYPE)
{

View File

@ -6883,41 +6883,34 @@ cp_parser_direct_new_declarator (cp_parser* parser)
while (true)
{
tree expression;
cp_token *token;
/* Look for the opening `['. */
cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE);
/* The first expression is not required to be constant. */
if (!declarator)
token = cp_lexer_peek_token (parser->lexer);
expression = cp_parser_expression (parser, /*cast_p=*/false, NULL);
/* The standard requires that the expression have integral
type. DR 74 adds enumeration types. We believe that the
real intent is that these expressions be handled like the
expression in a `switch' condition, which also allows
classes with a single conversion to integral or
enumeration type. */
if (!processing_template_decl)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
expression = cp_parser_expression (parser, /*cast_p=*/false, NULL);
/* The standard requires that the expression have integral
type. DR 74 adds enumeration types. We believe that the
real intent is that these expressions be handled like the
expression in a `switch' condition, which also allows
classes with a single conversion to integral or
enumeration type. */
if (!processing_template_decl)
expression
= build_expr_type_conversion (WANT_INT | WANT_ENUM,
expression,
/*complain=*/true);
if (!expression)
{
expression
= build_expr_type_conversion (WANT_INT | WANT_ENUM,
expression,
/*complain=*/true);
if (!expression)
{
error_at (token->location,
"expression in new-declarator must have integral "
"or enumeration type");
expression = error_mark_node;
}
error_at (token->location,
"expression in new-declarator must have integral "
"or enumeration type");
expression = error_mark_node;
}
}
/* But all the other expressions must be. */
else
expression
= cp_parser_constant_expression (parser,
/*allow_non_constant=*/false,
NULL);
/* Look for the closing `]'. */
cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);

View File

@ -1,3 +1,12 @@
2012-06-25 Florian Weimer <fweimer@redhat.com>
* g++.dg/init/new35.C: New.
* g++.dg/init/new36.C: New.
* g++.dg/init/new37.C: New.
* g++.dg/ext/vla5.C: New warning.
* g++.dg/ext/vla8.C: New warning.
* g++.dg/cpp0x/regress/debug-debug7.C: Update diagnostics.
2012-06-25 Matthew Gretton-Dann <matthew.gretton-dann@arm.com>
James Greenhalgh <james.greenhalgh@arm.com>

View File

@ -7,8 +7,8 @@ int
main() {
int a = 4;
int b = 5; // { dg-message "not const" }
int (*x)[b] = new int[a][b]; // { dg-error "not usable" }
int b = 5;
int (*x)[b] = new int[a][b]; // { dg-error "array size.*must be constant|usable in a constant" }
x[2][1] = 7;

View File

@ -6,5 +6,5 @@
void
test (int a)
{
new (char[a]);
new (char[a]); // { dg-warning "variable-length array" }
}

View File

@ -8,8 +8,8 @@ struct AvlTreeIter
AvlTreeIter()
{
new (void* [Num()]);
new (void* [Num()]); // { dg-warning "variable-length array" }
}
};
AvlTreeIter<int> a;
AvlTreeIter<int> a; // { dg-message "from here" }

View File

@ -0,0 +1,13 @@
// { dg-do compile }
// { dg-options "" }
int
main (int argc, char **argv)
{
typedef char A[argc];
new A; // { dg-warning "variable-length array types|not a constant" }
new A[0]; // { dg-error "must be constant|not a constant" }
new A[5]; // { dg-error "must be constant|not a constant" }
new (A[0]); // { dg-error "must be constant|not a constant" }
new (A[5]); // { dg-error "must be constant|not a constant" }
}

View File

@ -0,0 +1,153 @@
// Testcase for invocation of constructors/destructors in operator new[].
// { dg-do run }
#include <stdlib.h>
struct E {
virtual ~E() { }
};
struct S {
S();
~S();
};
static int count;
static int max;
static int throwAfter = -1;
static S *pS;
S::S()
{
if (throwAfter >= 0 && count >= throwAfter)
throw E();
if (pS)
{
++pS;
if (this != pS)
abort();
}
else
pS = this;
++count;
max = count;
}
S::~S()
{
if (count > 1)
{
if (this != pS)
abort();
--pS;
}
else
pS = 0;
--count;
}
void __attribute__((noinline)) doit(int n)
{
{
S *s = new S[n];
if (count != n)
abort();
if (pS != s + n - 1)
abort();
delete [] s;
if (count != 0)
abort();
}
throwAfter = 2;
max = 0;
try
{
new S[n];
abort();
}
catch (E)
{
if (max != 2)
abort();
}
throwAfter = -1;
}
int main()
{
{
S s;
if (count != 1)
abort();
if (pS != &s)
abort();
}
if (count != 0)
abort();
{
S *s = new S;
if (count != 1)
abort();
if (pS != s)
abort();
delete s;
if (count != 0)
abort();
}
{
S *s = new S[1];
if (count != 1)
abort();
if (pS != s)
abort();
delete [] s;
if (count != 0)
abort();
}
{
S *s = new S[5];
if (count != 5)
abort();
if (pS != s + 4)
abort();
delete [] s;
if (count != 0)
abort();
}
typedef S A[5];
{
S *s = new A;
if (count != 5)
abort();
if (pS != s + 4)
abort();
delete [] s;
if (count != 0)
abort();
}
throwAfter = 2;
max = 0;
try
{
new S[5];
abort();
}
catch (E)
{
if (max != 2)
abort();
}
max = 0;
try
{
new A;
abort();
}
catch (E)
{
if (max != 2)
abort();
}
throwAfter = -1;
doit(5);
}

View File

@ -0,0 +1,63 @@
// { dg-do compile }
void
nonconst(int n)
{
new (long[n][n]); // { dg-error "variable length|array size|not a constant" }
new long[n][n]; // { dg-error "variable length|array size|not a constant" }
}
template <typename T>
void *
callnew(int n)
{
return new long[n][T::n];
}
template <typename T>
void *
callnew_fail_1(int n)
{
return new long[n][T::n]; // { dg-error "variable length|array size|usable in a constant" }
}
template <typename T>
void *
callnew_fail_2()
{
return new long[T::n]; // { dg-error "size in array new" }
}
template <typename T>
void *
callnew_fail_3()
{
return new T[2][T::n]; // { dg-error "size of array has non-integral type" }
}
struct T1 {
static int n;
};
struct T2 {
static const double n = 2; // { dg-error "non-integral type" }
};
struct T3 {
static const int n = 2;
};
struct T4 {
enum { n = 3 };
};
void
test_callnew(int n)
{
new long[0.2]; // { dg-error "integral or enumeration type" }
callnew_fail_1<T1>(n);
callnew_fail_2<T2>();
callnew_fail_3<T2>();
callnew<T3>(n);
callnew<T4>(n);
}