-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mangling for converted non-type template arguments #47
Comments
The link in this issue appears dead. Here is the thread summarized from my personal mail archive: Subject: N4198 and mangling for member pointer template arguments Richard Smith, Nov. 25, 2014:
(Here, a naive approach would use In order to resolve this, I suggest we introduce a new mangling for the case of a member pointer template argument where the class containing the member is different from the class in the template parameter. The minimal information we'll need to include is the class in the template parameter and a designator if the base class is a repeated base class. One approach would be to use
and to explicitly include the final type plus those intermediate types that introduce multiple inheritance from the base class (that is, just enough to uniquely identify the path). Another would be to introduce a new mangling that incorporates the final type and an offset or discriminator. Thoughts? John McCall, Dec. 1, 2014: Richard Smith, replies same day: John McCall, Dec. 1, 2014: Richard Smith, replies same day: GCC's c++filt gives John McCall, Dec. 1, 2014: We’d have to specify in what dependent situations we include the path. “Never” is the easiest answer, so that in
we’d mangle Richard Smith, replies same day: There's another issue that we should probably fix at the same time: qualification conversions are permitted in template arguments, and we currently mangle a signature that performs a qualification conversion the same way as we mangle a signature that does not. We could either fold the qualification conversion into the last (synthetic) static_cast, or add an explicit synthetic const_cast to model it. I'm inclined to favour the latter, even though it will give longer manglings in the (hopefully rare) case where both conversions occur (because it also works if the user has cast away constness, and because it's simpler). Example:
Here:
|
To illustrate the
(Some implementations incorrectly fail the static assertion check.) If I understand correctly, the type of |
Concrete suggestion: [UPDATED 2023-03-29 to specify the natural template parameter corresponding to a pack argument.] STEP 1: Form a base mangling
STEP 2: Add the path to the subobject (or containing class)For a non-null pointer or a reference, apply the minimal series of member access expressions and array indexing in order to reach the most-derived object of the identified subobject.
For a reference or for a non-null pointer or pointer-to-member, if the object identified so far (or for a pointer to member, the class in the value's type) is of a different class type from that of the parameter, those classes must be related by inheritance, and a minimal sequence of casts is inserted. For each class in the inheritance path between the class type associated with the argument and the class type associated with the parameter that directly inherits from multiple base classes that have a path to the target base class, a cast to the chosen base class of that class is inserted. (For a base-to-derived pointer-to-member conversion, these casts are prepended in base-to-derived order, otherwise they are prepended in derived-to-base order.) Casts are expressed using For a pointer, the address is taken using If the template parameter is a non-type template parameter with a deduced type, and the type of the parameter is not the same as that of the Finally, if the expression for a non-type template argument is not an STEP 3: Ensure a unique overload is identifiedIf the template is not overloadable, use the Otherwise, determine the natural template parameter for the
If the natural template parameter for the Comparison to status quoThis proposal aims to preserve all unambiguous status quo manglings wherever possible. The following existing manglings are changed:
|
For Daveed's example, this gives the suggested type for
So I think we should not bother with named casts and just use The above approach is based around the idea of trying not to invent new mangling productions wherever possible. Another way we could go for identifying subobjects would be to introduce a new mini-grammar to more compactly describe subobject paths. Eg, |
This could generate a needlessly long path under certain circumstances, e.g.
It should be straightforward to get an actually minimal path by just updating the target as we go, starting from the most-derived end:
Minimizing the path is useful no matter how we end up mangling it. |
As for how to mangle the path: you're right that it's probably simplest to use casts, but they can definitely get verbose, especially with member pointers. (Member pointers won't be as bad as they could be because the member type should be reliably substituted, but it's still a lot.) Also, using casts means that all of those intermediate cast types will be substitution candidates, which might be really awkward for implementations. I wouldn't be opposed to some sort of optimized conversion mangling that's only used (for now) in template arguments. |
Agreed, your approach to minimizing the sequence of conversions is strictly better, and seems no harder to implement. Please pretend I suggested the smarter thing ;-) Regarding an optimized mangling for conversion paths: If we want a minimal representation for pointers, references, and pointers-to-members, I think all we need is:
Alternatively, we could number the possible values and just use a base value plus an index. That should even be workable for pointers-to-members -- whenever we have a non-null constant value of type At the other end of the scale, if we want a good, readable demangling, we need a path of member accesses, base classes (only where ambiguous), array indexes, and a final "one past the end" flag. This could be very compact, eg 3mem5otherA13_ for ".mem.other[13]". So I think we need to consider how much information we want in the demangled form versus how much mangled name length we're prepared to pay. (And also the cost of non-uniformity between dependent and resolved names.) |
I'm reconsidering "STEP 2" above. It seems important that renaming a private non-static data member is not an ABI-affecting change (except in weird cases, like forming a pointer-to-member or using the member name in a SFINAE context). So given:
... it seems important that the mangling for For pointers to members, something somewhat similar but more subtle (and probably far less important) can happen if we encode the conversion path:
Here, suppose
... affects the ABI. More broadly: private implementation-detail classes in the middle of the inheritance hierarchy can show up in pointer-to-member conversion paths. So I suggest we replace STEP 2 with something like this: STEP 2: Add the offset to the subobject (or containing class)For a non-null pointer or a reference, if the value is a subobject pointer/reference or a past-the-end pointer value, identify the subobject with a new expression mangling (used only for this purpose):
where
For a pointer, the address is then taken using For a pointer-to-member, if the class of the member is different from that in the parameter type, it is converted using a new expression mangling (used only for this purpose):
where
If either the template parameter is a non-type template parameter with a deduced type, or the template name is unresolved or refers to an overloadable template, and the type of the parameter is not the same as that of the Finally, if the expression for a non-type template argument is not an CommentaryThis adds new expression manglings that are not used for expressions in general, only for expressions in template arguments. That seems a little unfortunate, but there's really no way to express the past-the-end flag and the union member accesses with any current mangling, without relying on field names. We could mangle converted pointers to members as (eg) The above manglings should be invariant under most changes that don't have broader effects on ABI (renaming, rearranging class hierarchies without layout changes, and so on). The exceptions are:
Folding a qualification conversion into the |
Indeed, non-static data member names are already part of the ABI in some situations. Is it such a problem to have that existing condition extended to the new functionality? And identifying union members by name seems to me the best choice. |
I think it's fine for us to use the names of union members in class NTTP manglings, because those members are required to be public. But I think it's not reasonable for private member names to be used in pointer and reference NTTP manglings with the class author being able to do nothing about it. (Today, if you don't form a pointer to member, and you don't use the member name in a SFINAE context, you can rename private members and reorganize in ways that don't change class layout freely.) For example, there is no way to avoid the private member names of, say, std::tuple becoming part of the ABI with the previous approach, because they're exposed by reference to constant evaluations. I imagine the folks who want to provide ABI stability guarantees would be very upset by that.
I'm much more on the fence on this one. But the positional approach results in shorter manglings, and if we're not going to be able to recover the full path anyway, using a minimal discriminator seems like it might be preferable. |
mangling support for non-type template parameters of class type and template parameter objects. The Itanium side of this follows the approach I proposed in itanium-cxx-abi/cxx-abi#47 on 2020-09-06. The MSVC side of this was determined empirically by observing MSVC's output. Differential Revision: https://reviews.llvm.org/D89998
…ons. The extensions in question are described in: itanium-cxx-abi/cxx-abi#47 itanium-cxx-abi/cxx-abi#63 Differential Revision: https://reviews.llvm.org/D90003
mangling support for non-type template parameters of class type and template parameter objects. The Itanium side of this follows the approach I proposed in itanium-cxx-abi/cxx-abi#47 on 2020-09-06. The MSVC side of this was determined empirically by observing MSVC's output. Differential Revision: https://reviews.llvm.org/D89998
…to be different if they have different types. For the Itanium ABI, this implements the mangling rule suggested in itanium-cxx-abi/cxx-abi#47, namely mangling such template arguments as being cast to the parameter type in the case where the template name is overloadable. This can cause a mangling change for rare cases, where * the template argument declaration is converted from its declared type to the type of the template parameter, and * the template parameter either has a deduced type or is a parameter of a function template. However, such changes are necessary to avoid mangling collisions. The ABI changes can be reversed with -fclang-abi-compat=11 or earlier. Differential Revision: https://reviews.llvm.org/D91488
…to be different if they have different types. For the Itanium ABI, this implements the mangling rule suggested in itanium-cxx-abi/cxx-abi#47, namely mangling such template arguments as being cast to the parameter type in the case where the template name is overloadable. This can cause a mangling change for rare cases, where * the template argument declaration is converted from its declared type to the type of the template parameter, and * the template parameter either has a deduced type or is a parameter of a function template. However, such changes are necessary to avoid mangling collisions. The ABI changes can be reversed with -fclang-abi-compat=11 or earlier. Re-commit with a fix for the regression introduced last time: don't expect parameters and arguments to line up inside an <unresolved-name> mangling. Differential Revision: https://reviews.llvm.org/D91488
…to be different if they have different types. For the Itanium ABI, this implements the mangling rule suggested in itanium-cxx-abi/cxx-abi#47, namely mangling such template arguments as being cast to the parameter type in the case where the template name is overloadable. This can cause a mangling change for rare cases, where * the template argument declaration is converted from its declared type to the type of the template parameter, and * the template parameter either has a deduced type or is a parameter of a function template. However, such changes are necessary to avoid mangling collisions. The ABI changes can be reversed with -fclang-abi-compat=11 or earlier. Re-commit with a fix for a couple of regressions. Differential Revision: https://reviews.llvm.org/D91488
mangling support for non-type template parameters of class type and template parameter objects. The Itanium side of this follows the approach I proposed in itanium-cxx-abi/cxx-abi#47 on 2020-09-06. The MSVC side of this was determined empirically by observing MSVC's output. Differential Revision: https://reviews.llvm.org/D89998
…ons. The extensions in question are described in: itanium-cxx-abi/cxx-abi#47 itanium-cxx-abi/cxx-abi#63 Differential Revision: https://reviews.llvm.org/D90003
…ons. The extensions in question are described in: itanium-cxx-abi/cxx-abi#47 itanium-cxx-abi/cxx-abi#63 Differential Revision: https://reviews.llvm.org/D90003
@zygoloid, I know compilers have been adopting some variant of this. Any changes you want to make before I land this? |
I think I'm happy enough with the rules as described above if others are, and Clang has implemented those rules for a while, so we have at least some experience with them. |
mangling support for non-type template parameters of class type and template parameter objects. The Itanium side of this follows the approach I proposed in itanium-cxx-abi/cxx-abi#47 on 2020-09-06. The MSVC side of this was determined empirically by observing MSVC's output. Differential Revision: https://reviews.llvm.org/D89998
I think I buy Richard's argument about just using an offset in step 2:
@jicama, does that part of Richard's proposal seem acceptable? |
And I think the ABI break in Part 3 is probably necessary and best made as soon as possible. |
This includes the material from itanium-cxx-abi#47 (non-type template arguments), (C++20 lambda-expressions).
This includes the material from itanium-cxx-abi#47 (non-type template arguments), (C++20 lambda-expressions). @zygoloid gets credit for most of this, although I've made a few substantive changes from his suggestions.
This includes the material from itanium-cxx-abi#47 (non-type template arguments), itanium-cxx-abi#63 (class constants), and the template-param-decl portions of itanium-cxx-abi#85 (C++20 lambda-expressions). @zygoloid gets credit for most of this, although I've made a few substantive changes from his suggestions.
Okay, I've pushed a complete draft for this as #166. This includes the specification of There are a few substantive differences between my draft and Richard's last proposal. Chiefly, I took the way that mangling was different for function template arguments and formalized it into the idea of contexts that require "precise typing". That then made it pretty easy to stop using precise typing in some places where it's not necessary, like if we're mangling a |
This implements proposals from: - itanium-cxx-abi/cxx-abi#24: mangling for constraints, requires-clauses, requires-expressions. - itanium-cxx-abi/cxx-abi#31: requires-clauses and template parameters in a lambda expression are mangled into the <lambda-sig>. - itanium-cxx-abi/cxx-abi#47 (STEP 3): mangling for template argument is prefixed by mangling of template parameter declaration if it's not "obvious", for example because the template parameter is constrained (we already implemented STEP 1 and STEP 2). This changes the manglings for a few cases: - Functions and function templates with constraints. - Function templates with template parameters with deduced types: `typename<auto N> void f();` - Function templates with template template parameters where the argument has a different template-head: `template<template<typename...T>> void f(); f<std::vector>();` In each case where a mangling changed, the change fixes a mangling collision. Note that only function templates are affected, not class templates or variable templates, and only new constructs (template parameters with deduced types, constrained templates) and esoteric constructs (templates with template template parameters with non-matching template template arguments, most of which Clang still does not accept by default due to `-frelaxed-template-template-args` not being enabled by default), so the risk to ABI stability from this change is relatively low. Nonetheless, `-fclang-abi-compat=17` can be used to restore the old manglings for cases which we could successfully but incorrectly mangle before. Fixes #48216, #49884, #61273 Reviewed By: erichkeane, #libc_abi Differential Revision: https://reviews.llvm.org/D147655
This includes the material from itanium-cxx-abi#47 (non-type template arguments), itanium-cxx-abi#63 (class constants), and the template-param-decl portions of itanium-cxx-abi#85 (C++20 lambda-expressions). @zygoloid gets credit for most of this, although I've made a few substantive changes from his suggestions.
See prior discussion here: http://sourcerytools.com/pipermail/cxx-abi-dev/2014-November/002785.html
Recent language changes (in particular,
auto
template parameters and the allowance of arbitrary constant expressions for pointer and member pointer non-type template arguments) mean that encoding the target of a non-type template argument is not sufficient to uniquely identify the argument. We also need the type in some cases, and for pointers to members, we need the conversion path used to form the type too.One previously-discussed approach that seemed to have support was to use a
cv
... expression to describe the conversion if the natural type of the non-type template argument differs from the actual type, and can't be inferred from the parameter (eg, for a function template or when the parameter has a deduced type). For a pointer-to-member, a minimal sequence ofcv
... expressions would be used to express the derived-to-base or base-to-derived conversion path.We need concrete rules describing exactly how this should work, of course :)
The text was updated successfully, but these errors were encountered: