Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[basic.def.odr] lambda-expression example seemingly constradicts normative wording #6364

Open
Eisenwave opened this issue Jun 30, 2023 · 7 comments

Comments

@Eisenwave
Copy link
Contributor

On one hand, lambda-expressions in default arguments are not required to have the same closure type:

In each such definition, except within the default arguments and default template arguments of D, corresponding lambda-expressions shall have the same closure type (see below).

http://eel.is/c++draft/basic.def.odr#14.6

On the other hand, the example shows the opposite:

inline void g(bool cond, void (*p)() = []{}) {
  if (cond) g(false);
}

If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type.

http://eel.is/c++draft/basic.def.odr#16

I can't make sense of it; it seems like a direct contradiction to me.

@Eisenwave Eisenwave changed the title [basic.def.odr] Example seemingly constradicts normative wording [basic.def.odr] lambda-expression example seemingly constradicts normative wording Jun 30, 2023
@frederick-vs-ja
Copy link
Contributor

I think p14.6 specifies behavior of implementations, while the example shows potentially invalid use in user codes due to such behavior, so there's no contradiction.

@Eisenwave
Copy link
Contributor Author

Eisenwave commented Jun 30, 2023

I think p14.6 specifies behavior of implementations, while the example shows potentially invalid use in user codes due to such behavior, so there's no contradiction.

My issue is that 14.6 says except within the default arguments, so it seemingly excludes the behavior described in the example.

The recursive call to g(false) should create a closure type inside of g, presumably.

@JohelEGP
Copy link
Contributor

except within the default arguments

It's saying that the closure types are the same except in that case.

@Eisenwave
Copy link
Contributor Author

except within the default arguments

It's saying that the closure types are the same except in that case.

It's not saying that; the quote I've posted is just one of many requirements for the ODR not to be violated.

@Eisenwave
Copy link
Contributor Author

Eisenwave commented Jun 30, 2023

After discussing it with a handful of other people, we managed to wrap our heads around it. A much better example would be:

inline void g(void (*p)() = []{}) {}

inline void g2() {
    // ill-formed, no diagnostic required
    // use of default argument defines a closure type outside of g2
    // which is not the same across TUs
    g();
}

@JohelEGP
Copy link
Contributor

So the current example also requires http://eel.is/c++draft/basic.def.odr#14.11 to make sense.
Is that right?

@Eisenwave
Copy link
Contributor Author

Eisenwave commented Jun 30, 2023

So the current example also requires http://eel.is/c++draft/basic.def.odr#14.11 to make sense. Is that right?

Not really. g making that recursive call means that []{} is introduced into its definition for ODR purposes, but that doesn't create any additional problems. We violate the ODR with lambdas when they don't have the same closure type in every TU; that's the actual issue.

It may look like p14.11 is relevant to the example with g, but it isn't. The problem is where our closure types are located.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants