Hi,

On 04/02/19 17:48, Jason Merrill wrote:
On Mon, Feb 4, 2019 at 11:00 AM Paolo Carlini <paolo.carl...@oracle.com> wrote:
On 04/02/19 15:47, Jason Merrill wrote:
On 2/1/19 3:52 PM, Paolo Carlini wrote:
Hi,

I think that this ICE on invalid (and valid, for c++17+) can be in
fact avoided by accepting in make_typename_type a TYPE_PACK_EXPANSION
as context, thus by not triggering the "‘T ...’ is not a class"
error. Not sure if a better fix would be something more general.
Note, anyway, that we are asserting TYPE_P (context) thus
TYPE_PACK_EXPANSIONs definitely get through beyond MAYBE_CLASS_TYPE_P.
The testcase should test that the using actually works, i.e. imports a
type from a base class.
Uhm, if I change the testcase to something like:

struct B { typedef int type; };

template<typename ...T> struct C : T... {
    using typename T::type ...;
    void f() { type value; }
};

template class C<B>;

we get a "sorry, unimplemented: use of ‘type_pack_expansion’ in
template" for value
I suspected that would happen.

which, arguably, is better than the current ICE,
but I'm not sure if we are close to completing the implementation of
this / we even want to attempt that at this Stage?!?
Well, I'd look at how we implement the equivalent for e.g. a variable:

struct A { static int i; };
template <class ...T> struct B: T... {
   using T::i ...;
};
auto x = B<A>::i; // works

and try to use that code path for the typename case as well.

Yes. Note that, AFAICS, the code you added to implement P0195R2 works fine for typename too. The problem happens when we actually encounter inside the class something like:

    void f() { type value; }

which, so to speak, appears to expand to a set of types: 'type' is a TYPENAME_TYPE which has a TYPE_PACK_EXPANSION as TYPE_CONTEXT. What should we do here, in general? Should we expand it to a set a VAR_DECLs? Note that in practice when the pack is tsubst-ed to more than one type we error out before seeing the function 'f', we say that we have a redeclaration:

struct B1 { typedef int type; };
struct B2 { typedef int type; };

template<typename ...T> struct C : T... {
  using typename T::type ...;
  //void f() { type value; }
};

template class C<B1, B2>;

we do exactly the same in the non-variadic using case, thus at least we are consistent.Therefore I can't imagine cases where potentially it would make sense to inject *a set* of VAR_DECLs and see what happens. See the attached for something more concrete: in hindsight it's obvious that if we change make_typename_type to let TYPE_PACK_EXPANSION through then we have to handle more than aggregates in tsubst...

Thanks, Paolo.

Index: decl.c
===================================================================
--- decl.c      (revision 268513)
+++ decl.c      (working copy)
@@ -3816,7 +3816,9 @@ make_typename_type (tree context, tree name, enum
   gcc_assert (identifier_p (name));
   gcc_assert (TYPE_P (context));
 
-  if (!MAYBE_CLASS_TYPE_P (context))
+  if (TREE_CODE (context) == TYPE_PACK_EXPANSION)
+    /* This can happen for C++17 variadic using (c++/88986).  */;
+  else if (!MAYBE_CLASS_TYPE_P (context))
     {
       if (complain & tf_error)
        error ("%q#T is not a class", context);
Index: pt.c
===================================================================
--- pt.c        (revision 268513)
+++ pt.c        (working copy)
@@ -14895,8 +14895,18 @@ tsubst (tree t, tree args, tsubst_flags_t complain
 
     case TYPENAME_TYPE:
       {
-       tree ctx = tsubst_aggr_type (TYPE_CONTEXT (t), args, complain,
-                                    in_decl, /*entering_scope=*/1);
+       tree ctx = TYPE_CONTEXT (t);
+       if (TREE_CODE (ctx) == TYPE_PACK_EXPANSION)
+         {
+           ctx = tsubst_pack_expansion (ctx, args, complain, in_decl);
+           if (ctx == error_mark_node
+               || TREE_VEC_LENGTH (ctx) != 1)
+             return error_mark_node;
+           ctx = TREE_VEC_ELT (ctx, 0);
+         }
+       else
+         ctx = tsubst_aggr_type (ctx, args, complain, in_decl,
+                                 /*entering_scope=*/1);
        if (ctx == error_mark_node)
          return error_mark_node;
 

Reply via email to