From c071d0a4cd3165a069c6f4142d41f1a9ddfa7896 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 May 2023 12:43:12 -0400 Subject: [PATCH] Fully specify the mangling of non-type template arguments. This includes the material from #47 (non-type template arguments), #63 (class constants), and the template-param-decl portions of #85 (C++20 lambda-expressions). @zygoloid gets credit for most of this, although I've made a few substantive changes from his suggestions. --- abi.html | 559 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 403 insertions(+), 156 deletions(-) diff --git a/abi.html b/abi.html index 8b4e2c2..e8f42f8 100644 --- a/abi.html +++ b/abi.html @@ -5418,7 +5418,7 @@
5.1.5.7 Pointer-to-member types produces the mangled name "_Z1fM1AKFvvRE". -
5.1.5.8 Template parameters
+
5.1.5.8 Template parameter references

A reference to a template parameter is mangled using the index @@ -5535,9 +5535,7 @@

5.1.5.10 Template Arguments

Template argument lists appear after the unqualified template name, and are bracketed by I/E. This is used in names for specializations -in particular, but also in types and scope identification. Template -argument packs are bracketed by J/E to distinguish them from other -arguments. +in particular, but also in types and scope identification.


   <template-args> ::= I <template-arg>+ E
@@ -5546,6 +5544,7 @@ 
5.1.5.10 Template Arguments
::= X <expression> E # expression ::= <expr-primary> # simple expressions ::= J <template-arg>* E # argument pack + ::= <template-param-decl> <template-arg> # converted template argument

@@ -5557,180 +5556,136 @@

5.1.5.10 Template Arguments
N1AIT0_E1XE.

-Template type arguments and template template arguments are mangled -using their regular encoding. For example, the class template +The mangling of template arguments in dependent contexts depends on +whether the template argument can be matched with a template parameter +and, if so, whether the argument can be checked against the parameter. +In general, mangling assumes that all possible matching and checking +has been performed. The conditions are not precisely laid out here, +but matching is generally inhibited by an unresolved template name +and in some situations with unexpanded pack expansions, while checking +is generally inhibited by dependent arguments or parameters. +In non-dependent contexts (in particular, in a top-level +<encoding>), template arguments should always be +fully matched and checked against the template parameters of the +specialized template. + +

+Under the ODR, function templates are distinguished by differences +in their template head, such as a constraint on a template type +parameter, the type of a non-type template parameter, or (recursively) +the template head of a template template parameter. The template +parameter list is not directly included in the +<encoding> of a function template specialization +(except for the operator() of a generic lambda), +but it is indirectly reflected in the mangling of the template +arguments as follows. A template parameter is said to be natural +for a template argument that has been matched and checked against +it if: +

+ +

+The type of a non-type template parameter is said to contain deduced +types if it is written with a placeholder type (auto or +decltype(auto)) or a placeholder for a deduced class type +(e.g. container when that is a template with deducible +template parameters). + +

+When mangling the template arguments of a function template that +is not the operator() of a lambda, if a template +argument has been matched and checked against a template parameter +that is not natural for the template argument, a +<template-param-decl> must be prefixed to the +template argument. Earlier versions of this ABI did not call for this +and so could fail to distinguish templates that must be distinguished +under the ODR. This is not done for class templates in order to +reduce ABI incompatibility, and in particular to allow class templates +to adopt constraints without breaking ABI. It is not done for +generic lambdas because the template parameter list is already +encoded in the <lambda-sig>. + +

+If a template parameter pack can be matched to a template argument +pack, the pack is bracketed with J...E +to distinguish it from other arguments. This is done even if some +of the arguments in the pack cannot be checked against the parameter. +If a <template-param-decl> is required for the +parameter, it is mangled immediately prior to the entire pack +(the J), not before the individual +arguments in the pack. + +

+A template type argument or template template argument is mangled +as a type in the usual way. For example, the class template specialization A<char, float> is encoded as 1AIcfE.

