C++ PATCH for nasty mangling issue

Mark Mitchell mark@codesourcery.com
Tue Oct 19 10:48:00 GMT 1999


We discovered that in the following test-case:

  typedef enum {} i;
  
  template <int II>
  class Bar {};
  
  void f (Bar<21>, int) {}
  void f (Bar<2>, i) {}

both overloads of `f' mangled to the same name.  Ugh.  

Trying to compile this file yielded an ICE.  Putting the definitions
in separate files yielded a link-time clash.  Trying to debug an
executable containing one of these definitions caused GDB to crash on
startup.  Ugh, ugh, ugh.

Fixing this obvious requires breaking ABI compatibility; at least one
of the functions must get a different name.  Attached is a patch that
makes what I think is the minimal change; only functions whose types
involve non-type template parameters that require more than one digit
when mangled are affected.  Thus, I don't think that this change
should (in and of itself) prevent the mainline from being
ABI-compatible with G++ 2.95 for most real code.

It's a shame that we don't build the mangler and demangler
automatically from some formal grammar for mangling.  That would also
allow us to check for the possibility of clashes automatically.  Oh,
well.

While I was in the code, I also fixed a crash when using a g++
extension: real-valued template parameters could cause a crash during
name-mangling.

Demangler fixes follow.

--
Mark Mitchell                   mark@codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com

1999-10-19  Mark Mitchell  <mark@codesourcery.com>

	* method.c (PARM_CAN_BE_ARRAY_TYPE): Remove.
	(mangling_flags): New type.
	(build_overload_int): Change prototype.
	(build_overload_value): Likewise.
	(numeric_output_need_bar): Improve comment.
	(mangle_expression): New function, broken out from ...
	(build_overload_int): Here.
	(build_overload_value): Adjust for use of mangling flags.  Don't
	warn about real-valued template parameters here.  Do handle
	complex expressions involving real-valued template parameters.
	(build_template_parm_names): Encase non-type template parameters
	in underscores, if necessary.
	(process_overload_item): Remove conditional on
	PARM_CAN_BE_ARRAY_TYPE.

Index: testsuite/g++.old-deja/g++.ext/realpt1.C
===================================================================
RCS file: realpt1.C
diff -N realpt1.C
*** /dev/null	Tue May  5 13:32:27 1998
--- realpt1.C	Tue Oct 19 10:38:07 1999
***************
*** 0 ****
--- 1,19 ----
+ // Build don't link:
+ // Special g++ Options:
+ // Origin: Mark Mitchell <mark@codesourcery.com>
+ 
+ template <double d>
+ struct S;
+ 
+ template <double d, double e>
+ void f (S<d>*, S<e>*, S<d + e>*);
+ 
+ void g ()
+ {
+   S<2.0>* s1;
+   S<3.7>* s2;
+   S<5.7>* s3;
+   
+   f (s1, s2, s3);
+ }
+ 
Index: testsuite/g++.old-deja/g++.pt/mangle1.C
===================================================================
RCS file: mangle1.C
diff -N mangle1.C
*** /dev/null	Tue May  5 13:32:27 1998
--- mangle1.C	Tue Oct 19 10:38:08 1999
***************
*** 0 ****
--- 1,10 ----
+ // Build don't link:
+ // Origin: Mark Mitchell <mark@codesourcery.com>
+ 
+ typedef enum {} i;
+ 
+ template <int II>
+ class Bar {};
+ 
+ void f (Bar<21>, int) {}
+ void f (Bar<2>, i) {}
Index: cp/NEWS
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/NEWS,v
retrieving revision 1.21
diff -c -p -r1.21 NEWS
*** NEWS	1999/09/20 20:19:00	1.21
--- NEWS	1999/10/19 17:38:24
***************
*** 1,5 ****
--- 1,12 ----
  *** Changes in GCC 3.0:
  
+ * In some obscure cases, functions with the same type could have the
+   same mangled name.  This bug caused compiler crashes, link-time clashes,
+   and debugger crahses.  Fixing this bug required breaking ABI
+   compatibility for the functions involved.  The functions in questions
+   are those whose types involve non-type template arguments whose
+   mangled representations require more than one digit.
+ 
  * Support for assignment to `this' has been removed.  This idiom 
    was used in the very early days of C++, before users were allowed
    to overload `operator new'; it is no longer allowed by the C++
Index: cp/method.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/method.c,v
retrieving revision 1.124
diff -c -p -r1.124 method.c
*** method.c	1999/10/08 17:10:19	1.124
--- method.c	1999/10/19 17:38:29
*************** Boston, MA 02111-1307, USA.  */
*** 25,34 ****
  #define __inline
  #endif
  
- #ifndef PARM_CAN_BE_ARRAY_TYPE
- #define PARM_CAN_BE_ARRAY_TYPE 1
- #endif
- 
  /* Handle method declarations.  */
  #include "config.h"
  #include "system.h"
--- 25,30 ----
*************** Boston, MA 02111-1307, USA.  */
*** 44,49 ****
--- 40,63 ----
  #include "ggc.h"
  #include "tm_p.h"
  
+ /* Various flags to control the mangling process.  */
+ 
+ enum mangling_flags
+ {
+   /* No flags.  */
+   mf_none = 0,
+   /* The thing we are presently mangling is part of a template type,
+      rather than a fully instantiated type.  Therefore, we may see
+      complex expressions where we would normally expect to see a
+      simple integer constant.  */
+   mf_maybe_uninstantiated = 1,
+   /* When mangling a numeric value, use the form `_XX_' (instead of
+      just `XX') if the value has more than one digit.  */
+   mf_use_underscores_around_value = 2,
+ };
+ 
+ typedef enum mangling_flags mangling_flags;
+ 
  /* TREE_LIST of the current inline functions that need to be
     processed.  */
  struct pending_inline *pending_inlines;
*************** static int old_backref_index PROTO((tree
*** 61,70 ****
  static int flush_repeats PROTO((int, tree));
  static void build_overload_identifier PROTO((tree));
  static void build_overload_nested_name PROTO((tree));
! static void build_overload_int PROTO((tree, int));
  static void build_overload_identifier PROTO((tree));
  static void build_qualified_name PROTO((tree));
! static void build_overload_value PROTO((tree, tree, int));
  static void issue_nrepeats PROTO((int, tree));
  static char *build_mangled_name PROTO((tree,int,int));
  static void process_modifiers PROTO((tree));
--- 75,85 ----
  static int flush_repeats PROTO((int, tree));
  static void build_overload_identifier PROTO((tree));
  static void build_overload_nested_name PROTO((tree));
! static void mangle_expression PROTO((tree));
! static void build_overload_int PROTO((tree, mangling_flags));
  static void build_overload_identifier PROTO((tree));
  static void build_qualified_name PROTO((tree));
! static void build_overload_value PROTO((tree, tree, mangling_flags));
  static void issue_nrepeats PROTO((int, tree));
  static char *build_mangled_name PROTO((tree,int,int));
  static void process_modifiers PROTO((tree));
*************** do_inline_function_hair (type, friend_li
*** 197,204 ****
  /* Nonzero if we should not try folding parameter types.  */
  static int nofold;
  
! /* This appears to be set to true if an underscore is required to be
!    comcatenated before another number can be outputed. */
  static int numeric_output_need_bar;
  
  static __inline void
--- 212,219 ----
  /* Nonzero if we should not try folding parameter types.  */
  static int nofold;
  
! /* Nonzero if an underscore is required before adding a digit to the
!    mangled name currently being built.  */
  static int numeric_output_need_bar;
  
  static __inline void
*************** build_overload_scope_ref (value)
*** 546,649 ****
    build_overload_identifier (TREE_OPERAND (value, 1));
  }
  
! /* Encoding for an INTEGER_CST value.  */
  
  static void
! build_overload_int (value, in_template)
       tree value;
-      int in_template;
  {
!   if (in_template && TREE_CODE (value) != INTEGER_CST)
      {
!       if (TREE_CODE (value) == SCOPE_REF)
! 	{
! 	  build_overload_scope_ref (value);
! 	  return;
! 	}
  
!       OB_PUTC ('E');
!       numeric_output_need_bar = 0;
  
!       if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (value))))
  	{
! 	  int i;
! 	  int operands = tree_code_length[(int) TREE_CODE (value)];
! 	  tree id;
! 	  const char *name;
! 
! 	  id = ansi_opname [(int) TREE_CODE (value)];
! 	  my_friendly_assert (id != NULL_TREE, 0);
! 	  name = IDENTIFIER_POINTER (id);
! 	  if (name[0] != '_' || name[1] != '_')
! 	    /* On some erroneous inputs, we can get here with VALUE a
! 	       LOOKUP_EXPR.  In that case, the NAME will be the
! 	       identifier for "<invalid operator>".  We must survive
! 	       this routine in order to issue a sensible error
! 	       message, so we fall through to the case below.  */
! 	    goto bad_value;
  
! 	  for (i = 0; i < operands; ++i)
! 	    {
! 	      tree operand;
! 	      enum tree_code tc;
  
! 	      /* We just outputted either the `E' or the name of the
! 		 operator.  */
! 	      numeric_output_need_bar = 0;
! 
! 	      if (i != 0)
! 		/* Skip the leading underscores.  */
! 		OB_PUTCP (name + 2);
  
! 	      operand = TREE_OPERAND (value, i);
! 	      tc = TREE_CODE (operand);
  
! 	      if (TREE_CODE_CLASS (tc) == 't')
! 		/* We can get here with sizeof, e.g.:
  		     
! 		   template <class T> void f(A<sizeof(T)>);  */
! 		build_mangled_name_for_type (operand);
! 	      else if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (tc)))
! 		build_overload_int (operand, in_template);
! 	      else
! 		build_overload_value (TREE_TYPE (operand),
! 				      operand,
! 				      in_template);
! 	    }
  	}
!       else
! 	{
! 	  /* We don't ever want this output, but it's
! 	     inconvenient not to be able to build the string.
! 	     This should cause assembler errors we'll notice.  */
  	    
! 	  static int n;
! 	bad_value:
! 	  sprintf (digit_buffer, " *%d", n++);
! 	  OB_PUTCP (digit_buffer);
! 	}
  
!       OB_PUTC ('W');
!       numeric_output_need_bar = 0;
        return;
      }
  
    my_friendly_assert (TREE_CODE (value) == INTEGER_CST, 243);
!   if (TYPE_PRECISION (TREE_TYPE (value)) == 2 * HOST_BITS_PER_WIDE_INT)
      {
!       if (TREE_INT_CST_HIGH (value)
! 	  != (TREE_INT_CST_LOW (value) >> (HOST_BITS_PER_WIDE_INT - 1)))
! 	{
! 	  /* need to print a DImode value in decimal */
! 	  dicat (TREE_INT_CST_LOW (value), TREE_INT_CST_HIGH (value));
! 	  numeric_output_need_bar = 1;
! 	  return;
! 	}
!       /* else fall through to print in smaller mode */
      }
