This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH: PR 23099


This patch fixes PR c++/23099.  The fundamental issue was that the
standard says that the initializers for static data members in
templates are not expanded until a definition of the static data
member is required.  

So, for example:

  struct A {};

  template <typename T, typename U>
  struct S {
    static const T t = U();
  };

  S<int, A> s;

is legal, even though lexically replacing "T" with "int", "U" with
"A", and making "S" a non-template class, would be invalid, since
"A()" is not of type "int".

Therefore, we have to delay substitution when instantiating the class
body, and instead notice uses of the static data member and make sure
to do the substitution at that point.

In the process, a latent bug (that we were not saving/restoring
skip_evaluation around calls to push_to_top_level) surfaced.  I'd be
surprised if there were no other way to trigger this bug, but perhaps
that was so.  Anyhow, that is now fixed as well.

This is a big enough change to the way in which static data members
are processed that I would not be surprised if there is some fallout.
Keep me posted.

Tested on x86_64-unknown-linux-gnu, applied on the branch and
mainline.

--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com

2005-08-28  Mark Mitchell  <mark@codesourcery.com>

	PR c++/23099
	* cp-tree.h (saved_scope): Add skip_evaluation.
	* decl.c (start_decl): Use DECL_INITIALIZED_IN_CLASS_P, not
	DECL_INITIAL, to determine whether or not a static data member was
	initialized in the class-specifier.
	(cp_finish_decl): Add comment.
	* init.c (integral_constant_value): Subtitute into the
	initializers for static data members in	templates.
	* name-lookup.c (push_to_top_level): Save skip_evaluation.
	(pop_from_top_level): Restore it.
	* pt.c (instantiate_class_template): Do not substitute into the
	intializers of static data members when instantiating a class.
	(regenerate_decl_from_template): Simplify.
	(instantiate_decl): Tidy.  Substitute into the initializer for a
	static data member even when the definition of the data member is
	not available.

2005-08-28  Mark Mitchell  <mark@codesourcery.com>

	PR c++/23099
	* g++.dg/init/member1.C: Make sure erroneous static data member
	definitions are required.
	* g++.dg/template/static13.C: New test.
	* g++.dg/template/static14.C: Likewise.

Index: gcc/cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.1159
diff -c -5 -p -r1.1159 cp-tree.h
*** gcc/cp/cp-tree.h	26 Aug 2005 19:32:24 -0000	1.1159
--- gcc/cp/cp-tree.h	29 Aug 2005 06:32:27 -0000
*************** struct saved_scope GTY(())
*** 653,662 ****
--- 653,663 ----
  
    HOST_WIDE_INT x_processing_template_decl;
    int x_processing_specialization;
    bool x_processing_explicit_instantiation;
    int need_pop_function_context;
+   bool skip_evaluation;
  
    struct stmt_tree_s x_stmt_tree;
  
    struct cp_binding_level *class_bindings;
    struct cp_binding_level *bindings;
Index: gcc/cp/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.1420
diff -c -5 -p -r1.1420 decl.c
*** gcc/cp/decl.c	22 Aug 2005 23:52:22 -0000	1.1420
--- gcc/cp/decl.c	29 Aug 2005 06:32:28 -0000
*************** start_decl (const cp_declarator *declara
*** 3714,3724 ****
  	      /* Static data member are tricky; an in-class initialization
  		 still doesn't provide a definition, so the in-class
  		 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_INITIAL (field))
  		error ("duplicate initialization of %qD", decl);
  	      if (duplicate_decls (decl, field))
  		decl = field;
  	    }
  	}
--- 3714,3725 ----
  	      /* Static data member are tricky; an in-class initialization
  		 still doesn't provide a definition, so the in-class
  		 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))
  		error ("duplicate initialization of %qD", decl);
  	      if (duplicate_decls (decl, field))
  		decl = field;
  	    }
  	}
*************** cp_finish_decl (tree decl, tree init, tr
*** 4919,4932 ****
  	    {
  	      error ("%qD is thread-local and so cannot be dynamically "
  		     "initialized", decl);
  	      init = NULL_TREE;
  	    }
  	  if (DECL_EXTERNAL (decl) && init)
  	    {
- 	      /* The static data member cannot be initialized by a
- 		 non-constant when being declared.  */
  	      error ("%qD cannot be initialized by a non-constant expression"
  		     " when being declared", decl);
  	      DECL_INITIALIZED_IN_CLASS_P (decl) = 0;
  	      init = NULL_TREE;
  	    }
