C++ PATCH: PR 20103

Mark Mitchell mark@codesourcery.com
Thu May 25 21:58:00 GMT 2006


This patch fixes PR c++/20103, a problem with compound literals in
C++.  I took the straightforward approach of transforming:

  (A) { ... } 

into:

  A tmp = { ... }

which, is, approximately, what the C standard says should happen.

In the process, I fixed a long-standing oddity in the front end
regarding variable initialization.  In particular, whereas one would
expect DECL_INITIAL to be NULL for an uninitialized variable,
error_mark_node for an erroneously initialized variable, start_decl
instead set things up so that we had NULL_TREE for a variable whose
type prevents it from being initialized, error_mark_node for a
variable which we know to be initialized, but when we haven't yet
processed the initializer, or for a variable which has been
erroneously initialized.  This result in various parts of the front
end do:

  var = create_temporary_var ();
  /* Tell finish_decl that it's OK to initialize this variable.  */
  DECL_INITIAL (var) = error_mark_node;
  /* Process the initializer.  */
  finish_decl (var, init);

Blech!  So, now, error_mark_node always means erroneous initializer.

In the process of testing these changes I broke initialization of
static data members, and nothing in the testsuite caught that --
except for some libstdc++ tests.  As a result, I had to debug a
miscompiled libstdc++ to work out what had gone wrong.  So, here, I
added a test case for that (g++.dg/init/const3.C) to test that feature
in isolation.

Tested on x86_64-unknown-linux-gnu, applied to mainline.  I will try
to backport the crucial bits, once 4.1 reopens.

--
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

2006-05-25  Mark Mitchell  <mark@codesourcery.com>

	PR c++/20103
	* gimplify.c (gimplify_decl_expr): Do not call gimple_add_tmp_var
	for anonymous variables explicitly declared by front ends. 

2006-05-24  Mark Mitchell  <mark@codesourcery.com>

	PR c++/20103
	* decl.c (cp_make_fname_decl): Don't set DECL_INITIAL to
	error_mark_node to indicate an initialization is OK.
	(start_decl): Likewise.  Adjust call to start_decl_1.
	(start_decl_1): Add initialized parameter.  Simplify.
	* except.c (initialize_handler_parm): Adjust call to
	setart_decl_1.
	(expand_start_catch_block): Let cp_finish_decl initialize catch
	parameters.
	* cp-tree.h (start_decl_1): Adjust prototype.
	* pt.c (tsubst_expr): Don't set DECL_INITIAL to error_mark_node.
	(instantiate_decl): Let cp_finish_decl handle initialization.
	* semantics.c (finish_compound_literal): Create a temporary
	variable for the literal.
	* typeck.c (build_unary_op): Remove COMPOUND_LITERAL_P special
	cases.
	* decl2.c (finish_static_data_member_decl): Don't set
	DECL_INITIAL.
	(grokfield): Do not try to initialize functions.

2006-05-25  Mark Mitchell  <mark@codesourcery.com>

	PR c++/20103
	* g++.dg/ext/complit6.C: New test.
	* g++.dg/ext/complit3.C: Adjust error markers.
	* g++.dg/init/const3.C: New test. 	

Index: gcc/testsuite/g++.dg/ext/complit6.C
===================================================================
--- gcc/testsuite/g++.dg/ext/complit6.C	(revision 0)
+++ gcc/testsuite/g++.dg/ext/complit6.C	(revision 0)
@@ -0,0 +1,19 @@
+// PR c++/20103
+// { dg-options "" }
+
+struct A
+{
+  A(const A&);
+};
+
+struct B
+{
+  A a;
+};
+
+void foo(B);
+
+void bar(A &x)
+{
+  foo((B){x});
+}
Index: gcc/testsuite/g++.dg/ext/complit3.C
===================================================================
--- gcc/testsuite/g++.dg/ext/complit3.C	(revision 113901)
+++ gcc/testsuite/g++.dg/ext/complit3.C	(working copy)
@@ -2,7 +2,7 @@
 
 int Compound_Literals_0()
 {
-  static int y[] = (int []) {1, 2, 3}; // { dg-error "compound literal" }
-  static int z[] = (int [3]) {1}; // { dg-error "compound literal" }
+  static int y[] = (int []) {1, 2, 3}; // { dg-error "init" }
+  static int z[] = (int [3]) {1}; // { dg-error "init" }
   return y[0]+z[0]; 
 }
