Proposed ABI changes for new C++0x SFINAE rules

David Vandevoorde daveed at edg.com
Wed Jun 30 15:33:44 UTC 2010


Hi all,

(I sent this e-mail with the wrong attachment last night.  I don't think it made it through the mailing list software, but if it does eventually, please ignore it.)

We've been implementing the new C++0x SFINAE rules (as you probably guessed from recent traffic on this list), and it turns out that it requires ABI changes in the mangling of template signatures.  Often these are pure extensions (reflecting the fact that more kinds of expressions can appear in the signatures), but some of the changes are backward incompatible.  Fortunately, the incompatible changes affect code that is highly unlikely in C++03 (and probably also somehwat unlikely in C++0x).

Attached are our proposed diffs for the ABI document.  Let me walk through its various aspects.

- We're introducing a new definition: "instantiation-dependent".  When encoding a signature that contains a non-dependent expression like "sizeof(int)", existing practice it to just replace that by the resulting value, or in some cases, by the encoding for a literal representing that value.  For example:

	template<class T, int N> struct S {};
	template<class T> void f(S<T, sizeof(int)>);
	  // "sizeof(int)" is encoded as "Li4E" on typical platforms;
	  // i.e., as if we'd written "4" instead.

Similarly, something like "decltype(int)" is just encoded as the known underlying type.

However, there are non-dependent expressions and types that can still fail instantiation.  With the new SFINAE rules, several template declarations differing only in such expressions can be written such that their encodings don't collide.  So reducing a sizeof/decltype that is applied to such an expression to a literal isn't workable.  Here is an example of the idea:

	template<class T> auto f(T *p)->decltype(sizeof(sizeof(T)));

"sizeof(sizeof(T))" is neither type-dependent nor value-dependent, but if T is e.g. a function type, that declaration will be "SFINAEed out".  

So an "instantiation-dependent" expression or type is one that is either type-dependent or value-dependent, or one that contains a type-dependent or value-dependent sub-expression.  decltype, sizeof, and alignof must be fully encoded if (and only if) their operand is "instantiation-dependent"; otherwise, the resulting type/value is encoded instead.


- Since "auto" can appear as a type specifier in new-expressions, we introduce "Da" for its encoding.


- A number of new codes are introduced for expression operators that were not previously expected in template signatures (e.g., new/delete operators, typeid, throw, etc.).  We also added codes to distinguish prefix and postfix increment/decrement operators (since e.g. "decltype(x++)" is different from "decltype(++x)"). See the patch for details.


- Explicit casts (previously always encoded with the generic "cv" code) now get different codes depending on the form used in the source.  This is needed, because two templates can now validly differ only in that respect (and so manglings must not collide).  For example:

	template<class T, int N> void f(int (*)[const_cast<T>(N)]);    // #1
	template<class T, int N> void f(int (*)[static_cast<T>(N)]);   // #2

	int main() {
	  f<long, 3>(0);  // Calls #2 since const_cast is invalid
	}

This is unfortunately a backward incompatible change (but as mentioned before, the code is unlikely in C++03 since the SFINAE rules for this were unclear at best).


- The encoding of "unresolved names" (e.g., the second operand of a "->" or "." member selection operation) is now more thorough.  Previously, <expression> had the following productions:

            ::= sr <type> <unqualified-name>                    # dependent name
            ::= sr <type> <unqualified-name> <template-args>    # dependent template-id

e.g. for the name "T::x" in "p->T::x" (where T is a template parameter).  In the new SFINAE world, however, this is insufficient. The qualifier may not be a resolved type (or a type at all?) and whether it instantiates successfully may depend on its exact form. E.g., "p->X::y" and "p->::X::y" must mangle differently (we're introducing a "gs" operator for that; it also applies to new/delete encodings).  In fact, this matters even for names that don't include a qualifier ("p->x" vs. "p->::x").  (For more analysis, see my mail titled "Member selections on dependent expressions" posted on this reflector last week.)  After looking at lots of examples, we ended up with the following:

	<expression> ::= <unresolved-name>

	<unresolved-name> ::= [gs] <base-unresolved-name>                # x or (with "gs") ::x
	                  ::= sr <unresolved-type> <base-unresolved-name>
	                                                                 # T::x / decltype(p)::x
	                  ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
	                                                                 # T::N::x /decltype(p)::N::x
	                  ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
	                                                                 # A::x, N::y, A<T>::z; "gs" means leading "::"
	<unresolved-type> ::= <template-param>
	                  ::= <decltype>

	<unresolved-qualifier-level> ::= <source-name> [ <template-args> ]

	<base-unresolved-name> ::= <source-name>                         # unresolved name
	                       ::= <source-name> <template-args>         # unresolved template-id
	                       ::= on <operator-name>                    # unresolved operator-function-id
	                       ::= on <operator-name> <template-args>    # unresolved operator template-id
	                       ::= dn <unresolved-name>                  # destructor name; e.g. ~X or ~T::X

This is backward incompatible for cases like p->A::x where the name qualifier is not a template parameter (or decltype operator; though that case was not specified at all in the past), but it is backward compatible for the more common unqualified case (p->x or p.x).

Note also the "on" prefix for operator names may not be strictly needed (but it wouldn't take much for the language to evolve in a way that would create ambiguities without it; even now, we're not 100% sure), but it appears to be existing (GCC) practice (and it simplifies demangling).  For example:

	template <class T> auto g(T p1) -> decltype(operator+(p1,p1));
	  // The decltype operation is encoded as "DTclonplfp_fp_EE"

Finally, let me mention that I've raised an issue with the C++ committee about p.T::x with T a template parameter possibly becoming ambiguous if the type of p is a class type with a member named T.  The proposal here assumes that the language will change to remove that ambiguity: If not (which leads to some bizarre language effects), the encoding rules would have to change.


- We propose additional encodings for literals that can now appear in signatures.  nullptr is "LDn0E"; i.e., "a zero of type std::nullptr_t".  More interestingly, string literals are encoded as L<character type>E, where <character type> is the encoding of the (unqualified) underlying character type.  This has a few consequences for the demangler: It cannot actually reproduce the string, and it can only distinguish character literals from string literals after having seen the first character following the character type code.


- The patch includes an encoding for parenthesized initializers (code "pi") in new-expressions (see the production for <initializer>).  It's leaving the door open for braced initializers, but since we haven't implemented those yet, we're not proposing the encoding at this time.  Similarly, since we don't implement complex integers, we haven't specified their literals, but I think the floating-point case we do specify gives a clear direction for that.


- The patch includes a number of minor improvement/corrections.  E.g., it is now much more explicit that when mangling the types of function template instantiations, the encoded function type is that of the template (i.e., before substitutions).  Also, some examples were added.


As always, we're looking for feedback/corrections.

Thanks,

	Daveed Vandevoorde
	Edison Design Group


-------------- next part --------------
A non-text attachment was scrubbed...
Name: SFINAE_diffs
Type: application/octet-stream
Size: 32735 bytes
Desc: not available
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20100630/c00ffdaf/attachment.obj>


More information about the cxx-abi-dev mailing list