23

For example:

template <typename T>
struct foo
{
    using bar = int;
};

// _Z3bazi
void baz(foo<int>::bar quux) {
}

template <typename T>
void baz(typename foo<T>::bar quux) {
}

// _Z3bazIiEvN3fooIT_E3barE
template void baz<int>(foo<int>::bar quux);

Why does the mangled form of baz<int> mention foo at all? How come it's not _Z3bazIiEvi?

This is apparently the reason that the C++17 std::default_order<T> proposal is dead in the water.

Tavian Barnes
  • 12,477
  • 4
  • 45
  • 118
  • So that when demangled, it looks like the source code? – BoBTFish Oct 20 '16 at 14:38
  • [relevant discussion on reddit](https://www.reddit.com/r/cpp/comments/56zkbx/c1417_features_and_stl_fixes_in_vs_15_preview_5/d8npapy) – krzaq Oct 20 '16 at 14:43
  • I untagged [gcc] because as an ABI question it also applies to Clang etc. I reproduced the problem with Clang, and it's very surprising… incredible that this could be conforming to the C++ standard. So the first step is to check that the [abi](https://mentorembedded.github.io/cxx-abi/abi.html#mangling) does in fact dictate it. – Potatoswatter Oct 20 '16 at 14:47
  • @Potatoswatter Indeed, the Itanium ABI is not specific to gcc. But I tagged [gcc] anyway because I felt like gcc experts may have the necessary knowledge to answer this question. Also, I believe lots of the mangling rules were invented by gcc and codified into the Itanium ABI later, no? – Tavian Barnes Oct 20 '16 at 14:50
  • @Barry Nonconformance comes if you can observe that a distinct symbol exists, for example if the two names of the one specialization yield different addresses. At first I thought Clang was letting me generate one specialization twice, but actually that's not the case. – Potatoswatter Oct 20 '16 at 15:18

1 Answers1

9

The issue comes from the <unresolved-name> construct in the ABI. Why would we ever want to use an unresolved name? It's all about declaration matching and overloads. C++14 §14.5.6.1/3 notes,

Two distinct function templates may have identical function return types and function parameter lists, even if overload resolution alone cannot distinguish them.

You can have another function in a different file,

template <typename T>
void baz(int quux) { std::abort(); }

Although this signature can't peacefully coexist in the same file — it cannot be named because of overload ambiguity — it can exist in a different file so it needs a distinct mangling.

(Even this level of coexistence is not guaranteed by the standard for all templates. It's a matter of QOI that the compiler uses the exact form of a function template declaration to perform declaration matching, so that copy-pasting a declaration into a definition will tend to provide an exact match and not a surprising conflict with another function template that resolves to the same signature. See §14.5.6.1/5-6.)

As for raining on default_order's parade, the problem is that template-ids implicitly pull default arguments from templates. So the user could unintentionally have a dependent typename in a signature just by mentioning std::set.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Thanks! Are you sure that `template void baz(int)` is actually allowed in a separate translation unit? I always assumed that was one of those "malformed, no diagnostic required" things. In particular, apparently MSVC mangles both the same way. Is that really nonconformant? – Tavian Barnes Oct 20 '16 at 16:17
  • @TavianBarnes NDR is possible per §14.5.6.1/6 if they are "functionally equivalent but not equivalent." It's a little odd that the rule specifies to compare template declarations according to a process defined only for expressions, so it talks about values when we only have types. I'll edit… – Potatoswatter Oct 20 '16 at 16:32
  • "functionally equivalent" means that "for any given set of template arguments, the evaluation of the expression results in the same value". Even if a similar rule applied to types, it wouldn't apply to `default_order`, which is *meant* to be specialized to return something different. – T.C. Oct 20 '16 at 21:24
  • @T.C. Equivalence is when the expression (or dependent typename) is expressed in a similar way, modulo parameter identifiers. Functional equivalence is when evaluation given all the actual template arguments results in same values (or dependent types?). For the `default_order` example, functional equivalence only makes sense in the context of use, after any customization. Equivalence is determined in processing declarations; functional equivalence may be determined at overload resolution. Both always apply, though they're relevant at different points in compilation. – Potatoswatter Oct 21 '16 at 00:24
  • 1
    You seem to read "for any" as "there exists any". I read it as "for every", so that two expressions are "functionally equivalent" only if there exists no set of arguments for which the two expressions will yield different values. Note that "functionally equivalent" is a property of two function templates, not two function template specializations, so you seem to be saying that `template void f(A); template void f(A);` is ill-formed NDR, which makes zero sense to me. – T.C. Oct 21 '16 at 00:38
  • @T.C. You're reading it correctly, but although it's a property of templates (not their specializations), functional equivalence of a function template signature *using* `default_order` can still depend on specializations of `default_order`. Your example is certainly ill-formed NDR. Plug `I=0, J=0` and you have a "given set of template arguments, the evaluation of the expression results in the same value." Any resulting confusion is a justification for the Itanium ABI sensibility of QOI. – Potatoswatter Oct 21 '16 at 01:08
  • 1
    @Potatoswatter No, `I + J` and `I * J` are not functionally equivalent under my reading because there exists some sets of arguments for which they do not have the same value (e.g., `I=1, J=1`). You are reading "for any" as ∃, I'm reading it as ∀. Indeed, under your reading, even the example two paragraphs above in [temp.over.link]/4 would be ill-formed NDR. If that were the intent, I can't imagine why they wouldn't call it out there. – T.C. Oct 21 '16 at 01:12
  • @T.C. Oh, now I see what you mean. "∀" is probably the correct reading, but seemingly not what MSVC does, so it may be nonconforming. I hedged this answer with "**may** not be guaranteed." However, that's orthogonal to the issue of `default_order` specializations. The NDR diagnosis is applied very lazily at link time, if at all, whereas you might be thinking in eager terms at template declaration time. – Potatoswatter Oct 21 '16 at 01:41
  • But they are related; unless for every `T`, `default_order_t` and `std::less` are the same type - and they are not - then `template void f(default_order_t);` and `template void f(std::less);` are distinct function templates that violate no rule in the standard, and the compiler must ensure that their specializations are *not* linked together erroneously. – T.C. Oct 21 '16 at 01:47
  • @T.C. The Itanium ABI seems to go with ∀ which minimizes unintuitive linking-together (but also fails to link together e.g. `A+B` with `B+A`). MSVC seems to go with ∃ (or it simply has an ABI bug) and overloading-ambiguous signatures may collide in the linker. The errors in either case come naturally from the linker, not from an attempt to actually prove equivalence. For example, MSVC may normally give same names to specializations of your two `f` templates, except when `default_order` is specialized, then the names will be distinct and there is no erroneous linkage. – Potatoswatter Oct 21 '16 at 02:09
  • @T.C. … If `default_order` is *not* specialized, then MSVC *does* link between those two forms (presuming I'm right about it; I cannot verify because I don't have a copy). That's probably what you're really getting at. But under their reading of ∃ it would be ill-formed NDR so linkage, even if erroneous, is actually OK. – Potatoswatter Oct 21 '16 at 02:25
  • "It's a matter of QOI that the compiler uses the exact form of a function template declaration to perform declaration matching, so that copy-pasting a declaration into a definition will tend to provide an exact match and not a surprising conflict with another function template that resolves to the same signature." - since `foo` can be specialized so that it yields non-`int` types, I disagree with you. The templates are not functionally equivalent AFAICS. – Johannes Schaub - litb May 14 '17 at 11:34
  • (note that currently I think the paragraphs you quote only apply to template parameter lists or return types that refer to expressions: "Two function templates are functionally equivalent if they are equivalent except that one or more **expressions** that involve template parameters in the return types and parameter lists are functionally equivalent using the rules described above to compare expressions involving template parameters.". – Johannes Schaub - litb May 14 '17 at 11:45
  • AFAIK, this is intended to also cover "type expressions" - in the sense of "and an attempt is made to find template argument values (a type for a type parameter, a..." from [temp.deduct] which is a bit less formal in its use of "values", in the same way that your referenced paragraphs are a bit less formal. But maybe I'm overlooking something. – Johannes Schaub - litb May 14 '17 at 11:46
  • @JohannesSchaub-litb 1. I've strengthened the "may not" in the preceding sentence. The parenthetical paragraph isn't intended to apply to the example at hand; yes, it involves expressions. 2. Even if the linker only saw fully instantiated templates, it wouldn't have problems with specialized `foo`. The program is only allowed to have one member-specification per specialization. – Potatoswatter May 15 '17 at 10:29
  • @JohannesSchaub-litb The standard mentions "return types and parameter lists that are equivalent using the rules described above to compare expressions involving template parameters" — the equivalence rules are used to account for identifier differences when expressions aren't involved, but I don't see how (mere) functional equivalence can occur without an expression, such in as the given example. – Potatoswatter May 15 '17 at 10:40