!   /* Wordsize or smaller */
!   icat (TREE_INT_CST_LOW (value));
!   numeric_output_need_bar = 1;
  }
  
  
--- 561,701 ----
    build_overload_identifier (TREE_OPERAND (value, 1));
  }
  
! /* VALUE is a complex expression.  Produce an appropriate mangling.
!    (We are forced to mangle complex expressions when dealing with
!    templates, and an expression involving template parameters appears
!    in the type of a function parameter.)  */
  
  static void
! mangle_expression (value)
       tree value;
  {
!   if (TREE_CODE (value) == SCOPE_REF)
      {
!       build_overload_scope_ref (value);
!       return;
!     }
  
!   OB_PUTC ('E');
!   numeric_output_need_bar = 0;
! 
!   if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (value))))
!     {
!       int i;
!       int operands = tree_code_length[(int) TREE_CODE (value)];
!       tree id;
!       const char *name;
! 
!       id = ansi_opname [(int) TREE_CODE (value)];
!       my_friendly_assert (id != NULL_TREE, 0);
!       name = IDENTIFIER_POINTER (id);
!       if (name[0] != '_' || name[1] != '_')
! 	/* On some erroneous inputs, we can get here with VALUE a
! 	   LOOKUP_EXPR.  In that case, the NAME will be the
! 	   identifier for "<invalid operator>".  We must survive
! 	   this routine in order to issue a sensible error
! 	   message, so we fall through to the case below.  */
! 	goto bad_value;
  
!       for (i = 0; i < operands; ++i)
  	{
! 	  tree operand;
! 	  enum tree_code tc;
  
! 	  /* We just outputted either the `E' or the name of the
! 	     operator.  */
! 	  numeric_output_need_bar = 0;
  
! 	  if (i != 0)
! 	    /* Skip the leading underscores.  */
! 	    OB_PUTCP (name + 2);
  
! 	  operand = TREE_OPERAND (value, i);
! 	  tc = TREE_CODE (operand);
  
! 	  if (TREE_CODE_CLASS (tc) == 't')
! 	    /* We can get here with sizeof, e.g.:
  		     
! 	       template <class T> void f(A<sizeof(T)>);  */
! 	    build_mangled_name_for_type (operand);
! 	  else
! 	    build_overload_value (TREE_TYPE (operand),
! 				  operand,
! 				  mf_maybe_uninstantiated);
  	}
!     }
!   else
!     {
!       /* We don't ever want this output, but it's
! 	 inconvenient not to be able to build the string.
! 	 This should cause assembler errors we'll notice.  */
  	    
!       static int n;
!     bad_value:
!       sprintf (digit_buffer, " *%d", n++);
!       OB_PUTCP (digit_buffer);
!     }
  
