public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: auto(x) partial substitution [PR110025, PR114138]
Date: Fri, 1 Mar 2024 10:17:00 -0500 (EST)	[thread overview]
Message-ID: <b8e7d048-d97d-a0a5-ee5b-9a64c669a395@idea> (raw)
In-Reply-To: <29400542-9b90-4a7c-9c58-234286058646@redhat.com>

On Fri, 1 Mar 2024, Jason Merrill wrote:

> On 2/29/24 14:17, Patrick Palka wrote:
> > On Wed, 28 Feb 2024, Jason Merrill wrote:
> > > I wonder about, rather than returning it directly, setting its level to 1
> > > for
> > > the substitution?
> > 
> > Done, that works nicely.
> > 
> > > Then I wonder if it would be feasible to give all autos level 0 and adjust
> > > it
> > > here?  That's probably not a stage 4 change, though...
> > 
> > It seems feasible.  I experimented doing this in the past[1] and ran
> > into two complications.  One complication was with constrained auto
> > deduction, e.g.
> > 
> >    template<class T>
> >    void g() {
> >      C<T*> auto x = ...;
> >    };
> > 
> > Here the underlying concept-id that we enter satisfaction with is
> > C<auto, T*> where this auto has level one greater than the template
> > depth, and the argument vector we pass has an extra innermost level
> > containing the deduced type, so things match up nicely.  This seems
> > to be the only place where we truly need auto to have a non 0/1 level.
> > In my WIP patch in that thread I just made do_auto_deduction build the
> > concept-id C<auto, T*> in terms of an auto of the proper level before
> > entering satisfaction, which was kind of ugly but worked.
> 
> So maybe set its level to TMPL_ARGS_DEPTH (targs) after add_to_template_args,
> rather than 1?

AFAICT in-place type modification in this case would be unsafe or at
least difficult to reason about due to the satisfaction/normalization
caches.  We would cache the result as if the auto had the nonzero level
and then (presumably) reset its level back to 0 afterward, leaving the
hash tables in an inconsistent state.

