Bug 110497 - Wrong error on non-static data member referenced in concept definition
Summary: Wrong error on non-static data member referenced in concept definition
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.1.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: rejects-valid
Depends on:
Blocks: concepts
  Show dependency treegraph
 
Reported: 2023-06-30 09:23 UTC by Fedor Chelnokov
Modified: 2023-07-02 08:08 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-06-30 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Fedor Chelnokov 2023-06-30 09:23:52 UTC
This program

struct B {
    const bool b = true;
};

template <typename T>
concept C = T::b;
static_assert( !C<B> );

is accepted in Clang. And I think correctly accepted because of substitution failure per https://eel.is/c++draft/temp.constr.constr#temp.constr.op-5

> [...] Furthermore, if substitution to determine whether an atomic constraint is satisfied ([temp.constr.atomic]) encounters a substitution failure, the constraint is not satisfied, regardless of the presence of a negation operator.

But GCC emits a weird error (at least the error text must be adjusted):

error: '*(B*)(void)0' is not a constant expression

Online demo: https://gcc.godbolt.org/z/xKf13cb6v
Comment 1 Patrick Palka 2023-06-30 13:44:35 UTC
It seems we're issuing a hard error during satisfaction here because substitution failure didn't occur, and the substituted constraint is non-constant which renders the program ill-formed as per [temp.constr.atomic]/3:

> If substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise, the lvalue-to-rvalue conversion is performed if necessary, and E shall be a constant expression of type bool.

One might argue substitution failure should occur for T::b with T=B, but this seems to be permitted by [expr.prim.id.general]/3 since constraint-expressions are unevaluated contexts.

Note that if we use B::b directly in the concept definition then Clang also rejects the program due to non-constant satisfaction rule:

struct B { const bool b = true; };
template <typename T>
concept C = B::b;
static_assert( !C<B> );

error: substitution into constraint expression resulted in a non-constant expression

So GCC might be correct to reject the original program.
Comment 2 Daniel Petrovic 2023-07-02 08:08:51 UTC
Just some observations:

There was a similar discussion in

https://stackoverflow.com/questions/75443227/static-assert-on-an-ill-formed-no-diagnostic-required-expression


Note that if we use anything else which leads to the ill-formed expression for T::b itself, e.g. B&, B*, int - gcc compiles without errors:

struct B {
    const bool b = true;
};

template <typename T>
concept C = T::b;

static_assert( !C<B*> );
static_assert( !C<B&> );
static_assert( !C<int> );

https://gcc.godbolt.org/z/9c6YW1vWe

I would expect (at least as a user) the same behaviour for static_assert(!C<B>) too, in that case.

So gcc seems to consider T::b (with T=b) in this context as a well-formed expression, but not a constant expression.

Looks strange.