OK, thanks.
On Wed, Mar 21, 2018 at 11:48 AM, Jason Merrill <ja...@redhat.com> wrote: > On Tue, Mar 20, 2018 at 11:27 PM, Alexandre Oliva <aol...@redhat.com> wrote: >> On Mar 20, 2018, Jason Merrill <ja...@redhat.com> wrote: >> >>> On Tue, Mar 20, 2018 at 6:07 PM, Alexandre Oliva <aol...@redhat.com> wrote: >>>> On Mar 20, 2018, Jason Merrill <ja...@redhat.com> wrote: >>>>> that doesn't mean it's wrong to peek. >> >>>> Huh? We're referencing members of an unrelated template that AFAIK >>>> needs not even be defined at that point, and even if it is, it could >>>> still be specialized afterwards. How can it possibly be right to >>>> short-circuit such nested names? >> >>>> template<typename> struct B : A {}; // would /*: A {}*/ make any diff? >>>> template<typename T> struct C : B<T> // would /* : B<T>*/ make any diff? >>>> { >>>> B<T>::A::I::I i; // { dg-error "typename" } >>>> }; >> >>> No, we look inside when we're trying to parse the qualified-id as the >>> name of a declaration >> >> Yeah, but that's not the case at hand. I guess we're miscommunicating. >> I understood you were saying it was ok to peek in this case. Would you >> please state, for clarity, what your stance is on peeking in this case, >> specifically, in the B<T>::A::I::I within the definition of C above? > > It's OK when we're tentatively trying to parse it as a declarator, not > when we're trying to parse it as a type-specifier. > > We seem to be talking past each other on this point: We already don't > peek when parsing a type-specifier, we only call resolve_typename_type > with false for only_current_p in the context of trying to parse a > declarator. The parser tries both interpretations for B<T>::A::I::I. > The type interpretation is ill-formed because of missing 'typename', > so it tries again as a declarator, and therefore tries to resolve the > TYPENAME_TYPE that it built while trying for a type, and trips the > assert. > >>> in a declarator-id we can look into uninstantiated classes, otherwise >>> there would be no way to define members of class templates. >> >>> void X<T>::N::f() { } // looks inside X<T> >> >> Of course, but then, we wouldn't get to a template-independent member. >> A member of a template-dependent name is still template-dependent. It's >> only when a qualified name maps to an external name that it might become >> template-independent, and when this happens, I believe the qualified-id >> is not one that can be used to define a member. Specifically: >> >> struct K { struct L { static double j; }; }; >> template <typename T> struct M { struct N { static int i; }; }; >> template <typename T> struct O { typedef M<T> P; typedef K Q; }; >> template <typename T> int O<T>::P::N::i = 42; >> template <typename T> double O<T>::Q::L::j = 42.0; >> >> if we remap O<T>::P to M<T> and O<T>::Q to K, how will we realize the >> given type-ids are not appropriate? Where will the template parmlist >> belong when the qualified-id is taken as equivalent to K::L::j? > > Agreed, these look bizarre because the template parm is used in O<T>, > which is not an enclosing class of i or j. And j is clearly > ill-formed because it declares a non-template as a template. > > But it's not clear to me that i is ill-formed; we allow > > struct A { static int i; }; > struct B { typedef ::A A; }; > int B::A::i = 0; > > and the i example seems analogous. I wouldn't argue against making it > ill-formed, but I don't see a rule that would make it so in the > current draft standard. And even resolving the scope to be > non-dependent doesn't necessarily mean the declaration will be, if the > template parameter list ends up being for a member template: > > struct K { struct L { template <typename T> static void f(T); }; }; > template <typename T> struct O { typedef K Q; }; > template <typename T> void O<T>::Q::L::f(T) { } > >>>>> I disagree; it seems to me that the assert should allow the case where >>>>> the scope was originally dependent, but got resolved earlier in the >>>>> function. >>>> >>>> Doesn't the function always take dependent scopes? I for some reason >>>> thought that was the case. >> >>> Yes, as the comment says, a TYPENAME_TYPE should always have a >>> dependent scope. In this case, the dependent scope was "typename >>> B<T>::A", but just above we resolved it to just A, making it no longer >>> dependent. >> >> Then you got me thoroughly confused: what did you mean by 'the assert >> should allow' a case that's a given? > > asserts are often used to verify that things we think of as givens are > actually true. :) > > I suppose we could remove the assert, but I'd probably move it up > above where we start messing with 'scope'. > > Jason