--- 4920,4943 ----
  	    {
  	      error ("%qD is thread-local and so cannot be dynamically "
  		     "initialized", decl);
  	      init = NULL_TREE;
  	    }
+ 
+ 	  /* Check that the initializer for a static data member was a
+ 	     constant.  Althouh we check in the parser that the
+ 	     initializer is an integral constant expression, we do not
+ 	     simplify division-by-zero at the point at which it
+ 	     occurs.  Therefore, in:
+ 
+ 	       struct S { static const int i = 7 / 0; };
+ 	       
+ 	     we issue an error at this point.  It would
+ 	     probably be better to forbid division by zero in
+ 	     integral constant expressions.  */
  	  if (DECL_EXTERNAL (decl) && init)
  	    {
  	      error ("%qD cannot be initialized by a non-constant expression"
  		     " when being declared", decl);
  	      DECL_INITIALIZED_IN_CLASS_P (decl) = 0;
  	      init = NULL_TREE;
  	    }
Index: gcc/cp/init.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/init.c,v
retrieving revision 1.427
diff -c -5 -p -r1.427 init.c
*** gcc/cp/init.c	26 Aug 2005 19:32:25 -0000	1.427
--- gcc/cp/init.c	29 Aug 2005 06:32:28 -0000
*************** integral_constant_value (tree decl)
*** 1570,1585 ****
    while ((TREE_CODE (decl) == CONST_DECL
  	  || (TREE_CODE (decl) == VAR_DECL
  	      /* And so are variables with a 'const' type -- unless they
  		 are also 'volatile'.  */
  	      && CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (decl))
! 	      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)))
! 	 && DECL_INITIAL (decl)
! 	 && DECL_INITIAL (decl) != error_mark_node
! 	 && TREE_TYPE (DECL_INITIAL (decl))
! 	 && INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (DECL_INITIAL (decl))))
!     decl = DECL_INITIAL (decl);
    return decl;
  }
  
  /* A more relaxed version of integral_constant_value, for which type
     is not considered.  This is used by the common C/C++ code, and not
--- 1570,1598 ----
    while ((TREE_CODE (decl) == CONST_DECL
  	  || (TREE_CODE (decl) == VAR_DECL
  	      /* And so are variables with a 'const' type -- unless they
  		 are also 'volatile'.  */
  	      && CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (decl))
! 	      && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))))
!     {
!       tree init;
!       /* If DECL is a static data member in a template class, we must
! 	 instantiate it here.  The initializer for the static data
! 	 member is not processed until needed; we need it now.  */ 
!       mark_used (decl);
!       init = DECL_INITIAL (decl);
!       /* If we are currently processing a template, the
! 	 initializer for a static data member may not be dependent,
! 	 but it is not folded until instantiation time.  */
!       if (init)
! 	init = fold_non_dependent_expr (init);
!       if (!(init || init == error_mark_node)
! 	  || !TREE_TYPE (init)
! 	  || !INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (init)))
! 	break;
!       decl = init;
!     }
    return decl;
  }
  
  /* A more relaxed version of integral_constant_value, for which type
     is not considered.  This is used by the common C/C++ code, and not
Index: gcc/cp/name-lookup.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/name-lookup.c,v
retrieving revision 1.137
diff -c -5 -p -r1.137 name-lookup.c
*** gcc/cp/name-lookup.c	16 Aug 2005 00:13:47 -0000	1.137
--- gcc/cp/name-lookup.c	29 Aug 2005 06:32:28 -0000
*************** push_to_top_level (void)
*** 4870,4885 ****
--- 4870,4887 ----
  
    s->prev = scope_chain;
    s->bindings = b;
    s->need_pop_function_context = need_pop;
    s->function_decl = current_function_decl;
+   s->skip_evaluation = skip_evaluation;
  
    scope_chain = s;
    current_function_decl = NULL_TREE;
    current_lang_base = VEC_alloc (tree, gc, 10);
    current_lang_name = lang_name_cplusplus;
    current_namespace = global_namespace;
+   skip_evaluation = 0;
    timevar_pop (TV_NAME_LOOKUP);
  }
  
  void
  pop_from_top_level (void)
*************** pop_from_top_level (void)
*** 4907,4916 ****
--- 4909,4919 ----
    /* If we were in the middle of compiling a function, restore our
       state.  */
    if (s->need_pop_function_context)
      pop_function_context_from (NULL_TREE);
    current_function_decl = s->function_decl;