Index: gcc/testsuite/g++.dg/init/const3.C
===================================================================
--- gcc/testsuite/g++.dg/init/const3.C	(revision 0)
+++ gcc/testsuite/g++.dg/init/const3.C	(revision 0)
@@ -0,0 +1,12 @@
+// { dg-do run }
+
+struct S {
+  static const int i = 3;
+};
+
+const int S::i;
+
+int main () {
+  if (!S::i)
+    return 1;
+}
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c	(revision 113902)
+++ gcc/cp/typeck.c	(working copy)
@@ -4324,9 +4324,6 @@ build_unary_op (enum tree_code code, tre
       if (TREE_CODE (argtype) != FUNCTION_TYPE
 	  && TREE_CODE (argtype) != METHOD_TYPE
 	  && TREE_CODE (arg) != OFFSET_REF
-	  /* Permit users to take the address of a compound-literal
-	     with sufficient simple elements.  */
-	  && !(COMPOUND_LITERAL_P (arg) && TREE_STATIC (arg))
 	  && !lvalue_or_else (arg, lv_addressof))
 	return error_mark_node;
 
@@ -4343,24 +4340,6 @@ build_unary_op (enum tree_code code, tre
 	  return val;
 	}
 
-      /* If the user has taken the address of the compound literal,
-	 create a variable to contain the value of the literal and
-	 then return the address of that variable.  */
-      if (COMPOUND_LITERAL_P (arg))
-	{
-	  tree var;
-	  gcc_assert (TREE_STATIC (arg));
-	  var = create_temporary_var (TREE_TYPE (arg));
-	  TREE_STATIC (var) = 1;
-	  set_compound_literal_name (var); 
-	  initialize_artificial_var (var, arg);
-	  arg = pushdecl (var);
-	  /* Since each compound literal is unique, pushdecl should
-	     never find a pre-existing variable with the same
-	     name.  */
-	  gcc_assert (arg == var);
-	}
-      
       if (TREE_CODE (arg) != COMPONENT_REF)
 	{
 	  val = build_address (arg);
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 113901)
+++ gcc/cp/decl.c	(working copy)
@@ -3311,7 +3311,6 @@ cp_make_fname_decl (tree id, int type_de
   TREE_STATIC (decl) = 1;
   TREE_READONLY (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
-  DECL_INITIAL (decl) = init;
 
   TREE_USED (decl) = 1;
 
@@ -3848,11 +3847,6 @@ start_decl (const cp_declarator *declara
       DECL_EXTERNAL (decl) = 0;
       if (toplevel_bindings_p ())
 	TREE_STATIC (decl) = 1;
-
-      /* Tell `pushdecl' this is an initialized decl
-	 even though we don't yet have the initializer expression.
-	 Also tell `cp_finish_decl' it may store the real initializer.  */
-      DECL_INITIAL (decl) = error_mark_node;
     }
 
   /* Set attributes here so if duplicate decl, will have proper attributes.  */
@@ -3895,8 +3889,7 @@ start_decl (const cp_declarator *declara
 		 declaration will have DECL_EXTERNAL set, but will have an
 		 initialization.  Thus, duplicate_decls won't warn
 		 about this situation, and so we check here.  */
-	      if (DECL_INITIAL (decl) 
-		  && DECL_INITIALIZED_IN_CLASS_P (field))
+	      if (initialized && DECL_INITIALIZED_IN_CLASS_P (field))
 		error ("duplicate initialization of %qD", decl);
 	      if (duplicate_decls (decl, field, /*newdecl_is_friend=*/false))
 		decl = field;
@@ -3930,8 +3923,7 @@ start_decl (const cp_declarator *declara
 
 	     We check for processing_specialization so this only applies
 	     to the new specialization syntax.  */
-	  if (!DECL_INITIAL (decl)
-	      && processing_specialization)
+	  if (!initialized && processing_specialization)
 	    DECL_EXTERNAL (decl) = 1;
 	}
 
@@ -3960,21 +3952,25 @@ start_decl (const cp_declarator *declara
       && !have_global_bss_p ())
     DECL_COMMON (tem) = 1;
 
-  if (! processing_template_decl)
-    start_decl_1 (tem);
+  if (!processing_template_decl && TREE_CODE (tem) == VAR_DECL)
+    start_decl_1 (tem, initialized);
 
   return tem;
 }
 
 void
-start_decl_1 (tree decl)
+start_decl_1 (tree decl, bool initialized)
 {
-  tree type = TREE_TYPE (decl);
-  int initialized = (DECL_INITIAL (decl) != NULL_TREE);
+  tree type;
 
-  if (type == error_mark_node)
+  gcc_assert (!processing_template_decl);
+
+  if (error_operand_p (decl))
     return;
 
+  gcc_assert (TREE_CODE (decl) == VAR_DECL);
+  type = TREE_TYPE (decl);
+
   if (initialized)
     /* Is it valid for this decl to have an initializer at all?
        If not, set INITIALIZED to zero, which will indirectly
@@ -3998,16 +3994,10 @@ start_decl_1 (tree decl)
 	  initialized = 0;
 	}
     }
-
-  if (!initialized
-      && TREE_CODE (decl) != TYPE_DECL
-      && TREE_CODE (decl) != TEMPLATE_DECL
-      && type != error_mark_node
-      && IS_AGGR_TYPE (type)
-      && ! DECL_EXTERNAL (decl))
+  else if (IS_AGGR_TYPE (type)
+	   && ! DECL_EXTERNAL (decl))
     {
-      if ((! processing_template_decl || ! uses_template_parms (type))
-	  && !COMPLETE_TYPE_P (complete_type (type)))
+      if (!COMPLETE_TYPE_P (complete_type (type)))
 	{
 	  error ("aggregate %q#D has incomplete type and cannot be defined",
 		 decl);
@@ -4027,9 +4017,6 @@ start_decl_1 (tree decl)
 	}
     }
 
-  if (! initialized)
-    DECL_INITIAL (decl) = NULL_TREE;
-
   /* Create a new scope to hold this declaration if necessary.
      Whether or not a new scope is necessary cannot be determined
      until after the type has been completed; if the type is a
@@ -4665,16 +4652,6 @@ check_initializer (tree decl, tree init,
   tree type = TREE_TYPE (decl);
   tree init_code = NULL;
 
-  /* If `start_decl' didn't like having an initialization, ignore it now.  */
-  if (init != NULL_TREE && DECL_INITIAL (decl) == NULL_TREE)
-    init = NULL_TREE;
-
-  /* If an initializer is present, DECL_INITIAL has been
-     error_mark_node, to indicate that an as-of-yet unevaluated
-     initialization will occur.  From now on, DECL_INITIAL reflects
-     the static initialization -- if any -- of DECL.  */
-  DECL_INITIAL (decl) = NULL_TREE;
-
   /* Things that are going to be initialized need to have complete
      type.  */
   TREE_TYPE (decl) = type = complete_type (TREE_TYPE (decl));
@@ -5040,7 +5017,7 @@ cp_finish_decl (tree decl, tree init, bo
       if (at_function_scope_p ())
 	add_decl_expr (decl);
 
-      if (init && DECL_INITIAL (decl))
+      if (init)
 	{
 	  DECL_INITIAL (decl) = init;
 	  if (init_const_expr_p)
@@ -5227,10 +5204,7 @@ cp_finish_decl (tree decl, tree init, bo
 	    {
 	      /* Initialize the local variable.  */
 	      if (processing_template_decl)
-		{
-		  if (init || DECL_INITIAL (decl) == error_mark_node)
-		    DECL_INITIAL (decl) = init;
-		}
+		DECL_INITIAL (decl) = init;
 	      else if (!TREE_STATIC (decl))
 		initialize_local_var (decl, init);
 	    }
Index: gcc/cp/except.c
===================================================================
--- gcc/cp/except.c	(revision 113902)
+++ gcc/cp/except.c	(working copy)
@@ -392,11 +392,9 @@ initialize_handler_parm (tree decl, tree
       init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init);
     }
 
-  /* Let `cp_finish_decl' know that this initializer is ok.  */
-  DECL_INITIAL (decl) = error_mark_node;
   decl = pushdecl (decl);
 
-  start_decl_1 (decl);
+  start_decl_1 (decl, true);
   cp_finish_decl (decl, init, /*init_const_expr_p=*/false, NULL_TREE,
 		  LOOKUP_ONLYCONVERTING|DIRECT_BIND);
 }
@@ -464,7 +462,6 @@ expand_start_catch_block (tree decl)
       DECL_REGISTER (exp) = 1;
       cp_finish_decl (exp, init, /*init_const_expr=*/false, 
 		      NULL_TREE, LOOKUP_ONLYCONVERTING);
-      finish_expr_stmt (build_modify_expr (exp, INIT_EXPR, init));
       initialize_handler_parm (decl, exp);
     }
 
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 113958)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -3835,7 +3835,7 @@ extern tree check_tag_decl			(cp_decl_sp
 extern tree shadow_tag				(cp_decl_specifier_seq *);
 extern tree groktypename			(cp_decl_specifier_seq *, const cp_declarator *);
 extern tree start_decl				(const cp_declarator *, cp_decl_specifier_seq *, int, tree, tree, tree *);
-extern void start_decl_1			(tree);
+extern void start_decl_1			(tree, bool);
 extern void cp_finish_decl			(tree, tree, bool, tree, int);
 extern void finish_decl				(tree, tree, tree);
 extern int cp_complete_array_type		(tree *, tree, bool);
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 114023)
+++ gcc/cp/pt.c	(working copy)
@@ -8266,8 +8266,6 @@ tsubst_expr (tree t, tree args, tsubst_f
 	    decl = tsubst (decl, args, complain, in_decl);
 	    if (decl != error_mark_node)
 	      {
-		if (init)
-		  DECL_INITIAL (decl) = error_mark_node;
 		/* By marking the declaration as instantiated, we avoid
 		   trying to instantiate it.  Since instantiate_decl can't
 		   handle local variables, and since we've already done
@@ -11850,7 +11848,6 @@ instantiate_decl (tree d, int defer_ok, 
 	  init = tsubst_expr (DECL_INITIAL (code_pattern), 
 			      args,
 			      tf_warning_or_error, NULL_TREE);
-	  DECL_INITIAL (d) = init;
 	  cp_finish_decl (d, init, /*init_const_expr_p=*/false,
 			  /*asmspec_tree=*/NULL_TREE,
 			  LOOKUP_ONLYCONVERTING);
@@ -11917,11 +11914,20 @@ instantiate_decl (tree d, int defer_ok, 
 
   if (TREE_CODE (d) == VAR_DECL)
     {
+      tree init;
+
       /* Clear out DECL_RTL; whatever was there before may not be right
 	 since we've reset the type of the declaration.  */
       SET_DECL_RTL (d, NULL_RTX);
       DECL_IN_AGGR_P (d) = 0;
 
+      /* The initializer is placed in DECL_INITIAL by
+	 regenerate_decl_from_template.  Pull it out so that
+	 finish_decl can process it.  */
+      init = DECL_INITIAL (d);
+      DECL_INITIAL (d) = NULL_TREE;
+      DECL_INITIALIZED_P (d) = 0;
+
       /* Clear DECL_EXTERNAL so that cp_finish_decl will process the
 	 initializer.  That function will defer actual emission until
 	 we have a chance to determine linkage.  */
@@ -11929,7 +11935,7 @@ instantiate_decl (tree d, int defer_ok, 
 
       /* Enter the scope of D so that access-checking works correctly.  */
       push_nested_class (DECL_CONTEXT (d));
-      finish_decl (d, DECL_INITIAL (d), NULL_TREE);
+      finish_decl (d, init, NULL_TREE);
       pop_nested_class ();
     }
   else if (TREE_CODE (d) == FUNCTION_DECL)
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	(revision 113911)
+++ gcc/cp/semantics.c	(working copy)
@@ -1089,7 +1089,6 @@ finish_handler_parms (tree decl, tree ha
     }
   else
     type = expand_start_catch_block (decl);
-
   HANDLER_TYPE (handler) = type;
   if (!processing_template_decl && type)
     mark_used (eh_type_info (type));
@@ -2019,33 +2018,43 @@ finish_unary_op_expr (enum tree_code cod
 tree
 finish_compound_literal (tree type, VEC(constructor_elt,gc) *initializer_list)
 {
+  tree var;
   tree compound_literal;
 
   /* Build a CONSTRUCTOR for the INITIALIZER_LIST.  */
   compound_literal = build_constructor (NULL_TREE, initializer_list);
   if (processing_template_decl)
-    TREE_TYPE (compound_literal) = type;
-  else
     {
-      /* Check the initialization.  */
-      compound_literal = reshape_init (type, compound_literal);
-      compound_literal = digest_init (type, compound_literal);
-      /* If the TYPE was an array type with an unknown bound, then we can
-	 figure out the dimension now.  For example, something like:
-
-	   `(int []) { 2, 3 }'
-
-	 implies that the array has two elements.  */
-      if (TREE_CODE (type) == ARRAY_TYPE && !COMPLETE_TYPE_P (type))
-	cp_complete_array_type (&TREE_TYPE (compound_literal),
-				compound_literal, 1);
-    }
-
-  /* Mark it as a compound-literal.  */
-  if (TREE_CODE (compound_literal) == CONSTRUCTOR)
-    TREE_HAS_CONSTRUCTOR (compound_literal) = 1;
-
-  return compound_literal;
+      TREE_TYPE (compound_literal) = type;
+      /* Mark the expression as a compound literal.  */
+      TREE_HAS_CONSTRUCTOR (compound_literal) = 1;
+      return compound_literal;
+    }
+
+  /* Create a temporary variable to represent the compound literal.  */
+  var = create_temporary_var (type);
+  if (!current_function_decl)
+    {
+      /* If this compound-literal appears outside of a function, then
+	 the corresponding variable has static storage duration, just
+	 like the variable in whose initializer it appears.  */  
+      TREE_STATIC (var) = 1;
+      /* The variable has internal linkage, since there is no need to
+	 reference it from another translation unit.  */
+      TREE_PUBLIC (var) = 0;
+      /* It must have a name, so that the name mangler can mangle it.  */
+      DECL_NAME (var) = make_anon_name ();
+    }
+  /* We must call pushdecl, since the gimplifier complains if the
+     variable hase been declared via a BIND_EXPR.  */
+  pushdecl (var);
+  /* Initialize the variable as we would any other variable with a
+     brace-enclosed initializer.  */
+  cp_finish_decl (var, compound_literal, 
+		  /*init_const_expr_p=*/false,
+		  /*asmspec_tree=*/NULL_TREE,
+		  LOOKUP_ONLYCONVERTING);
+  return var;
 }
 
 /* Return the declaration for the function-name variable indicated by
Index: gcc/cp/decl2.c
===================================================================
--- gcc/cp/decl2.c	(revision 113901)
+++ gcc/cp/decl2.c	(working copy)
@@ -720,9 +720,6 @@ finish_static_data_member_decl (tree dec
   /* We cannot call pushdecl here, because that would fill in the
      TREE_CHAIN of our decl.  Instead, we modify cp_finish_decl to do
      the right thing, namely, to put this decl out straight away.  */
-  /* current_class_type can be NULL_TREE in case of error.  */
-  if (!asmspec_tree && current_class_type)
-    DECL_INITIAL (decl) = error_mark_node;
 
   if (! processing_template_decl)
     note_vague_linkage_var (decl);
@@ -920,7 +917,9 @@ grokfield (const cp_declarator *declarat
       if (asmspec)
 	set_user_assembler_name (value, asmspec);
 
-      cp_finish_decl (value, init, /*init_const_expr_p=*/false, 
+      cp_finish_decl (value, 
+		      /*init=*/NULL_TREE, 
+		      /*init_const_expr_p=*/false, 
 		      asmspec_tree, flags);
 
       /* Pass friends back this way.  */
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	(revision 113869)
+++ gcc/gimplify.c	(working copy)
@@ -1216,10 +1216,12 @@ gimplify_decl_expr (tree *stmt_p)
 	    walk_tree (&init, force_labels_r, NULL, NULL);
 	}
 
-      /* This decl isn't mentioned in the enclosing block, so add it to the
-	 list of temps.  FIXME it seems a bit of a kludge to say that
-	 anonymous artificial vars aren't pushed, but everything else is.  */
-      if (DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
+      /* Some front ends do not explicitly declare all anonymous
+	 artificial variables.  We compensate here by declaring the
+	 variables, though it would be better if the front ends would
+	 explicitly declare them.  */
+      if (!DECL_SEEN_IN_BIND_EXPR_P (decl)
+	  && DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
 	gimple_add_tmp_var (decl);
     }
 



More information about the Gcc-patches mailing list