-A non-type template argument is considered dependent if either the -argument expression is instantiation-dependent or it is not matched -with a template parameter of non-dependent type. (The latter can -happen if, for example, the template name is dependent and not a -template parameter.) If so, it is mangled as an expression in the -usual way for a dependent mangling. -Otherwise, it is converted to the template parameter type, -constant-evaluated, and then mangled as a value. For example: +If a non-type template argument cannot be matched with a parameter, +or if it cannot be checked against that parameter, or if the +argument expression is instantiation-dependent, it is mangled +as an expression in the usual way for a +dependent mangling. Otherwise, +the argument is converted to the parameter type, constant-evaluated, +and then mangled as an expression representing that value. Precise typing is required when mangling +this constant if: +

+ +

+For example:

-template  class A;
+template <class T, T value> class A;
 
 template <class U, template <class T, T N> class Temp> void f(Temp<U, 4+5>) {}
 
-f    // _Z1fIj1AEvT0_IT_XplLi4ELi5EEE
+f<unsigned, A>    // _Z1fIj1AEvT0_IT_XplLi4ELi5EEE
 // The template parameter type is dependent, and so the template
 // argument is mangled as a dependent expression without any
 // constant-folding.
 
 template <template <class T, T N> class Temp> void g(Temp<unsigned, 4+5>) {}
-g    // _Z1gI1AEvT_IjLj9EE
+g<A>    // _Z1gI1AEvT_IjLj9EE
 // The template parameter type is not dependent, and so the template
 // argument is converted to unsigned and constant-evaluated.
 
-

-Non-type template argument values of integer, enumerated, -floating-point, or complex type are mangled as -literals of the template parameter -type. Note that this can produce combinations which cannot normally -be written in the source language, such as a literal of -short type. - -

-Non-type template argument values of pointer or member pointer type -that are null are mangled as as a literal 0 of the -template parameter type. For example: - -

-struct A;
-template <void (A::*)()> void f();
-
-f<nullptr> // _Z1fILM1AFvvE0EEvv
-
- -

-Non-type template argument values of reference or pointer type -are mangled using the subobject -specifier expression for the argument. If this is a simple -declaration reference, it may be mangled as an -<expr-primary>. - -

-Non-type template argument values of member pointer type -that are not null are mangled as expressions applying the -operator & to the member function declaration -as if it were a regular function. (Note that this does not -distinguish between member pointers that have been converted -to different types.) For example: - -

-struct A {
-    void foo();
-};
-template <void (A::*)()> void f();
-
-f<&A::foo> // _Z1fIXadL_ZN1A3fooEvEEEvv
-
- -

-Non-type template argument values of class type are mangled as -a direct initialization (tl) of the class type -using the member -initiializer sequence for the argument. - - -

5.1.5.11 Subobject specifier expressions
+ +
5.1.5.11 Template parameter declarations

-Constant evaluation may result in a pointer or reference to a specific -subobject of an object of static storage duration. This subobject is -identified in the mangling as if written with a specific expression, -called its subobject specifier expression. - -

-(To be specified. See -for now.) - - -

5.1.5.12 Member initializer sequences
- - -

-The constant evaluation of an expression of class type assigns -a constant value to each non-static data member of the class or, -if the class is a union, to at most one non-static data member, -called the active union member. Under the standard, this exact -structure uniquely determines a value, including whether a union -has an active union member member and (if so) which one. As -different values can produce different template specializations, -this structure must be faithfully represented in the mangling of -a value as a template argument. - -

-A value is converted to a flattened sequence of values by applying -the following expansions until no values of array or non-union class -type remain: -

-Values satisfying the following conditions are then removed from -the end of the sequence until the final value does not satisfy -these conditions or the sequence is empty: - - -

-This sequence is then mangled as a sequence of -<braced-expression>s -as follows: -

- -

-Note that qualifiers on the type of a non-static data member are not -part of the type of the value stored in that member. - -

