Skip to content

Rough draft of Section 3.5 conditionals: #if, #elif, etc. #61

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
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 122 additions & 3 deletions drafts/25-xxx-specifications.txt
Original file line number Diff line number Diff line change
Expand Up @@ -413,22 +413,141 @@ in28. Unlike INCLUDE lines (see the section on "INCLUDE lines"),
the #include directive is not prohibited from including the
same source file at a deeper level of nesting.


3.5 The '#if', '#ifdef', '#ifndef', '#elif', '#elifdef',
'#elifndef', '#else', '#endif' directives
'#elifndef', '#else', '#endif' conditional directives
---------------------------------------------------------

Example syntax:
Example syntax (extra spacing for illustration purposes only):
General form:
<if-head> #endif EOL
<if-head> <if-elif> #endif EOL
<if-head> <if-elif> <if-else> #endif EOL

Where:
<if-head> is one of:
#if token-list EOL <lines>
#ifdef ID EOL <lines>
#ifndef ID EOL <lines>

<if-elif> is zero or more of:
#elif token-list EOL <lines>
#elifdef ID EOL <lines>
#elifndef ID EOL <lines>

<if-else> is zero or one of:
#else EOL <lines>

<lines> are zero or more of:
directive lines
Fortran comment lines
Fortran source lines


3.5.1 Static semantics specifications
--------------------------------------
-------------------------------------

if05. A #if, #ifdef, or #ifndef directive signals the start of a list
of conditional directives.

if10: Whitespace must immediately follow the directives tokens
'ifdef', 'ifndef', 'elifdef', and 'elifndef' to separate them
from the ID tokens.
Comment on lines +453 to +455
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is roughly true for (nearly?) every preprocessing directive, not even just those in this section. Stating it here for only three makes them seem like an exception. (I say roughly because what's actually required is a token boundary, which might or might not include whitespace)

I think what we actually mean here is that standard directives are only recognized as such if the token immediately following the # introducing a directive line exactly matches one of the standard directive names. If it does not match for any reason (e.g. due to lack of whitespace) then the directive does not conform to one of the standard directives and is instead parsed as a processor-dependent directive.

We should probably directly state something to this effect in section 3.0 and then delete this rule.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section 2.3 is titled "Significance of whitespace" - would this fit there?

Copy link
Member Author

@gklimowicz gklimowicz May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I replied that Section 2.3 seems like an excellent place for it. I will generate a pull request for section 2 for this. See PR #64, up for review.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try some phrasing like "I think what we actually mean here is that standard directives are only recognized as such if the token immediately following the # introducing a directive line exactly matches one of the standard directive names."


if15. The list of #elif, #elifdef, and #elifndef directives, and the
#else directive (if present) are part of the same list of
directives introduced by the #if directive.

if20. The chain of conditional directives ends with the #endif
directive.

if25. Within a conditional directive list, a #if, #ifdef, or #endif
directive introduces a new list of conditional directives, at a new
nesting level, within the enclosing conditional directive list.

if30. The conditional directive lists must properly nest.

if35. As shown in the illustrative syntax, there should be no
unmatched #endif directives.
Comment on lines +470 to +471
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property can be directly derived from the pseudo-grammar above, so repeating it here is redundant. Worse, we only state one such property but omit others, for example:

  1. there should be no <if-head> without a matching #endif,
  2. there should be no <if-elif> without a matching <if-head> and #endif,
  3. there should be no #else without a matching <if-head> and #endif,

which makes this one seem like an outlier. We should either list all such analogous grammatical properties or none.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with letting the grammar speak for itself here, as long as we believe J3 as a whole won't feel like it's under-specified.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove if35.


if40. As shown in the illustrative syntax, there should be no more
than one #else directive in a list of conditional directives at
any nesting level.


3.5.2 Evaluation semantics specifications
-----------------------------------------


ix05. The #ifdef and #ifndef directives are evaluated as if they had been
written "#if defined(ID)" and "#if !defined(ID)" respectively.
(For brevity, the descriptions of #ifdef and #ifndef directives
below are omitted, and assume this transformation.)

ix10. The #elifdef and #elifndef macros are evaluated as if they had
been written "#elif defined(ID)" and "#elif !defined(ID)"
respectively.

ix15. The token-lists in #if and #elif are processed as described in
the section "Macro recognition and expansion".
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted in the meeting, we also need to clarify whether macro expansion occurs within an #elif directive line if we already know that "group" is being skipped


ix20. After expansion, the resulting token list in #if and #elif
directives must represent a valid integer expression as
described in the section "Expressions allowed in #if and #elif
directives". This expression is called the "controlling
expression" for the directive.

ix25. Controlling expressions in #if and #elif directives are parsed
and evaluated according to the semantic rules described in
section "Expressions allowed in #if and #elif directives".

ix30. If the controlling expression in a #if evaluates to a nonzero
value, the <lines> immediately following the #if participate in
preprocessing. They are subject to preprocessing expansion,
substitution, and evaluation of processing directives in the
<lines>.

ix40. Further preprocessing when #if controlling expression evaluates
to a nonzero value proceeds. Subsequent #elif and #else
directives at the same level of nesting are skipped, and do not
participate in preprocessing except to be examined for proper
nesting of conditional directives.
Comment on lines +504 to +514
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unhappy with the structure of how we describe the controlled <lines> here and in the subsequent rules.

In particular, the definition of phrases like " immediately following the #if" and " immediately following the #elif" is too ambiguous, especially if those <lines> themselves contain directives (and possibly nested conditionals).

I think we should instead use the grammar non-terminals to help make this more precise. For example:

Suggested change
ix30. If the controlling expression in a #if evaluates to a nonzero
value, the <lines> immediately following the #if participate in
preprocessing. They are subject to preprocessing expansion,
substitution, and evaluation of processing directives in the
<lines>.
ix40. Further preprocessing when #if controlling expression evaluates
to a nonzero value proceeds. Subsequent #elif and #else
directives at the same level of nesting are skipped, and do not
participate in preprocessing except to be examined for proper
nesting of conditional directives.
ix30. If the controlling expression in an <if-head> construct evaluates to a nonzero
value, then the <lines> contained within that <if-head> construct participate in
preprocessing. They are subject to preprocessing expansion,
substitution, and evaluation of processing directives in those
<lines>.
ix40. Further preprocessing when an <if-head> controlling expression evaluates
to a nonzero value proceeds. Subsequent <if-elif> and <if-else>
constructs at the same level of nesting are skipped, and the <lines>
contained within those constructs do not participate in preprocessing
except to be examined for proper nesting of conditional directives.

and similarly for the next four rules.

Thoughts?

Copy link
Member Author

@gklimowicz gklimowicz May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these updates to the phrasing. I am OK committing the phrasing for this one as suggested, and then updating the PR with new commits to add changes for the remaining rules.

Better yet, Dan could make the suggestions for the other rules, and then get credit for the contribution. :-)