+   skip_evaluation = s->skip_evaluation;
    timevar_pop (TV_NAME_LOOKUP);
  }
  
  /* Pop off extraneous binding levels left over due to syntax errors.
  
Index: gcc/cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.1026
diff -c -5 -p -r1.1026 pt.c
*** gcc/cp/pt.c	26 Aug 2005 19:35:07 -0000	1.1026
--- gcc/cp/pt.c	29 Aug 2005 06:32:29 -0000
*************** instantiate_class_template (tree type)
*** 5697,5717 ****
  		  r = tsubst (t, args, tf_error | tf_warning, NULL_TREE);
  		  if (TREE_CODE (t) == TEMPLATE_DECL)
  		    --processing_template_decl;
  		  if (TREE_CODE (r) == VAR_DECL)
  		    {
! 		      tree init;
! 
! 		      if (DECL_INITIALIZED_IN_CLASS_P (r))
! 			init = tsubst_expr (DECL_INITIAL (t), args,
! 					    tf_error | tf_warning, NULL_TREE);
! 		      else
! 			init = NULL_TREE;
! 
! 		      finish_static_data_member_decl
! 			(r, init, /*asmspec_tree=*/NULL_TREE, /*flags=*/0);
  
  		      if (DECL_INITIALIZED_IN_CLASS_P (r))
  			check_static_variable_definition (r, TREE_TYPE (r));
  		    }
  		  else if (TREE_CODE (r) == FIELD_DECL)
  		    {
--- 5697,5722 ----
  		  r = tsubst (t, args, tf_error | tf_warning, NULL_TREE);
  		  if (TREE_CODE (t) == TEMPLATE_DECL)
  		    --processing_template_decl;
  		  if (TREE_CODE (r) == VAR_DECL)
  		    {
! 		      /* In [temp.inst]:
  
+ 			   [t]he initialization (and any associated
+ 			   side-effects) of a static data member does
+ 			   not occur unless the static data member is
+ 			   itself used in a way that requires the
+ 			   definition of the static data member to
+ 			   exist.  
+ 
+ 			 Therefore, we do not substitute into the
+ 		         initialized for the static data member here.  */
+ 		      finish_static_data_member_decl 
+ 			(r, 
+ 			 /*init=*/NULL_TREE, 
+ 			 /*asmspec_tree=*/NULL_TREE, 
+ 			 /*flags=*/0);
  		      if (DECL_INITIALIZED_IN_CLASS_P (r))
  			check_static_variable_definition (r, TREE_TYPE (r));
  		    }
  		  else if (TREE_CODE (r) == FIELD_DECL)
  		    {
*************** regenerate_decl_from_template (tree decl
*** 11276,11292 ****
  	DECL_DECLARED_INLINE_P (decl) = 1;
        if (DECL_INLINE (code_pattern) && !DECL_INLINE (decl))
  	DECL_INLINE (decl) = 1;
      }
    else if (TREE_CODE (decl) == VAR_DECL)
!     {
!       if (!DECL_INITIALIZED_IN_CLASS_P (decl)
! 	  && DECL_INITIAL (code_pattern))
! 	DECL_INITIAL (decl) =
! 	  tsubst_expr (DECL_INITIAL (code_pattern), args,
! 		       tf_error, DECL_TI_TEMPLATE (decl));
!     }
    else
      gcc_unreachable ();
  
    pop_access_scope (decl);
  }
--- 11281,11293 ----
  	DECL_DECLARED_INLINE_P (decl) = 1;
        if (DECL_INLINE (code_pattern) && !DECL_INLINE (decl))
  	DECL_INLINE (decl) = 1;
      }
    else if (TREE_CODE (decl) == VAR_DECL)