!   OB_PUTC ('W');
!   numeric_output_need_bar = 0;
! }
! 
! /* Encoding for an INTEGER_CST value.  */
! 
! static void
! build_overload_int (value, flags)
!      tree value;
!      mangling_flags flags;
! {
!   int multiple_words_p = 0;
!   int multiple_digits_p = 0;
! 
!   if ((flags & mf_maybe_uninstantiated) && TREE_CODE (value) != INTEGER_CST)
!     {
!       mangle_expression (value);
        return;
      }
  
+   /* Unless we were looking at an uninstantiated template, integers
+      should always be represented by constants.  */
    my_friendly_assert (TREE_CODE (value) == INTEGER_CST, 243);
! 
!   /* If the high-order word is not merely a sign-extension of the
!      low-order word, we must use a special output routine that can
!      deal with this.  */
!   if (TREE_INT_CST_HIGH (value)
!       != (TREE_INT_CST_LOW (value) >> (HOST_BITS_PER_WIDE_INT - 1)))
!     {
!       multiple_words_p = 1;
!       /* And there is certainly going to be more than one digit.  */
!       multiple_digits_p = 1;
!     }
!   else 
!     multiple_digits_p = (TREE_INT_CST_LOW (value) > 9
! 			 || TREE_INT_CST_LOW (value) < -9);
! 
!   /* If necessary, add a leading underscore.  */
!   if (multiple_digits_p && (flags & mf_use_underscores_around_value))
!     OB_PUTC ('_');
! 
!   /* Output the number itself.  */
!   if (multiple_words_p)
!     dicat (TREE_INT_CST_LOW (value), TREE_INT_CST_HIGH (value));
!   else
!     icat (TREE_INT_CST_LOW (value));
! 
!   if (flags & mf_use_underscores_around_value)
      {
!       if (multiple_digits_p)
! 	OB_PUTC ('_');
!       /* Whether or not there were multiple digits, we don't need an
! 	 underscore.  We've either terminated the number with an
! 	 underscore, or else it only had one digit.  */
!       numeric_output_need_bar = 0;
      }
!   else
!     /* We just output a numeric value.  */
!     numeric_output_need_bar = 1;
  }
  
  
*************** build_mangled_C9x_name (bits)
*** 701,709 ****
  #endif
  
  static void
! build_overload_value (type, value, in_template)
       tree type, value;
!      int in_template;
  {
    my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (type)) == 't', 0);
  
--- 753,761 ----
  #endif
  
  static void
! build_overload_value (type, value, flags)
       tree type, value;
