Bug 101681 - PMF comparison to nullptr is not considered a constexpr inside a template argument
Summary: PMF comparison to nullptr is not considered a constexpr inside a template arg...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 12.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
Depends on:
Blocks: constexpr pmf, ptmf
  Show dependency treegraph
 
Reported: 2021-07-29 21:05 UTC by Andrew Pinski
Modified: 2022-01-11 18:40 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Pinski 2021-07-29 21:05:58 UTC
Take:
template <bool> struct Z { };

struct C
{
    void f();
    Z<&C::f == nullptr> z;
};
---- CUT ---
This should be accepted in C++11 or newer.
Comment 1 Steven Sun 2021-08-06 19:08:43 UTC
The following program compiles. https://godbolt.org/z/aTvchYxYW

```
struct C {
  void f() {}
  static_assert(__builtin_constant_p(&C::f));
  static_assert(!__builtin_constant_p(&C::f == nullptr)); // not nonzero yet
};

static_assert(__builtin_constant_p(&C::f == nullptr)); // nonzero now

struct D {
  void f() {}
  static_assert(__builtin_constant_p(&C::f == nullptr));
  static_assert(!__builtin_constant_p(&D::f == nullptr));
};

static_assert(__builtin_constant_p(&C::f == nullptr));
static_assert(__builtin_constant_p(&D::f == nullptr));

```


Looks that the `&C::f` is known to be constexpr right after the function was parsed.

But only when the class completely parsed, its value was assigned. We can then compare it to nullptr.


To make code in comment0 accepted, we need some kind of `not null' mark on the expression tree. 0ne possible way is to assign the `&C::f` in advance, right after it was parsed.
Comment 2 Steven Sun 2021-08-07 00:33:57 UTC
The root cause for this is that the compiler forbids constant folding when involving PMF of an incomplete class.

https://gcc.gnu.org/git?p=gcc.git;a=blob;f=gcc/cp/expr.c;h=d16d1896f2ddd08264b389b02b9640cca332ec13;hb=refs/heads/master#l42

(gcc/cp/expr.c)
> 42   /* We can't lower this until the class is complete.  */
> 43         if (!COMPLETE_TYPE_P (DECL_CONTEXT (member)))
> 44           return cst;


If we comment this `if`, the constant folding will succeed at


(gcc/cp/expr.c)
> 67             expand_ptrmemfunc_cst (cst, &delta, &pfn);
> 68             cst = build_ptrmemfunc1 (type, delta, pfn);


solving everything.
Comment 3 Steven Sun 2021-08-07 00:43:14 UTC
By the way, in the current design, the class definition is passed twice in order we can see every member data/function declaration before parsing NSDMI and member functions.

The class is complete after parsing all declaration, which means `&C::f == nullptr` can reduce to false since that.

So, under current design, the following code compiles on GCC.
https://godbolt.org/z/fMTsf4KoM

```
struct C {
  C() {
    static_assert(&C::f != 0);  // complete type
  }
  void f() noexcept(&C::f != 0) {
    static_assert(&C::f != 0);  // complete type
  }
  static_assert(__builtin_constant_p(&C::f));        // incomplete type
  static_assert(!__builtin_constant_p(&C::f == 0));  // incomplete type
};

static_assert(&C::f != 0);  // complete type

```
Comment 4 Andrew Pinski 2022-01-11 18:40:09 UTC
Note completeness of the class does not matter either:
template <bool> struct Z { };
struct C
{
    void f();
};
static_assert ((&C::f == 0) == false,"");
Z<&C::f == 0> z;

--- CUT ---
The static_assert passes just fine but the template is still rejected.