!     DECL_INITIAL (decl) =
!       tsubst_expr (DECL_INITIAL (code_pattern), args,
! 		   tf_error, DECL_TI_TEMPLATE (decl));
    else
      gcc_unreachable ();
  
    pop_access_scope (decl);
  }
*************** instantiate_decl (tree d, int defer_ok, 
*** 11365,11375 ****
    tree args;
    tree td;
    tree code_pattern;
    tree spec;
    tree gen_tmpl;
!   int pattern_defined;
    int need_push;
    location_t saved_loc = input_location;
  
    /* This function should only be used to instantiate templates for
       functions and static member variables.  */
--- 11366,11376 ----
    tree args;
    tree td;
    tree code_pattern;
    tree spec;
    tree gen_tmpl;
!   bool pattern_defined;
    int need_push;
    location_t saved_loc = input_location;
  
    /* This function should only be used to instantiate templates for
       functions and static member variables.  */
*************** instantiate_decl (tree d, int defer_ok, 
*** 11413,11425 ****
    if (! push_tinst_level (d))
      return d;
  
    timevar_push (TV_PARSE);
  
-   /* We may be in the middle of deferred access check.  Disable it now.  */
-   push_deferring_access_checks (dk_no_deferred);
- 
    /* Set TD to the template whose DECL_TEMPLATE_RESULT is the pattern
       for the instantiation.  */
    td = template_for_substitution (d);
    code_pattern = DECL_TEMPLATE_RESULT (td);
  
--- 11414,11423 ----
*************** instantiate_decl (tree d, int defer_ok, 
*** 11435,11444 ****
--- 11433,11446 ----
  
    if (TREE_CODE (d) == FUNCTION_DECL)
      pattern_defined = (DECL_SAVED_TREE (code_pattern) != NULL_TREE);
    else
      pattern_defined = ! DECL_IN_AGGR_P (code_pattern);
+ 
+   /* We may be in the middle of deferred access check.  Disable it now.  */
+   push_deferring_access_checks (dk_no_deferred);
+ 
    /* Unless an explicit instantiation directive has already determined
       the linkage of D, remember that a definition is available for
       this entity.  */
    if (pattern_defined
        && !DECL_INTERFACE_KNOWN (d)
*************** instantiate_decl (tree d, int defer_ok, 
*** 11484,11499 ****
        tsubst (type, gen_args, tf_error | tf_warning, d);
  
        pop_access_scope (d);
      }
  
-   /* We should have set up DECL_INITIAL in instantiate_class_template
-      for in-class definitions of static data members.  */
-   gcc_assert (!(TREE_CODE (d) == VAR_DECL
- 		&& DECL_INITIALIZED_IN_CLASS_P (d)
- 		&& DECL_INITIAL (d) == NULL_TREE));
- 
    /* Do not instantiate templates that we know will be defined
       elsewhere.  */
    if (DECL_INTERFACE_KNOWN (d)
        && DECL_REALLY_EXTERN (d)
        && ! (TREE_CODE (d) == FUNCTION_DECL
--- 11486,11495 ----
*************** instantiate_decl (tree d, int defer_ok, 
*** 11502,11511 ****
--- 11498,11521 ----
    /* Defer all other templates, unless we have been explicitly
       forbidden from doing so.  We restore the source position here
       because it's used by add_pending_template.  */
    else if (! pattern_defined || defer_ok)
      {
+       /* The definition of the static data member is now required so
+ 	 we must substitute the initializer.  */
+       if (TREE_CODE (d) == VAR_DECL
+ 	  && !DECL_INITIAL (d) 
+ 	  && DECL_INITIAL (code_pattern))
+ 	{
+ 	  push_nested_class (DECL_CONTEXT (d));
+ 	  DECL_INITIAL (d)
+ 	    = tsubst_expr (DECL_INITIAL (code_pattern), 
+ 			   args,
+ 			   tf_error | tf_warning, NULL_TREE);
+ 	  pop_nested_class ();
+ 	}
+ 
        input_location = saved_loc;
  
        if (at_eof && !pattern_defined
  	  && DECL_EXPLICIT_INSTANTIATION (d))
  	/* [temp.explicit]
*************** instantiate_decl (tree d, int defer_ok, 
*** 11568,11581 ****
  	 we have a chance to determine linkage.  */
        DECL_EXTERNAL (d) = 0;
  
        /* Enter the scope of D so that access-checking works correctly.  */
        push_nested_class (DECL_CONTEXT (d));
!       cp_finish_decl (d,
! 		      (!DECL_INITIALIZED_IN_CLASS_P (d)
! 		       ? DECL_INITIAL (d) : NULL_TREE),
! 		      NULL_TREE, 0);
        pop_nested_class ();
      }
    else if (TREE_CODE (d) == FUNCTION_DECL)
      {
        htab_t saved_local_specializations;
--- 11578,11588 ----
  	 we have a chance to determine linkage.  */
        DECL_EXTERNAL (d) = 0;
  
        /* Enter the scope of D so that access-checking works correctly.  */
        push_nested_class (DECL_CONTEXT (d));
!       cp_finish_decl (d, DECL_INITIAL (d), NULL_TREE, 0);
        pop_nested_class ();
      }
    else if (TREE_CODE (d) == FUNCTION_DECL)
      {
        htab_t saved_local_specializations;
Index: gcc/testsuite/g++.dg/init/member1.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/init/member1.C,v
retrieving revision 1.1
diff -c -5 -p -r1.1 member1.C
*** gcc/testsuite/g++.dg/init/member1.C	13 Jun 2005 15:58:09 -0000	1.1
--- gcc/testsuite/g++.dg/init/member1.C	29 Aug 2005 06:32:38 -0000
*************** template<typename> struct A;
*** 9,18 ****
  template<int> struct B {};
  
  template<typename T> struct C
  {
    static const int i = A<T>::i;  // { dg-error "incomplete" }
!   static const int j = i;      // { dg-error "initialized by a non-const" }
    B<j> b;  // { dg-error "not a valid template arg" }
  };
  
  C<int> c;
--- 9,21 ----
  template<int> struct B {};
  
  template<typename T> struct C
  {
    static const int i = A<T>::i;  // { dg-error "incomplete" }
!   static const int j = i;
    B<j> b;  // { dg-error "not a valid template arg" }
  };
  
  C<int> c;
+ 
+ int i = C<int>::i;
+ int j = C<int>::j;
Index: gcc/testsuite/g++.dg/template/static13.C
===================================================================
RCS file: gcc/testsuite/g++.dg/template/static13.C
diff -N gcc/testsuite/g++.dg/template/static13.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/g++.dg/template/static13.C	29 Aug 2005 06:32:40 -0000
***************
*** 0 ****
--- 1,14 ----
+ // PR c++/23099
+ 
+ struct Base {
+   int x;
+ };
+ 
+ template <typename T>
+ struct A {
+   static const int N = sizeof(static_cast<Base*>(T()));
+ };
+ 
+ struct Derived : Base {
+   A<Derived*> a;
+ };
Index: gcc/testsuite/g++.dg/template/static14.C
===================================================================
RCS file: gcc/testsuite/g++.dg/template/static14.C
diff -N gcc/testsuite/g++.dg/template/static14.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- gcc/testsuite/g++.dg/template/static14.C	29 Aug 2005 06:32:40 -0000
***************
*** 0 ****
--- 1,13 ----
+ struct Base {
+   int x;
+ };
+ 
+ template <typename T>
+ struct A {
+   static const int N = sizeof(static_cast<Base*>(T()));
+   int a[N];
+ };
+ 
+ struct Derived : Base {
+   A<Derived*> a;
+ };


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]