Bug 98939 (P1787R6) - [C++23] Implement P1787R6 "Declarations and where to find them"
Summary: [C++23] Implement P1787R6 "Declarations and where to find them"
Status: ASSIGNED
Alias: P1787R6
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.0
: P3 normal
Target Milestone: ---
Assignee: Marek Polacek
URL: http://wg21.link/p1787r6
Keywords:
Depends on: cwg563 28985 CWG1635 cwg1837 C++DR244
Blocks: CWG1291, CWG2396 c++23-core 103555
  Show dependency treegraph
 
Reported: 2021-02-02 19:40 UTC by Jason Merrill
Modified: 2022-05-09 18:00 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-02-02 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jason Merrill 2021-02-02 19:40:25 UTC
The changes from this paper should not affect a significant amount of code; many are clarifications that bring the wording in line with existing practice, some are clarifications of corner cases that most code doesn't depend on, like ambiguous lookup within a conversion-type-id.

A few changes that allow code that has been ill-formed:

conversion-type-id is added to the list of type-only contexts from P0634:

template <class T> struct A { operator T::type(); }; // OK

::template is also not required in type-only contexts:

template <class T> auto f(T t) { return static_cast<T::X<int>>(t); } // OK

Default template arguments are now complete-class contexts, like default function arguments:

template <class T> struct A {
  template <int I = sizeof(t)> void g() { } // OK
  T t;
};

One change that might break a small amount of existing code:

Since lookup for a name after . or -> now happens first in the scope of the object, .template is required in dependent.template X<...> even if a definition of X would be found by unqualified lookup.

template <int> struct X { void f(); };
template <class T> void g(T t) { t.X<2>::f(); } // error, needs .template
Comment 1 Marek Polacek 2021-07-08 17:05:04 UTC
(In reply to Jason Merrill from comment #0)
> The changes from this paper should not affect a significant amount of code;
> many are clarifications that bring the wording in line with existing
> practice, some are clarifications of corner cases that most code doesn't
> depend on, like ambiguous lookup within a conversion-type-id.
> 
> A few changes that allow code that has been ill-formed:
> 
> conversion-type-id is added to the list of type-only contexts from P0634:
> 
> template <class T> struct A { operator T::type(); }; // OK

This already works; I fixed it via DR 2413 in GCC 10.

> ::template is also not required in type-only contexts:
> 
> template <class T> auto f(T t) { return static_cast<T::X<int>>(t); } // OK

This needs to be implemented, but it's in the spirit of P0634 which I implemented.  Strongly related to DR 1478.

It's also reminiscent of CWG 96 which is assigned to me.

> Default template arguments are now complete-class contexts, like default
> function arguments:
> 
> template <class T> struct A {
>   template <int I = sizeof(t)> void g() { } // OK
>   T t;
> };

This is DR 1635 / bug 57314.

Since I've dealt with deferred parsing quite often recently (in the context of deferred noexcept parsing), I might as well tackle this one too.  My hope is that the very same trick of stashing the tokens and then reparsing them at the end of the class will work here too.  This will probably need some kind of tparm -> defarg mapping.

So it looks like I'm in a position to fix at least parts of this proposal, thus mine for now.


A question worth considering is whether we only want to allow the code in C++23, or whether we really want to treat those issues as DRs, and so allow the code in previous modes.  P0634 is only enabled in C++20, so it makes the best sense to me to only allow the above in C++23.
Comment 2 Marek Polacek 2021-11-12 21:14:34 UTC
I'm having trouble with "Default template arguments are now complete-class contexts":

class C {
  template <typename...> struct _List;

  template <unsigned long, typename, bool B = value> struct S; // #1

  template <unsigned long _Sz, typename _Uint, typename... _UInts>
  struct S<_Sz, _List<_Uint, _UInts...>>; // #2

  static constexpr bool value = false;
};

#2 wants to lookup_template_class S, but S can contain a DEFERRED_PARSE, so that's not going to work.  Perhaps we have to delay finish_template_type until the end of class somehow...
Comment 3 Jason Merrill 2021-11-18 17:38:01 UTC
(In reply to Marek Polacek from comment #2)
> class C {
>   template <typename...> struct _List;
> 
>   template <unsigned long, typename, bool B = value> struct S; // #1
> 
>   template <unsigned long _Sz, typename _Uint, typename... _UInts>
>   struct S<_Sz, _List<_Uint, _UInts...>>; // #2
> 
>   static constexpr bool value = false;
> };
> 
> #2 wants to lookup_template_class S, but S can contain a DEFERRED_PARSE, so
> that's not going to work.  Perhaps we have to delay finish_template_type
> until the end of class somehow...

As I was saying in our meeting, I think in this testcase #2 is ill-formed because it depends on a default argument that hasn't been parsed yet.
Comment 4 Marek Polacek 2021-11-18 20:05:15 UTC
Does that mean that code like this (from type_traits) needs to be fixed?

  class __make_unsigned_selector_base
  {
  protected:
    template<typename...> struct _List { }; 

    template<typename _Tp, typename... _Up> 
      struct _List<_Tp, _Up...> : _List<_Up...>
      { static constexpr size_t __size = sizeof(_Tp); };

    template<size_t _Sz, typename _Tp, bool = (_Sz <= _Tp::__size)>
      struct __select;

    template<size_t _Sz, typename _Uint, typename... _UInts>
      struct __select<_Sz, _List<_Uint, _UInts...>, true>
      { using __type = _Uint; };

    template<size_t _Sz, typename _Uint, typename... _UInts>
      struct __select<_Sz, _List<_Uint, _UInts...>, false>
      : __select<_Sz, _List<_UInts...>>
      { }; 
  };

when parsing the default template argument I can't know if it can be parsed right away or if I need to delay parsing (unless it's a simple literal, which in this case it isn't).
Comment 5 Patrick Palka 2021-11-18 21:59:12 UTC
Some further interesting examples:

struct A {
  template<class T, int=42> struct B;
  template<class T> struct B<T> { };     // #1
  template<class T> struct B<T, 42> { }; // #2
};

Presumably we should continue to diagnose that #2 is a redefinition of #1.


And for class-scope explicit specializations (which GCC doesn't yet support):

struct A {
  template<class T, int=T::value> void f();  // #1
  template<class T> void f();                // #2
  template<> void f<int>();                  // #3, specialization of #2
};

The above testcase was valid before P1787, is it still so?
Comment 6 Andrew Pinski 2021-12-03 05:02:07 UTC
Note I think this paper applies to C++20 too or at least part of it.

From CWG1291:
[Accepted at the November, 2020 meeting as part of paper P1787R6 and moved to DR at the February, 2021 meeting.]
Comment 7 Jason Merrill 2021-12-03 20:41:08 UTC
(In reply to Andrew Pinski from comment #6)
> Note I think this paper applies to C++20 too or at least part of it.
> 
> From CWG1291:
> [Accepted at the November, 2020 meeting as part of paper P1787R6 and moved
> to DR at the February, 2021 meeting.]

Yes, but I think we need to be conservative about changes that break existing code.  My expectation in comment #0 that the amount of code broken would be small seems to have been over-optimistic.
Comment 8 Marek Polacek 2022-02-28 14:55:46 UTC
Additional comments about this proposal:
https://gcc.gnu.org/pipermail/gcc-patches/2021-August/578297.html