> 
> > The other complication was with Concepts TS extended auto deduction:
> > 
> >    tuple<auto...> t = tuple<int, char>{};
> > 
> > because unify_pack_expansion (called from fn_type_unification during
> > do_auto_deduction) isn't prepared to see a parameter pack of level 0
> > (unify has no problems with ordinary tparms of level 0 though).  This
> > shouldn't be too hard to fix though.
> > 
> > How does the following look for trunk and perhaps 13 (there should be
> > no functional change for code that doesn't use auto(x))?
> > 
> > [1]: https://gcc.gnu.org/pipermail/gcc-patches/2022-January/587818.html
> > 
> > -- >8 --
> > 
> > 	PR c++/110025
> > 	PR c++/114138
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-tree.h (make_cast_auto): Declare.
> > 	* parser.cc (cp_parser_functional_cast): Replace a parsed auto
> > 	with a level-less one via make_cast_auto.
> > 	* pt.cc (find_parameter_packs_r): Don't treat level-less auto
> > 	as a type parameter pack.
> > 	(tsubst) <case TEMPLATE_TYPE_PARM>: Generalized CTAD placeholder
> > 	handling to all level-less autos.
> > 	(make_cast_auto): Define.
> > 	(do_auto_deduction): Handle replacement of a level-less auto.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp23/auto-fncast16.C: New test.
> > 	* g++.dg/cpp23/auto-fncast17.C: New test.
> > 	* g++.dg/cpp23/auto-fncast18.C: New test.
> > ---
> >   gcc/cp/cp-tree.h                           |  1 +
> >   gcc/cp/parser.cc                           | 11 ++++
> >   gcc/cp/pt.cc                               | 37 +++++++++++-
> >   gcc/testsuite/g++.dg/cpp23/auto-fncast16.C | 12 ++++
> >   gcc/testsuite/g++.dg/cpp23/auto-fncast17.C | 15 +++++
> >   gcc/testsuite/g++.dg/cpp23/auto-fncast18.C | 69 ++++++++++++++++++++++
> >   6 files changed, 142 insertions(+), 3 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast16.C
> >   create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast17.C
> >   create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast18.C
> > 
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 04c3aa6cd91..6f1da1c7bad 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7476,6 +7476,7 @@ extern tree make_decltype_auto
> > (void);
> >   extern tree make_constrained_auto		(tree, tree);
> >   extern tree make_constrained_decltype_auto	(tree, tree);
> >   extern tree make_template_placeholder		(tree);
> > +extern tree make_cast_auto			(void);
> >   extern bool template_placeholder_p		(tree);
> >   extern bool ctad_template_p			(tree);
> >   extern bool unparenthesized_id_or_class_member_access_p (tree);
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 3ee9d49fb8e..3dbe6722ba1 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -33314,6 +33314,17 @@ cp_parser_functional_cast (cp_parser* parser, tree
> > type)
> >     if (!type)
> >       type = error_mark_node;
> >   +  if (TREE_CODE (type) == TYPE_DECL
> > +      && is_auto (TREE_TYPE (type)))
> > +    type = TREE_TYPE (type);
> > +
> > +  if (is_auto (type)
> > +      && !AUTO_IS_DECLTYPE (type)
> > +      && !PLACEHOLDER_TYPE_CONSTRAINTS (type)
> > +      && !CLASS_PLACEHOLDER_TEMPLATE (type))
> > +    /* auto(x) and auto{x} are represented by level-less auto.  */
> > +    type = make_cast_auto ();
> > +
> >     if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
> >       {
> >         cp_lexer_set_source_position (parser->lexer);
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 2803824d11e..369e33f23c7 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -3921,7 +3921,8 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees,
> > void* data)
> >   	 parameter pack (14.6.3), or the type-specifier-seq of a type-id that
> >   	 is a pack expansion, the invented template parameter is a template
> >   	 parameter pack.  */
> > -      if (ppd->type_pack_expansion_p && is_auto (t))
> > +      if (ppd->type_pack_expansion_p && is_auto (t)
> > +	  && TEMPLATE_TYPE_LEVEL (t) != 0)
> >   	TEMPLATE_TYPE_PARAMETER_PACK (t) = true;
> >         if (TEMPLATE_TYPE_PARAMETER_PACK (t))
> >           parameter_pack_p = true;
> > @@ -16297,9 +16298,19 @@ tsubst (tree t, tree args, tsubst_flags_t complain,
> > tree in_decl)
> >         }
> >         case TEMPLATE_TYPE_PARM:
> > -      if (template_placeholder_p (t))
> > +      if (TEMPLATE_TYPE_LEVEL (t) == 0)
> >   	{
> > +	  /* This is either an ordinary level-less auto or a CTAD placeholder
> > +	     auto.  These get replaced only via do_auto_deduction which, in
> > the
> > +	     ordinary case, temporarily overrides its level to 1 before
> > calling
> > +	     tsubst.  CTAD placeholders are replaced via do_class_deduction.
> > */
> > +	  gcc_checking_assert (is_auto (t));
> >   	  tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t);
> > +	  if (!tmpl)
> > +	    /* Ordinary level-less auto, nothing to substitute.  */
> > +	    return t;
> > +
> > +	  /* Substitute the template of this CTAD placeholder.  */
> >   	  tmpl = tsubst_expr (tmpl, args, complain, in_decl);
> >   	  if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM)
> >   	    tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl);
> > @@ -29311,6 +29322,17 @@ template_placeholder_p (tree t)
> >     return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t);
> >   }
> >   +/* Return an auto for an explicit cast, e.g. auto(x) or auto{x}.
> > +   Like CTAD placeholders, these have level 0 so that they're not
> > +   accidentally replaced via tsubst, and are always directly resolved
> > +   via do_auto_deduction.  */
> > +
> > +tree
> > +make_cast_auto ()
> > +{
> > +  return make_auto_1 (auto_identifier, true, /*level=*/0);
> > +}
> > +
> >   /* Make a "constrained auto" type-specifier. This is an auto or
> >     decltype(auto) type with constraints that must be associated after
> >     deduction.  The constraint is formed from the given concept CON
> > @@ -31213,7 +31235,16 @@ do_auto_deduction (tree type, tree init, tree
> > auto_node,
> >   	}
> >       }
> >   -  if (TEMPLATE_TYPE_LEVEL (auto_node) == 1)
> > +  if (TEMPLATE_TYPE_LEVEL (auto_node) == 0)
> > +    {
> > +      /* Substitute this level-less auto via tsubst by temporarily
> > +	 overriding its level to 1.  */
> > +      TEMPLATE_TYPE_LEVEL (auto_node) = 1;
> > +      type = tsubst (type, targs, complain, NULL_TREE);
> > +      TEMPLATE_TYPE_LEVEL (auto_node) = 0;
> > +      return type;
> > +    }
> > +  else if (TEMPLATE_TYPE_LEVEL (auto_node) == 1)
> >       /* The outer template arguments are already substituted into type
> >          (but we still may have used them for constraint checking above).
> > */;
> >     else if (context == adc_unify)
> > diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C
> > b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C
> > new file mode 100644
> > index 00000000000..e2c13f6b050
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C
> > @@ -0,0 +1,12 @@
> > +// PR c++/110025
> > +// { dg-do compile { target c++23 } }
> > +
> > +template<auto V, class = decltype(auto(V)), class = decltype(auto{V})>
> > +struct A { };
> > +
> > +template<auto V>
> > +A<V> f();
> > +
> > +int main() {
> > +  f<0>();
> > +}
> > diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C
> > b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C
> > new file mode 100644
> > index 00000000000..25186dfdbf2
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C
> > @@ -0,0 +1,15 @@
> > +// PR c++/110025
> > +// { dg-do compile { target c++23 } }
> > +
> > +template<class...> struct tuple;
> > +
> > +template<auto V>
> > +using constant_t = int;
> > +
> > +template<auto... V>
> > +using constants_t = tuple<constant_t<auto(V)>...>;
> > +
> > +using ty0 = constants_t<>;
> > +using ty1 = constants_t<1>;
> > +using ty2 = constants_t<1, 2>;
> > +using ty3 = constants_t<1, 2, 3>;
> > diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C
> > b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C
> > new file mode 100644
> > index 00000000000..4656723684f
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C
> > @@ -0,0 +1,69 @@
> > +// PR c++/114138
> > +// { dg-do compile { target c++23 } }
> > +
> > +namespace std {
> > +  template <class T>
> > +  T&& declval() noexcept requires true;
> > +
> > +  template <class>
> > +  void declval() noexcept;
> > +
> > +  namespace detail {
> > +    struct none_such;
> > +    template <class>
> > +    using none_such_t = none_such;
> > +
> > +    template <class T>
> > +      extern const none_such_t<T> _getter_for;
> > +
> > +    template <class T>
> > +    using _decay_t = decltype(auto(declval<T>()));
> > +
> > +    static_assert(__is_same_as(_decay_t<void>, void));
> > +  }
> > +
> > +  template <const auto& Fn, class... Args>
> > +    using _result_of_t = decltype(Fn(declval<Args>()...));
> > +
> > +  template <unsigned I, class Tuple>
> > +    using tuple_element_t =
> > _result_of_t<detail::_getter_for<detail::_decay_t<Tuple>>, char(*)[I+1],
> > Tuple>;
> > +
> > +  template <class First, class Second>
> > +  struct pair {
> > +    First first;
> > +    Second second;
> > +  };
> > +
> > +  template <class>
> > +    inline constexpr bool _is_pair = false;
> > +
> > +  template <class First, class Second>
> > +    inline constexpr bool _is_pair<pair<First, Second>> = true;
> > +
> > +  template <class T>
> > +    concept Pair = _is_pair<decltype(auto(std::declval<T>()))>;
> > +
> > +  template <unsigned I, Pair P>
> > +    requires (I <= 1)
> > +  decltype(auto) get(P&& p) noexcept {
> > +    if constexpr (I == 0) {
> > +      return (static_cast<P&&>(p).first);
> > +    } else {
> > +      return (static_cast<P&&>(p).second);
> > +    }
> > +  }
> > +
> > +  namespace detail {
> > +    inline constexpr auto _pair_getter =
> > +      []<unsigned J, class Pair>(char(*)[J], Pair&& p) noexcept ->
> > decltype(auto) {
> > +        return std::get<J-1>(static_cast<Pair&&>(p));
> > +      };
> > +
> > +    template <class First, class Second>
> > +      inline constexpr auto _getter_for<pair<First, Second>> =
> > _pair_getter;
> > +  }
> > +
> > +}
> > +
> > +static_assert(__is_same_as(int&, std::tuple_element_t<0, std::pair<int,
> > float>&>));
> > +static_assert(__is_same_as(float&&, std::tuple_element_t<1, std::pair<int,
> > float>&&>));
> 
> 


  reply	other threads:[~2024-03-01 15:17 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-27 20:48 Patrick Palka
2024-02-28 21:43 ` Jason Merrill
2024-02-29 19:17   ` Patrick Palka
2024-03-01 13:39     ` Jason Merrill
2024-03-01 15:17       ` Patrick Palka [this message]
2024-03-01 15:27         ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b8e7d048-d97d-a0a5-ee5b-9a64c669a395@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).