8
struct A{
    template<typename U>
    void T(){}
};
struct B{
    template<typename U>
    struct T{
       using type = U;
    };
};
struct C:A,B{

};
int main(){
    C::T<int>::type d;
}

This example is accepted by neither GCC nor Clang.

As per basic.lookup.qual#1

The name of a class or namespace member or enumerator can be referred to after the​::​ scope resolution operator ([expr.prim.id.qual]) applied to a nested-name-specifier that denotes its class, namespace, or enumeration. If a​::​ scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that ​::​ considers only namespaces, types, and templates whose specializations are types.

That means that when looking up the declarations for the template name T, the specialization of T shall denote a type in this context. On the other hand, as per class.member.lookup#4

If C contains a declaration of the name f, the declaration set contains every declaration of f declared in C that satisfies the requirements of the language construct in which the lookup occurs.

Again, when looking up the template T in the scope of C, only those templates whose specialization is a type should be considered by this lookup. The scope of C does not have any declarations for T, hence the lookup will be performed for S(T,C) in every one of its base classes. The template T in A does not satisfy the requirement. Meanwhile, the template T declared in the scope of B does satisfy the requirement. So the lookup is not ambiguous and the B::T is the unique result. That means C::T<int>::type d should be well-formed. Why do both GCC and Clang reject this example? Can it be considered a bug of in both? If I missed something, what's the reason that this example should be ill-formed?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
xmh0511
  • 7,010
  • 1
  • 9
  • 36
  • *"The template T in A does not satisfy the requirement."* Which is the *requirements* for you. `T` or `T::type` or something else... I would have said the first one, but I'm not even sure it is the requirement in concern here, and it doesn't talk about *context*. – Jarod42 Feb 26 '21 at 09:57
  • @Jarod42 I don't know what you are saying. – xmh0511 Feb 26 '21 at 09:59
  • Adding typename/template only *"helps"* gcc [Demo](https://godbolt.org/z/WTdqsM). – Jarod42 Feb 26 '21 at 10:00
  • I don't know the answer. I meant that it is not clear what *"requirement"* means. You seems to understand that it produces valid "interpretation" for code, but `C::T` is ambiguous, `C::T::type` is only valid for `B`, `C::T::type d{1, 42};` would be invalid for both. Adding `typename` should force the "context" to exclude `A::T`, but doesn't for clang/msvc. – Jarod42 Feb 26 '21 at 10:10
  • From the "note" of class.member.lookup#4, we are in a place where we can have both a type or a function, so both `A::T` and `B::T` are "valid". But basic.lookup.qual#1 should indeed discards `A::T` from my understanding. – Jarod42 Feb 26 '21 at 10:19
  • @Jarod42 I think `template` is not necessary because of https://timsong-cpp.github.io/cppwp/n4861/temp.names#2.sentence-3 – Language Lawyer Feb 26 '21 at 10:37
  • 1
    @LanguageLawyer: My point was mostly on `typename`. `template` doesn't hurt. (I tried with and without and no difference indeed). – Jarod42 Feb 26 '21 at 10:44
  • @Jarod42 Why not do you think that `C::T::` is a **nested-name-specifier**? The name followed by operator `::` should obey `basic.lookup.qual#1` that is the requirement here for lookup – xmh0511 Feb 26 '21 at 10:58
  • @Jarod42 As per [temp.res#3](https://timsong-cpp.github.io/cppwp/n4861/temp.res#3). I think adding `typename/template` is essentially equavelent to without the `typename/template` – xmh0511 Feb 26 '21 at 11:02
  • I would expect that adding `typename` (or use `using D = C::T::type`) would discard non-type `A::T` (from class.member.lookup#4), but it seems not. – Jarod42 Feb 26 '21 at 11:07
  • Not a language lawyer. As I (mis?)understand basic.lookup.qual#1, in `SomeStuff::`, `SomeStuff` should only consider class/namespace, so discard function `A::T`. (but compilers disagree). – Jarod42 Feb 26 '21 at 11:11
  • @Jarod42 If you change the example, that is, change the template function and class in A and B to a common function and class, respectively. Then these compilers will agree basic.lookup.qual#1. It makes no sense that they don't agree with that rule in the case of template specialization. – xmh0511 Feb 26 '21 at 11:38
  • Worth to note, that gcc accepts `typename C::T::type d;` but both 'typename` and `template` should be redundant here according to the standard. – Secundi Feb 26 '21 at 12:13
  • 1
    Long ago, Johannes Schaub observed, that gcc rejects function name look-ups in a typename-specifier lookup: https://bugs.llvm.org/show_bug.cgi?id=8263 That would explain gcc's special behavior here. A very similar (but unfortunately quite mixed up issues) question was stated here: https://stackoverflow.com/questions/18311496/should-the-name-of-a-function-template-be-visible-during-lookup-of-a-name-preced – Secundi Feb 26 '21 at 12:36
  • An [example](https://godbolt.org/z/Thqfae) that is the case fixed by P1787. – xmh0511 Feb 27 '21 at 12:34

1 Answers1

4

The lookahead required to have the lookup for T depend on the :: even as the interpretation of < depends on the meaning of T is considered undesirable. As such, lookup for a name followed by < is not restricted to types and namespaces regardless of any >:: found later. P1787R6 fixed this, restricting the special lookup to identifiers immediately followed by :: (since other kinds of names can’t refer to types or namespaces anyway).

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • Do you refer to [basic.lookup.qual#general-1](https://eel.is/c++draft/basic.lookup.qual#general-1)? "If a name, template-id, or decltype-specifier is followed by a ​::​, it shall designate a namespace, class, enumeration, or dependent type, and the ​::​ is never interpreted as a complete nested-name-specifier." However, it requires us firstly to determine whether `T` is a template-id or not. Hence temp.names#3.2 applies here to try to lookup whether `T` is a template. That means there's no other restriction for this lookup, – xmh0511 Feb 27 '21 at 02:09
  • We would find both `A::T` and `B::T` in the scope of `C`, which is an ambiguous merge. So, Do you mean we should add the keyword `template` before `T` to make `T` be considered as a template-id, after doing it, then [temp.res#general-4.1] will apply to `T`, Right? – xmh0511 Feb 27 '21 at 02:12
  • @jackX: `template` would make /4.1 *unnecessary*, but being in a type-only context doesn’t affect what name lookup finds for `T`, so the ambiguity still renders the program ill-formed. – Davis Herring Feb 27 '21 at 02:52
  • Doesn't [basic.lookup#general-4](https://eel.is/c++draft/basic.lookup#general-4) not apply here for the name `T`? which means `T` should be a template whose specialization is a type. – xmh0511 Feb 27 '21 at 03:15
  • @jackX: Type-only lookups are different from type-only contexts, which are a template-specific thing. (That’s why both are italicized when introduced.) – Davis Herring Feb 27 '21 at 03:38
  • So, According to "A qualified-id whose terminal name is dependent and that is in a type-only context is considered to denote a type. ". The name should satisfy two conditions(one is it's a dependent name, the other is it's in a type-only context), then it will affect the lookup rule, Right? In this example, although `T` is said to be in a **type-only context** but it is not a dependent name, hence, it does not affect the lookup rule, Right? – xmh0511 Feb 27 '21 at 03:43
  • @jackX: No, not even that affects lookup—that’s the case where `typename` need *not* be used to force interpretation of a qualified name as a type **before** it can be looked up. You can still find a non-type during instantiation; it’s just an error if you do ([temp.res.general]/3). – Davis Herring Feb 27 '21 at 03:55
  • Is it the reason that sentence [temp.res#general-5] merely says the qualified-id is considered to denote a type when it satisfies these rules. It does not talk about **lookup** at all. it's the difference with [basic.lookup.qual#general-1], that is **Lookup** of an identifier followed by a ​::​ scope resolution operator **considers only namespaces, types, and templates whose specializations are types**. [This example](https://godbolt.org/z/WG8b6G) indeed proves what's the rule says. – xmh0511 Feb 27 '21 at 04:17
  • @jackX good example! I think the core question is: Is basic.lookup.qual#general-1 strong enough to prevent the compiler from considering all matching candidates for the current 'local' template instantiation context even if the 'real' usage of the template is ill-formed for all possible cases (merge ambiguity), not just for qualified look-up scenarios. At least, one can interprete basic.lookup.qual#general-1 here as a fallback since 'considering' needn't to be more prioritised than instantiation a priori, solely ensuring the prevention of further wrong parser lines. – Secundi Feb 27 '21 at 09:11
  • @Secundi I think P1787 fixed this question by rewording the rule as "Lookup of an identifier followed by a ​::​ scope resolution operator...". That means a `template-id` is not the case stated here(i.e, there's no restriction to the `template-name` of a `tempalte-id` followed by `::` when performing the lookup). – xmh0511 Feb 27 '21 at 09:46
  • @jackX yes, that's it. – Secundi Feb 27 '21 at 10:17
  • 1
    @Secundi As Divas said in his answers, I conflated the `only-type lookup` with the `only-type context`. There's no connection between them. – xmh0511 Feb 27 '21 at 10:21
  • An [example](https://godbolt.org/z/Thqfae) that is the case fixed by P1787. – xmh0511 Feb 27 '21 at 12:36