On Sat, 2 Mar 2024, Nathaniel Shead wrote:

> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> 
> -- >8 --
> 
> When streaming in a nested template-template parameter as in the
> attached testcase, we end up reaching the containing template-template
> parameter in 'tpl_parms_fini'. We should not set the DECL_CONTEXT to
> this (nested) template-template parameter, as it should already be the
> struct that the outer template-template parameter is declared on.
> 
>       PR c++/98881
> 
> gcc/cp/ChangeLog:
> 
>       * module.cc (trees_out::tpl_parms_fini): Clarify logic purely
>       for checking purposes. Don't consider a template template
>       parameter as the owning template.
>       (trees_in::tpl_parms_fini): Don't consider a template template
>       parameter as the owning template.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/modules/tpl-tpl-parm-3_a.H: New test.
>       * g++.dg/modules/tpl-tpl-parm-3_b.C: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> ---
>  gcc/cp/module.cc                                | 17 ++++++++++++-----
>  gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_a.H | 11 +++++++++++
>  gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_b.C | 13 +++++++++++++
>  3 files changed, 36 insertions(+), 5 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_a.H
>  create mode 100644 gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_b.C
> 
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index 67f132d28d7..5663d01ed9c 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -10126,10 +10126,14 @@ trees_out::tpl_parms_fini (tree tmpl, unsigned 
> tpl_levels)
>         tree dflt = TREE_PURPOSE (parm);
>         tree_node (dflt);
>  
> -       if (streaming_p ())
> +       if (CHECKING_P && streaming_p ())
>           {
> +           /* Sanity check that the DECL_CONTEXT we'll infer when
> +              streaming in is correct.  */
>             tree decl = TREE_VALUE (parm);
> -           if (TREE_CODE (decl) == TEMPLATE_DECL)
> +           if (TREE_CODE (decl) == TEMPLATE_DECL
> +               /* A template template parm is not the owning template.  */
> +               && !DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
>               {
>                 tree ctx = DECL_CONTEXT (decl);
>                 tree inner = DECL_TEMPLATE_RESULT (decl);
> @@ -10164,8 +10168,13 @@ trees_in::tpl_parms_fini (tree tmpl, unsigned 
> tpl_levels)
>           return false;
>         TREE_PURPOSE (parm) = dflt;
>  
> +       /* Original template template parms have a context
> +          of their owning template.  Reduced ones do not.
> +          But if TMPL is itself a template template parm
> +          then it cannot be the owning template.  */
>         tree decl = TREE_VALUE (parm);
> -       if (TREE_CODE (decl) == TEMPLATE_DECL)
> +       if (TREE_CODE (decl) == TEMPLATE_DECL
> +           && !DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))

IIUC a TEMPLATE_DECL inside a template parameter list always represents
a template template parm, so won't this effectively disable the
DECL_CONTEXT setting logic?

>           {
>             tree inner = DECL_TEMPLATE_RESULT (decl);
>             tree tpi = (TREE_CODE (inner) == TYPE_DECL
> @@ -10173,8 +10182,6 @@ trees_in::tpl_parms_fini (tree tmpl, unsigned 
> tpl_levels)
>                         : DECL_INITIAL (inner));
>             bool original = (TEMPLATE_PARM_LEVEL (tpi)
>                              == TEMPLATE_PARM_ORIG_LEVEL (tpi));
> -           /* Original template template parms have a context
> -              of their owning template.  Reduced ones do not.  */
>             if (original)
>               DECL_CONTEXT (decl) = tmpl;
>           }
> diff --git a/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_a.H 
> b/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_a.H
> new file mode 100644
> index 00000000000..21bbc054fa3
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_a.H
> @@ -0,0 +1,11 @@
> +// PR c++/98881
> +// { dg-additional-options "-fmodule-header" }
> +// { dg-module-cmi {} }
> +
> +template <typename P> struct X {};
> +
> +template<template <typename> typename TT>
> +struct X<TT<int>> {
> +  template<template <typename> typename UU>
> +  void f (X<UU<int>>&);
> +};

I wonder why the partial specialization is relevant here?  I can't
seem to trigger the ICE without the partial specialization.
Slightly further reduced to not use bound ttps:

    template<template<class> class TT>
    struct X { };

    template<template<class> class TT> requires true
    struct X<TT> {
      template<template<class> class UU>
      void f(X<UU>);
    };

Maybe the expectation is that tpl_parms_fini for UU should be called
with tpl_levels=1 (so that we stream only its own template parameters)
but it's instead called with tpl_levels=3 for some reason?

IIUC that assert should always hold in the first iteration of the loop
(for the ttp's own template parameters).  Perhaps for subsequent
iterations we need to actually stream the contexts?

Ah, but we also ICE in the same spot with:

  template<template<class> class TT> struct X;      // #1
  template<template<class> class UU> struct X { };  // #2

because DECL_CONTEXT of UU is #1 but tmpl is #2 (which seem like a
bug)...  But maybe this suggests we should just stream the DECL_CONTEXT
of each ttp as-is instead of trying to avoid it via an apparently
fragile assumption?

> diff --git a/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_b.C 
> b/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_b.C
> new file mode 100644
> index 00000000000..234e822faa9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/tpl-tpl-parm-3_b.C
> @@ -0,0 +1,13 @@
> +// PR c++/98881
> +// { dg-additional-options "-fmodules-ts" }
> +
> +import "tpl-tpl-parm-3_a.H";
> +
> +template <typename T> struct Y {};
> +template <typename T> struct Z {};
> +
> +void foo() {
> +  X<Y<int>> y;
> +  X<Z<int>> z;
> +  y.f(z);
> +}
> -- 
> 2.43.2
> 
> 

Reply via email to