-For example: - -

-struct A {
-    int x, y;
-};
-struct B {
-    union { A a; };
-    constexpr B(int x, int y) { a.x = x; a.y = y; }
-};
+Template parameter declarations are usually not mangled.  To distinguish
+function templates with different template signatures, the ABI mostly
+relies on the mangling of template arguments to include enough
+information to determine which template is meant.  However, there are
+some cases where this is not sufficient, such as when the template
+parameter contains deduced types, or when mangling generic lambdas.
+In these cases, the template parameter must be mangled directly.
 
-template <B v> struct C {};
-
-C<0,0> // 1CIXtl1BEEE
-C<1,0> // 1CIXtl1BtlNS0_Ut_Edi1atl1ALi1EEEEEE
-C<1,1> // 1CIXtl1BtlNS0_Ut_Edi1atl1ALi1ELi1EEEEEE
-
+

+  <template-param-decl> ::= Ty                                           # template type parameter
+                        ::= Tn <type>                                    # template non-type parameter
+                        ::= Tt <template-param-decl>* E                  # template template parameter
+                        ::= Tp <non-pack template-param-decl>            # template parameter pack
+

5.1.6 Expressions

@@ -6022,6 +5977,298 @@
5.1.6.2 References to declared ent

+ +
+
5.1.6.3 Constant values
+ +

+It is sometimes necessary to mangle a constant value resulting from +constant evaluation. Currently this is limited to several places +in the mangling of a non-dependent non-type +template argument. In general, this is done by mangling a notional +expression with the value and type of the constant. For various reasons, +these mangled expressions do not always correspond to valid source +expressions. + +

+The mangling of constant values is sensitive to the converted type of +the constant. For example, the expression 5 has type +int, but when it is used as the argument for a template +parameter of type unsigned short, it is converted to that +type and mangled as Lt5E. Some contexts require +precise typing, meaning that constants must be mangled in a way +that includes the exact converted type. In other contexts, some +information not necessary to distinguish constants is omitted in +order to reduce symbol sizes and preserve existing manglings. +The places in this ABI that call for the mangling of a constant value +all indicate whether precise typing is required for a particular +constant. + +

+Values of integer, enumerated, floating-point, or complex type are +mangled as literals of the approproiate +type. Note that this can produce combinations which cannot normally be +written in the source language, such as a literal of short +type. + +

+Null pointers and member pointers are mangled as as a literal +0 of their type. For example: + +

+struct A;
+template <void (A::*)()> void f();
+
+f<nullptr> // _Z1fILM1AFvvE0EEvv
+
+ +

+Non-null values of reference or pointer type are mangled using the +constant object reference +expression for the argument. Note that, if this is a simple +declaration reference, it may end up being an +<expr-primary> +and must be mangled without X...E when +mangled as a template-arg. + +

+Non-type template argument values of member pointer type +that are not null are mangled using the +member pointer +reference expression for the argument. + +

+Non-type template argument values of class type are mangled as +a direct initialization (tl) of the class type +using the member +initiializer sequence for the contents of the type. + + +

5.1.6.3 Constant object reference expressions
+ + +

+Constant evaluation may result in a pointer or reference to a function +or to a subobject of an object of static storage duration, possibly +converted to a different type. Under the standard, references to +formally different subobjects are different template arguments even +if they coincide in their type and address. This entity is identified +in the mangling as if it were written with a specific expression, +called the reference expression. The exact expression depends on +whether precise typing is +required. + +

+Let ObjectType be the unqualified type of the referenced +function or subobject, and let ConstantType be the pointee +type of the constant type. These types can differ by more than just +qualification if the constant has been converted to a type such as +void*. + +

+Initially, the reference expression is an expression referring +to the function or top-level object, mangled with the L_Z +production in +<expr-primary>. +The notional type of this expression is the declared type of the +function or object, including any qualifiers. + +

+Next, if the value refers to an object that is not a top-level object, +the reference expression is wrapped in a special expression used only +for this purpose, using the so mangling +below. The notional type of this expression is the encoded referent +type. +

+ +

+  <expression> ::= so <referent type> <expression> [<offset number>] <union-selector>* E
+  <union-selector> ::= _ [<number>]
+
+ +

+Next, if the constant is a pointer, the reference expression is +wrapped in a unary & operator (that is, prefixed with +ad. The notional type of this expression +is a pointer to the notional type of the previous reference expression. + +

+Finally, if precise typing is required and the notional type of the +current reference expression doesn't match the type of the constant, +the reference expression is wrapped in a C-style cast to the constant +type (that is, prefixed with cv <type>). + + +

5.1.6.4 Member pointer reference expressions
+ + +

+Constant evaluation may result in a member pointer to a specific +member, possibly converted to a different type. As with other constant +manglings, this value is identified in the mangling as if it were written +with a certain expression, called the reference expression. The exact expression depends on whether precise +typing is required. + +

+The initial reference expression applies the prefix operator +& to the member declaration as if it were a regular +function or variable. The notional type of this expression is a +member pointer type using the declaring class and the declared type of +the member. For example: + +

+struct A {
+    void foo();
+};
+template <void (A::*)()> void f();
+
+f<&A::foo> // _Z1fIXadL_ZN1A3fooEvEEEvv
+
+ +

+If the class type of the constant is not the declaring class of the +member, the reference expression is wrapped in a special expression +used only for this purpose, which encodes the this-adjustment +that must be applied to a pointer to the class type of the constant +in order to produce a pointer to the declaring class of the member. +The offset is omitted if zero. The notional type of this expression +is a member pointer type using the class type of the constant and the +declared type of the member. + +


+  <expression> ::= mc <class type> <expression> [<offset number>] E
+
+ +

+Finally, if precise typing is required and the notional type of the +current reference expression is not the type of the constant, the +reference expression is wrapped in a C-style cast to the constant +type (that is, prefixed with cv <type>). +This can happen because static_cast may add qualifiers +to the member type. + +

+struct A {
+  char x;
+  void foo();
+};
+struct B {
+  char y;
+};
+
+struct C : A, B {};
+template <void (C::*)()> void f();
+
+f<&A::foo> // _Z1fIXmcM1CFvvEadL_ZN1A3fooEvE1EEEvv
+
+ +

+The mc mangling uses an offset instead of +a base path to avoid exposing private details of the class into the ABI, +such as the names of any intermediate base classes. + + +

5.1.6.5 Member initializer sequences
+ + +

+The constant evaluation of an expression of class type assigns +a constant value to each non-static data member of the class or, +if the class is a union, to at most one non-static data member, +called the active union member. Under the standard, this exact +structure uniquely determines a value, including whether a union +has an active union member member and (if so) which one. As +different values can produce different template specializations, +this structure must be faithfully represented in the mangling of +a value as a template argument. + +

+A value is converted to a flattened sequence of values by applying +the following expansions until no values of array or non-union class +type remain: +

+Values satisfying the following conditions are then removed from +the end of the sequence until the final value does not satisfy +these conditions or the sequence is empty: + + +

+This sequence is then mangled as a sequence of +<braced-expression>s +as follows: +

+ +

+Precise typing is not required when mangling the constant member values. +Note that qualifiers on the type of a non-static data member are not +part of the type of the value stored in that member. + +

+For example: + +

+struct A {
+    int x, y;
+};
+struct B {
+    union { A a; };
+    constexpr B(int x, int y) { a.x = x; a.y = y; }
+};
+
+template <B v> struct C {};
+
+C<0,0> // 1CIXtl1BEEE
+C<1,0> // 1CIXtl1BtlNS0_Ut_Edi1atl1ALi1EEEEEE
+C<1,1> // 1CIXtl1BtlNS0_Ut_Edi1atl1ALi1ELi1EEEEEE
+
+

5.1.7 Scope Encoding