!      mangling_flags flags;
  {
    my_friendly_assert (TREE_CODE_CLASS (TREE_CODE (type)) == 't', 0);
  
*************** build_overload_value (type, value, in_te
*** 738,761 ****
        build_overload_identifier (DECL_NAME (value));
        return;
      }
  
    switch (TREE_CODE (type))
      {
-     case INTEGER_TYPE:
-     case ENUMERAL_TYPE:
-     case BOOLEAN_TYPE:
-       {
- 	build_overload_int (value, in_template);
- 	return;
-       }
      case REAL_TYPE:
        {
  	REAL_VALUE_TYPE val;
  	char *bufp = digit_buffer;
  
! 	pedwarn ("ANSI C++ forbids floating-point template arguments");
  
- 	my_friendly_assert (TREE_CODE (value) == REAL_CST, 244);
  	val = TREE_REAL_CST (value);
  	if (REAL_VALUE_ISNAN (val))
  	  {
--- 790,820 ----
        build_overload_identifier (DECL_NAME (value));
        return;
      }
+   else if (INTEGRAL_TYPE_P (type))
+     {
+       build_overload_int (value, flags);
+       return;
+     }
+ 
+   /* The only case where we use the extra underscores here is when
+      forming the mangling for an integral non-type template argument.
+      If that didn't happen, stop now.  */
+   flags &= ~mf_use_underscores_around_value;
  
    switch (TREE_CODE (type))
      {
      case REAL_TYPE:
        {
  	REAL_VALUE_TYPE val;
  	char *bufp = digit_buffer;
  
! 	/* We must handle non-constants in templates.  */
! 	if (TREE_CODE (value) != REAL_CST)
! 	  {
! 	    mangle_expression (value);
! 	    break;
! 	  }
  
  	val = TREE_REAL_CST (value);
  	if (REAL_VALUE_ISNAN (val))
  	  {
*************** build_overload_value (type, value, in_te
*** 817,823 ****
      case POINTER_TYPE:
        if (TREE_CODE (value) == INTEGER_CST)
  	{
! 	  build_overload_int (value, in_template);
  	  return;
  	}
        else if (TREE_CODE (value) == TEMPLATE_PARM_INDEX)
--- 876,882 ----
      case POINTER_TYPE:
        if (TREE_CODE (value) == INTEGER_CST)
  	{
! 	  build_overload_int (value, flags);
  	  return;
  	}
        else if (TREE_CODE (value) == TEMPLATE_PARM_INDEX)
*************** build_overload_value (type, value, in_te
*** 877,885 ****
  	my_friendly_assert (TREE_CODE (value) == PTRMEM_CST, 0);
  
  	expand_ptrmemfunc_cst (value, &delta, &idx, &pfn, &delta2);
! 	build_overload_int (delta, in_template);
  	OB_PUTC ('_');
! 	build_overload_int (idx, in_template);
  	OB_PUTC ('_');
  	if (pfn)
  	  {
--- 936,944 ----
  	my_friendly_assert (TREE_CODE (value) == PTRMEM_CST, 0);
  
  	expand_ptrmemfunc_cst (value, &delta, &idx, &pfn, &delta2);
! 	build_overload_int (delta, flags);
  	OB_PUTC ('_');
! 	build_overload_int (idx, flags);
  	OB_PUTC ('_');
  	if (pfn)
  	  {
*************** build_overload_value (type, value, in_te
*** 890,896 ****
  	else
  	  {
  	    OB_PUTC ('i');
! 	    build_overload_int (delta2, in_template);
  	  }
        }
        break;
--- 949,955 ----
  	else
  	  {
  	    OB_PUTC ('i');
! 	    build_overload_int (delta2, flags);
  	  }
        }
        break;
*************** build_template_parm_names (parmlist, arg
*** 983,989 ****
  	  /* It's a PARM_DECL.  */
  	  build_mangled_name_for_type (TREE_TYPE (parm));
  	  build_overload_value (TREE_TYPE (parm), arg, 
! 				uses_template_parms (arglist));
  	}
      }
   }
--- 1042,1050 ----
  	  /* It's a PARM_DECL.  */
  	  build_mangled_name_for_type (TREE_TYPE (parm));
  	  build_overload_value (TREE_TYPE (parm), arg, 
! 				((mf_maybe_uninstantiated 
! 				  * uses_template_parms (arglist))
! 				 | mf_use_underscores_around_value));
  	}
      }
   }
*************** process_overload_item (parmtype, extra_G
*** 1317,1323 ****
        goto more;
  
      case ARRAY_TYPE:
- #if PARM_CAN_BE_ARRAY_TYPE
        {
          OB_PUTC ('A');
          if (TYPE_DOMAIN (parmtype) == NULL_TREE)
--- 1378,1383 ----
*************** process_overload_item (parmtype, extra_G
*** 1337,1346 ****
  	  OB_PUTC ('_');
          goto more;
        }
- #else
-       OB_PUTC ('P');
-       goto more;
- #endif
  
      case POINTER_TYPE:
        OB_PUTC ('P');
--- 1397,1402 ----


More information about the Gcc-patches mailing list