Copy link
Member Author

@gklimowicz gklimowicz May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will make these changes. And others, regarding use of [ ] for optional things, and -list for lists.

See F2023 §4.1.3 for rules we should use.


ix45. If the controlling expression in a #if evaluates to a zero
value, the lines immediately following the #if directive are
skipped, and examined only to determine nesting of conditionals.
Within these lines, no token expansion occurs, and no Fortran
comment lines nor source lines are made available for subsequent
processing by the processor. Preprocessing continues with any
Comment on lines +519 to +521
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to specifically call out that macros in the body are not evaluated in any way, in addition to the lack of expansion and source lines? I'm thinking of things like #error, #warning, and #pragma which aren't really about token expansion or source lines.

Copy link
Member Author

@gklimowicz gklimowicz May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. We should be clear that in the skipped lines,

  • Nested conditional directives are recognized but no evaluated
  • No other directives are evaluated
  • No macro expansion occurs
  • No directive lines, Fortran source lines, or Fortran comment lines are made available for subsequent processing

To Dan's point about nesting, maybe we can omit that first bullet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure that skipped parts act as if they aren't there, and have no effect on the preprocessing.

#elif directives at the same level of nesting, as described
below.

ix50. When the #if controlling expression evaluates to zero, the
controlling expressions in each of the #elif directives at the
same nesting level are evaluated in turn. Those whose expression
evaluate to false are skipped in the same manner as when a #if
clause evaluates to false.

ix55. When a #elif controlling expression evaluates to a nonzero
value, the <lines> immediately following the #elif participate
in preprocessing just as they do when the controlling expression
in a #if evaluates to a nonzero value. Subsequent #elif and
#else directives at the same nesting level are skipped, as
described for #if.

ix60. When all controlling expression in #if or #elif directives
evaluates to a zero value and a #else directive is present at
the same nesting level, the <lines> between the #else directive
and the matching #endif directive are subject to preprocessing,
expansion and substitution as they would be in #if true
processing.

ix65. When all controlling expression in #if or #elif directives
evaluates to a zero value and no #else directive is present at
the same nesting level, there are no <lines> in the list of
conditional directives subject to preprocessing.


3.6 The '#error' and '#warning' directives
------------------------------------------

Expand Down