Skip to content

CWG3018 [cpp.cond] Ambiguous validity of defined in __has_embed #694

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

Open
hubert-reinterpretcast opened this issue Mar 24, 2025 · 8 comments

Comments

@hubert-reinterpretcast
Copy link
Collaborator

Full name of submitter (unless configured in github; will be published with the issue): Hubert Tong

Reference (section label): cpp.cond

Link to reflector thread (if any): N/A

Issue description:
Consider (with the proposed offset parameter for a more plausible use case):

#if __has_embed(__FILE__ offset(\
  defined(WANT_4K_SOURCE) ? 4095 : 0\
)) == __STDC_EMBED_EMPTY__
#error Desired source not found
#endif

It is unclear whether the above is ill-formed due to https://wg21.link/cpp.embed.param.limit#1 because https://wg21.link/cpp.cond#12 can be read as saying that the evaluation of the defined-macro-expression has occurred prior to the evaluation of the has-embed-expression.

Suggested resolution:
Add a restriction that

A defined-macro-expression shall not appear within a has-embed-expression.

"Editor's" note: This is for consistency with the restriction in the context of #embed, which is (in turn) motivated by the special macro expansion logic associated with defined only in the [cpp.cond] context. Since we are already in the [cpp.cond] context for __has_embed, there is no major design barrier for specifying that the defined-macro-expression‌s are evaluated first.

@jensmaurer
Copy link
Member

I notice that the phrasing in [cpp.embed.param.limit] p1 is bad regardless:

The token defined shall not appear in the constant-expression.

There is no constant-expression at that point; it is formed later in p2.

@jensmaurer
Copy link
Member

there is no major design barrier for specifying that the defined-macro-expression‌s are evaluated first.

I'm confused. This sounds like it's fine to have defined(X) anywhere in #if, including inside __has_embed (we're in a special context anyway), yet you want to add a rule that prohibits defined(X) inside __has_embed.

Should we just specify for #embed that defined is bad anywhere (now that we macro-expand everywhere), and have a parallel top-level restriction for within __has_embed(...) ?

@jensmaurer
Copy link
Member

jensmaurer commented Mar 25, 2025

Or do you want to simply allow defined also in #embed parameters, because it's obviously the right way given how the delegation to #if works?

@hubert-reinterpretcast
Copy link
Collaborator Author

hubert-reinterpretcast commented Mar 25, 2025

Or do you want to simply allow defined also in #embed parameters, because it's obviously the right way given how the delegation to #if works?

This alternative was why we disallowed defined in #embed in the first place. If we allowed it, then the "natural expectation" is that you are supposed to expand any potential macro name after defined in a prefix parameter but not in a limit parameter.

@hubert-reinterpretcast
Copy link
Collaborator Author

I'm confused. This sounds like it's fine to have defined(X) anywhere in #if, including inside __has_embed (we're in a special context anyway), yet you want to add a rule that prohibits defined(X) inside __has_embed.

Yes, it can be fine. If we want it to be fine, I said that we should specify that the evaluation of defined happens first.

Should we just specify for #embed that defined is bad anywhere (now that we macro-expand everywhere), and have a parallel top-level restriction for within __has_embed(...) ?

I don't think so. defined not having any special macro non-expansion behaviour in the context of a prefix parameter is fine.
The only mechanism needed for __has_embed if we want a diagnostic for defined is to defer to #embed except that we need to avoid prior evaluation of defined.

@jensmaurer
Copy link
Member

I'm sure I'm being stupid here, but let's start at the beginning, please.

For #embed, we (nowadays) macro-expand the entire thing without protecting macro names after "defined". For the limit parameter, we branch off to the #if rules for the constant-expression evaluation, and we say

The token defined shall not appear in the constant-expression.

So, we make sure we get a deterministic error message in such a case. Example:

#define X Z
#embed "somefile.h" limit(defined(X) + 10)

yields

#embed "somefile.h" limit(defined(Z) + 10)

which would work after applying the #if rules, and surprisingly yields "limit(10)". But we make it a syntax error because we pass "defined" to the #if rules.

Now, on to __has_embed.

#define X Z
#if __has_embed("somefile.h" limit(defined(X))) == 42

There, we are in an #if context, thus we don't macro-expand X right away, and the recursive invocation of the #if evaluation rules would see defined(X), which is not what happens for the equivalent #embed.

So, we want your original suggestion:

A defined-macro-expression shall not appear within a has-embed-expression.

Alternatively, we could say we don't evaluate defined(something) inside a __has_embed early, but instead pass it to #embed, at which point it would become ill-formed there. This latter approach seems more complicated to word.

@hubert-reinterpretcast
Copy link
Collaborator Author

So, we want your original suggestion

Sounds good to me.

@jensmaurer
Copy link
Member

CWG3018

I've also disallowed

#define EMPTY
#if __has_include(EMPTY <defined(X)blah.h>)

for symmetry.

But #if __has_include(<defined(X)blah.h>) is allowed, because of header-name lexing the defined into hiding.

@jensmaurer jensmaurer changed the title [cpp.cond] Ambiguous validity of defined in __has_embed CWG3018 [cpp.cond] Ambiguous validity of defined in __has_embed Mar 25, 2025
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

2 participants