From mboxrd@z Thu Jan 1 00:00:00 1970 From: Carlo Wood To: nobody@gcc.gnu.org Cc: gcc-prs@gcc.gnu.org Subject: c++/3211: Incorrect, substitution related mangling. Date: Sun, 17 Jun 2001 10:06:00 -0000 Message-id: <20010617170600.7560.qmail@sourceware.cygnus.com> X-SW-Source: 2001-06/msg00715.html List-Id: The following reply was made to PR c++/3211; it has been noted by GNATS. From: Carlo Wood To: gcc-bugs@gcc.gnu.org Cc: gcc-gnats@gcc.gnu.org Subject: c++/3211: Incorrect, substitution related mangling. Date: Sun, 17 Jun 2001 18:59:39 +0200 I am afraid this is going to be a long one. The background is that I've been working on a demangler for several weeks now, using g++-3.0 (pre) and the demangler in libiberty for comparision (without looking at the source). Now I've come to a point where start to find bugs in g++ instead of in my own demangler ;). PS If you don't have time to read it all, there is a CONCLUSION half way. I've used http://reality.sgi.com/dehnert_engr/cxx/abi.html#mangling-type as reference for writing my demangler. --- This reference defines ::= M being a itself, it is subject to substitution. Moreover, " " being a , both " " and "" are subject to substitution. For clarity, let be an unqualified type below. So we have to write: ::= [] M [] [] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer-to-member-type ____/ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ class type ____/ member type ____/ and subsitution members are: M M As an example, has qualifiers in this case: -------------------- struct A { int volatile member; }; int volatile (A::*member_pointer) = &A::member; -------------------- We can use the following test program to find what g++-3.0 makes of it: -------------------- #include #include struct A { int volatile member; }; int volatile (A::*member_pointer) = &A::member; int main(void) { std::cout << typeid(member_pointer).name() << '\n'; return 0; } -------------------- which gives: M1AVi Now note that "A const::*" is incorrect C++. The following test case: -------------------- #include #include struct A { int volatile member; }; template struct const_test { static int volatile (T::*member_pointer); }; template int volatile (T::*const_test::member_pointer) = &T::member; int main(void) { std::cout << typeid(const_test::member_pointer).name() << '\n'; return 0; } -------------------- Still return "M1AVi", because the 'const' is simply dropped. The only way to get a qualifier before is when is a member *function* that is `const'. Please not that even: -------------------- #include #include struct A { int member(void) { } }; template struct const_test { typedef int (member_type)(void); member_type (T::*member_pointer); }; int main(void) { std::cout << typeid(const_test::member_pointer).name() << '\n'; return 0; } -------------------- Returns "MK1AFivE", where "1A" is and "FivE" is . There is no 'const' qualifier for "1A". This seems to indicate that qualifiers directly after the "M" really belong to the member *function*. For example: -------------------- #include #include struct A { int member(void) const { } }; int (A::*member_pointer)(void) const = &A::member; int main(void) { std::cout << typeid(member_pointer).name() << '\n'; return 0; } -------------------- returns "MK1AFivE", with a 'K' in front of the -- which as we saw before really can't BE there *unless* is a function. [ Note that this is very equivalent with how qualifiers work with nested functions, for example: -------------------- struct A { int f1(float); int f2(float) const volatile; }; int A::f1(float) { } int A::f2(float) const volatile { } -------------------- results in the mangled names: 00000000 T _ZN1A2f1Ef 00000010 T _ZNVK1A2f2Ef very similar like -------------------- struct A; void f1p(int (A::*)(float)) { } void f2p(int (A::*)(float) const volatile) { } -------------------- results in the mangled names 00000000 T _Z3f1pM1AFifE 00000020 T _Z3f2pMVK1AFifE ] Imho, the draft is wrong and should have defined like it defines : ::= M [CV-qualifiers] stripping off qualifiers that otherwise *errornous* seem to belong to the class type (which is not possible). Another argument for this is found by looking at what happens when is a itself. For example: -------------------- #include #include struct A { struct B { int member(void) const { } }; }; int (A::B::*member_pointer)(void) const = &A::B::member; int main(void) { std::cout << typeid(member_pointer).name() << '\n'; return 0; } -------------------- prints "MKN1A1BEFivE", as it should (imho) and NOT "MN1AK1BEFivE", or "MNK1A1BEFivE" for that matter. The demangler in libiberty is already *heavily* broken concerning qualifiers (as I reported before), but how does the above affect the mangling of g++? It does because it has effect on the used substitutions, after all - when using ::= M [CV-qualifiers] the resulting substitutions would be: <-- unqualified, even when being a M M And " " is gone from the list. Now before you say 'yes, but this is at most a bug in the reference and not in g++', let me point out an inconsistency in the reference related to this. It states: "substitutable components are the represented symbolic constructs, not their associated mangling character strings". This would mean that a substitution for " " would result in storing and re-using a string like "A::B const", but it is possible to get: MS3_FivE for example, where S3_ represents the mentioned qualified . Following the reference literally we'd HAVE to demangle that as: "void (A::B const::*)(int)" where the string "A::B const" (S3_) appears literally. ========== CONCLUSION ========== This is why I am convinced that this substitution is wrong, it shouldn't exist. The correct way to mangle the above is as follows: with for example: S2_ == "A::B" S3_ == "A::B const" "void (A::B::*)(int) const" should mangle as "MKS2_FviE" and not as "MS3_FviE". g++-3.0 makes of this: ---------------------- struct A { struct B { int member(void) const { } static int (A::B::* const member_pointer)(void) const = &A::B::member; void foo(A::B const*, typeof(member_pointer)); }; }; void A::B::foo(A::B const*, typeof(A::B::member_pointer)) { } ---------------------- where 'A::B::foo' is mangled as "_ZN1A1B3fooEPKS0_MS1_FivE". this should be "_ZN1A1B3fooEPKS0_MKS0_FivE" imho. -- Carlo Wood ===================================================================================== PS Note that the demangler in libiberty chokes on this as well: /usr/src/gcc/gcc-cvs-3.0/libiberty>c++filt _ZN1A1B3fooEPKS0_MKS0_FivE -> mangled-name at position 0 -> encoding at position 2 -> name at position 2 -> nested-name at position 2 -> prefix at position 3 -> unqualified-name at position 3 -> source-name at position 3 -> number at position 3 -> number* at position 3 -> identifier at position 4 SUBSTITUTIONS: S_ : A -> unqualified-name at position 5 -> source-name at position 5 -> number at position 5 -> number* at position 5 -> identifier at position 6 SUBSTITUTIONS: S_ : A S0_ : A::B -> unqualified-name at position 7 -> source-name at position 7 -> number at position 7 -> number* at position 7 -> identifier at position 8 -> bare-function-type at position 12 -> type at position 12 -> type* at position 12 -> type* at position 13 -> type at position 13 -> CV-qualifiers at position 13 -> type at position 14 -> substitution at position 14 -> number at position 15 -> number* at position 15 SUBSTITUTIONS: S_ : A S0_ : A::B S1_ : A::B const SUBSTITUTIONS: S_ : A S0_ : A::B S1_ : A::B const S2_ : A::B const* -> type at position 17 -> type* at position 17 -> type at position 18 -> CV-qualifiers at position 18 -> type at position 19 -> substitution at position 19 -> number at position 20 -> number* at position 20 SUBSTITUTIONS: S_ : A S0_ : A::B S1_ : A::B const S2_ : A::B const* S3_ : A::B const -> type* at position 22 -> function-type at position 22 -> bare-function-type at position 23 -> type at position 23 -> builtin-type at position 23 SUBSTITUTIONS: S_ : A S0_ : A::B S1_ : A::B const S2_ : A::B const* S3_ : A::B const S4_ : int ()() SUBSTITUTIONS: S_ : A S0_ : A::B S1_ : A::B const S2_ : A::B const* S3_ : A::B const S4_ : int ()() S5_ : int (A::B const::*)() A::B::foo(A::B const*, int (A::B const::*)()) Where "A::B const" appears TWICE in the substitution list, which is a definite bug. Moreover, I am not convinved that "int ()()" should be there (it can neither be used as a literal replacement; same argument). Finally, the "A::B const::*" is syntactical nonsense.