C++: Reject variably modified types in operator new
authorFlorian Weimer <fweimer@redhat.com>
Mon, 25 Jun 2012 17:03:10 +0000 (19:03 +0200)
committerFlorian Weimer <fw@gcc.gnu.org>
Mon, 25 Jun 2012 17:03:10 +0000 (19:03 +0200)
From-SVN: r188948

gcc/cp/ChangeLog
gcc/cp/init.c
gcc/cp/parser.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/regress/debug-debug7.C
gcc/testsuite/g++.dg/ext/vla5.C
gcc/testsuite/g++.dg/ext/vla8.C
gcc/testsuite/g++.dg/init/new35.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/new36.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/new37.C [new file with mode: 0644]

index f87b4d68ec14600a919ead358864f2179d0dc2ff..fbaca5106c25508a270979a8570edcdeee1f287e 100644 (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
index 419c13f3e61002b2312dbfada6ca5d53d1ce0198..5a81643b5d941a2ade026b77a7e7b17c5f753c74 100644 (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)
     {
index 6bc6877c325a970f9ad872707fc1fd60770a1881..46f1401b66fda5f8811eb94dc3e37140c393c3fc 100644 (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);
 
index 4b5b80c15daa8d80f987dd03f35827d5b8c5b937..40547dc30320e1788e21c9ec5a83f12b09e14bbf 100644 (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>
 
index ea8f1eb2e6c119b5f972307d3914985b2673ff46..d3f14f4e242813ff13fc942dea72b9cac3c6aa31 100644 (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;
 
index 021d4846946ae8c6ae25661770a5a847f8f26b84..2457e34f18336b7d81a52ed110ad5c8883a78f55 100644 (file)
@@ -6,5 +6,5 @@
 void
 test (int a)
 {
-  new (char[a]);
+  new (char[a]); // { dg-warning "variable-length array" }
 }
index 7b7428d07a93796dd28a86f56d331f539b85b251..1c6000fa3b786b3aa0e1b9a1c76f1d7697c39e8d 100644 (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" }
diff --git a/gcc/testsuite/g++.dg/init/new35.C b/gcc/testsuite/g++.dg/init/new35.C
new file mode 100644 (file)
index 0000000..c5f79aa
--- /dev/null
@@ -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" }
+}
diff --git a/gcc/testsuite/g++.dg/init/new36.C b/gcc/testsuite/g++.dg/init/new36.C
new file mode 100644 (file)
index 0000000..c9b7af2
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/init/new37.C b/gcc/testsuite/g++.dg/init/new37.C
new file mode 100644 (file)
index 0000000..82ca18b
--- /dev/null
@@ -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);
+}