cplusplus / CWG

Core Working Group
23 stars 7 forks source link

CWG2893 [stmt.if] Instantiations in `if constexpr (false)` outside templates #535

Open Eisenwave opened 1 month ago

Eisenwave commented 1 month ago

Reference: [stmt.if] p2

Link to reflector/Mattermost: https://lists.isocpp.org/core/2024/05/15848.php, https://chat.isocpp.org/general/pl/5gki63y36fd9m8wwk77qzofica

Issue Description

int main() {
    auto f = [](auto s) {
        s.x = 0;
    };
    if constexpr (false) {
        f(0);
    }
}

This program is well-formed because neither of the conditions (see [temp.inst] p5) for instantiating the call operator template of the generic lambda are met:

However, MSVC, GCC, and Clang reject this program. Only Clang rejects it for a lambda with -> void return type. It would be burdensome for users and implementors if this program was well-formed.

An instantiation might still be needed considering that the lambda can have a return type with a private destructor, or be [[nodiscard]], although the rules are unclear for this.

Suggested Resolution

In [smt.if] p2, re-define discarded statement to only apply within a templated entity:

 If the value of the converted condition
+of a constexpr if statement in an enclosing templated entity ([temp.pre])
 is false,
 the first substatement is a discarded statement.
 During the instantiation of an enclosing templated entity
-([temp.pre])
 ...
t3nsor commented 1 month ago

if constexpr in non-dependent contexts has two special powers. The discarded statements don't odr-use entities (well, technically they do odr-use them, it's just that this odr-use doesn't require a definition), and don't participate in return type deduction. Instead of removing those special powers, we should just clarify that the definition affects the semantics in this case.

Add a paragraph after [temp.inst]/8:

The existence of a definition of a function is considered to affect the semantics of the program if the function is named by an expression ([basic.def.odr]) and its declared return type is a placeholder type ([dcl.spec.auto]).

Eisenwave commented 1 month ago

@t3nsor what about the compiler divergence for -> void? Clang would be wrong then if we preserved these properties outside of templates.

Even with insisting on instantiation of deduced return type functions, I think it would rustle a lot of people's jimmies if non-deduced lambdas weren't instantiated, and I'm not convinced we want to keep that behavior.

t3nsor commented 1 month ago

I think Clang just has a bug in the -> void case, unless someone convinces me otherwise.

The current wording is clear that something like if constexpr(false) { f(0); } doesn't require f to have a definition anywhere in the program (in the non-deduced return type cases). I don't think we should be messing with that in CWG and breaking the code of anyone who relies on this. So if you want to force instantiation in the -> void case then you should come up with some other specification technique to make it so. But I don't really see the point of spending effort on this (and you'll probably still break some people's code).

For the decltype(s.x) case, you get a substitution failure, and overload resolution fails. We don't need any change to the wording to make it clear that it's ill-formed.

jensmaurer commented 1 month ago

CWG2893