diff mbox

C++ PATCH to correct mangling/demangling of cv-qualified function types

Message ID 515CC647.1090106@redhat.com
State New
Headers show

Commit Message

Jason Merrill April 4, 2013, 12:16 a.m. UTC
While I was working on the ref-qualifier mangling, I noticed that we 
were handling function-cv-quals wrong; those quals should be considered 
an indivisible part of the function type rather than a separate qualifier.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 6af1b337883b2c88e6411f24d8cb7bf2a76ea94e
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 3 12:37:41 2013 -0400

    libiberty/
    	* cp-demangle.c (cplus_demangle_type): Fix function quals.
    	(d_pointer_to_member_type): Simplify.
    gcc/cp/
    	* mangle.c (write_type): When writing a function type with
    	function-cv-quals, don't add the unqualified type as a
    	substitution candidate.

diff --git a/gcc/common.opt b/gcc/common.opt
index bdbd3b6..e02e7ed 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -792,6 +792,11 @@  Driver Undocumented
 ; 7: The version of the ABI that treats nullptr_t as a builtin type and
 ;    corrects the mangling of lambdas in default argument scope.
 ;    First selectable in G++ 4.8.
+;
+; 8: The version of the ABI that corrects the substitution behavior of
+;    function types with function-cv-qualifiers.
+;    First selectable in G++ 4.9.
+;
 ; Additional positive integers will be assigned as new versions of
 ; the ABI become the default version of the ABI.
 fabi-version=
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 4e68c51..83c3e62 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -1897,8 +1897,16 @@  write_type (tree type)
       tree t = TYPE_MAIN_VARIANT (type);
       if (TREE_CODE (t) == FUNCTION_TYPE
 	  || TREE_CODE (t) == METHOD_TYPE)
-	t = build_ref_qualified_type (t, type_memfn_rqual (type));
-      write_type (t);
+	{
+	  t = build_ref_qualified_type (t, type_memfn_rqual (type));
+	  if (abi_version_at_least (8))
+	    /* Avoid adding the unqualified function type as a substitution.  */
+	    write_function_type (t);
+	  else
+	    write_type (t);
+	}
+      else
+	write_type (t);
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)
     /* It is important not to use the TYPE_MAIN_VARIANT of TYPE here
diff --git a/gcc/testsuite/g++.dg/abi/mangle62.C b/gcc/testsuite/g++.dg/abi/mangle62.C
new file mode 100644
index 0000000..6dbfd78
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle62.C
@@ -0,0 +1,11 @@ 
+// Before v8, we mistakenly treated an unqualified function type
+// as a substitution candidate for a function type with function-cv-quals.
+// Test for the conformant behavior.
+
+// { dg-options -fabi-version=0 }
+
+template <class T, class U> struct A { };
+// { dg-final { scan-assembler "_Z1fP1AIKFvvEFvvEE" } }
+void f (A<void()const, void()> *){}
+// { dg-final { scan-assembler "_Z1gP1AIFvvEKFvvEE" } }
+void g (A<void(), void()const> *){}
diff --git a/gcc/testsuite/g++.dg/abi/mangle62a.C b/gcc/testsuite/g++.dg/abi/mangle62a.C
new file mode 100644
index 0000000..fca1cb6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle62a.C
@@ -0,0 +1,11 @@ 
+// Before v8, we mistakenly treated an unqualified function type
+// as a substitution candidate for a function type with function-cv-quals.
+// Test for that for backward compatibility.
+
+// { dg-options -fabi-version=7 }
+
+template <class T, class U> struct A { };
+// { dg-final { scan-assembler "_Z1fP1AIKFvvES0_E" } }
+void f (A<void()const, void()> *){}
+// { dg-final { scan-assembler "_Z1gP1AIFvvEKS0_E" } }
+void g (A<void(), void()const> *){}
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 271d3d3..70f5438 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -2198,8 +2198,16 @@  cplus_demangle_type (struct d_info *di)
       pret = d_cv_qualifiers (di, &ret, 0);
       if (pret == NULL)
 	return NULL;
-      *pret = cplus_demangle_type (di);
-      if (! *pret)
+      if (d_peek_char (di) == 'F')
+	{
+	  /* cv-qualifiers before a function type apply to 'this',
+	     so avoid adding the unqualified function type to
+	     the substitution list.  */
+	  *pret = d_function_type (di);
+	}
+      else
+	*pret = cplus_demangle_type (di);
+      if (!*pret)
 	return NULL;
       if ((*pret)->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
 	  || (*pret)->type == DEMANGLE_COMPONENT_REFERENCE_THIS)
@@ -2739,53 +2747,32 @@  d_pointer_to_member_type (struct d_info *di)
 {
   struct demangle_component *cl;
   struct demangle_component *mem;
-  struct demangle_component **pmem;
 
   if (! d_check_char (di, 'M'))
     return NULL;
 
   cl = cplus_demangle_type (di);
-
-  /* The ABI specifies that any type can be a substitution source, and
-     that M is followed by two types, and that when a CV-qualified
-     type is seen both the base type and the CV-qualified types are
-     substitution sources.  The ABI also specifies that for a pointer
-     to a CV-qualified member function, the qualifiers are attached to
-     the second type.  Given the grammar, a plain reading of the ABI
-     suggests that both the CV-qualified member function and the
-     non-qualified member function are substitution sources.  However,
-     g++ does not work that way.  g++ treats only the CV-qualified
-     member function as a substitution source.  FIXME.  So to work
-     with g++, we need to pull off the CV-qualifiers here, in order to
-     avoid calling add_substitution() in cplus_demangle_type().  But
-     for a CV-qualified member which is not a function, g++ does
-     follow the ABI, so we need to handle that case here by calling
-     d_add_substitution ourselves.  */
-
-  pmem = d_cv_qualifiers (di, &mem, 1);
-  if (pmem == NULL)
-    return NULL;
-  *pmem = cplus_demangle_type (di);
-  if (*pmem == NULL)
+  if (cl == NULL)
     return NULL;
 
-  if (pmem != &mem
-      && ((*pmem)->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
-	  || (*pmem)->type == DEMANGLE_COMPONENT_REFERENCE_THIS))
-    {
-      /* Move the ref-qualifier outside the cv-qualifiers so that
-	 they are printed in the right order.  */
-      struct demangle_component *fn = d_left (*pmem);
-      d_left (*pmem) = mem;
-      mem = *pmem;
-      *pmem = fn;
-    }
+  /* The ABI says, "The type of a non-static member function is considered
+     to be different, for the purposes of substitution, from the type of a
+     namespace-scope or static member function whose type appears
+     similar. The types of two non-static member functions are considered
+     to be different, for the purposes of substitution, if the functions
+     are members of different classes. In other words, for the purposes of
+     substitution, the class of which the function is a member is
+     considered part of the type of function."
 
-  if (pmem != &mem && (*pmem)->type != DEMANGLE_COMPONENT_FUNCTION_TYPE)
-    {
-      if (! d_add_substitution (di, mem))
-	return NULL;
-    }
+     For a pointer to member function, this call to cplus_demangle_type
+     will end up adding a (possibly qualified) non-member function type to
+     the substitution table, which is not correct; however, the member
+     function type will never be used in a substitution, so putting the
+     wrong type in the substitution table is harmless.  */
+
+  mem = cplus_demangle_type (di);
+  if (mem == NULL)
+    return NULL;
 
   return d_make_comp (di, DEMANGLE_COMPONENT_PTRMEM_TYPE, cl, mem);
 }
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index ed73245..1259e4a 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -4273,8 +4273,8 @@  foo
 #
 --format=gnu-v3 --no-params
 _Z1fIKFvvES0_Evv
-void f<void () const, void ()>()
-f<void () const, void ()>
+void f<void () const, void () const>()
+f<void () const, void () const>
 #
 --format=gnu-v3
 _ZN4modc6parser8sequenceINS_9astParser13LocatedParserINS0_9ParserRefINS2_UlRNS2_16TokenParserInputEE_EEEEEINS0_14OptionalParserINS2_18ListParserTemplateILNS_6tokens5Token4TypeE4EXadL_ZNSD_Ut_13parenthesizedEEEE6ParserINS4_INS0_6ParserIS5_NS_3ast10ExpressionEEEEEEEEENSA_INS4_INS2_22OneOfKeywordsToTParserINSJ_5StyleEEEEEEENS0_14SequenceParserIS5_INS0_18ExactElementParserIS5_EENSA_ISM_EEEEENS0_14RepeatedParserINS4_INS0_15TransformParserINSU_IS5_INS4_INSP_INSJ_10Annotation12RelationshipEEEEESX_EEENS2_UlNS2_3LocES12_ONS_5MaybeISK_EEE19_EEEEELb0EEEEEENSU_INS0_17ExtractParserTypeIT_E9InputTypeEINS0_8MaybeRefIS1F_E4TypeEDpNS1I_IT0_E4TypeEEEEOS1F_DpOS1L_