Skip to content

fix: always emit resync linemarker after eval directives#66

Open
sbryngelson wants to merge 2 commits into
aradi:mainfrom
sbryngelson:fix/eval-resync-linemarker
Open

fix: always emit resync linemarker after eval directives#66
sbryngelson wants to merge 2 commits into
aradi:mainfrom
sbryngelson:fix/eval-resync-linemarker

Conversation

@sbryngelson
Copy link
Copy Markdown

@sbryngelson sbryngelson commented May 13, 2026

Problem

_postprocess_eval_line only emitted a trailing resync linemarker when
unsync or multiline was true. For single-line $: / @: / inline
#{...}# calls where span[0] == span[1] and no continuation folding
occurred, both flags were False — so no resync was written.

This causes silent line-number corruption whenever the expanded
macro body contains preprocessor conditionals (#ifdef / #endif).
Downstream Fortran preprocessors (e.g. gfortran -cpp) skip the
content of never-taken branches, but the physical source lines inside
those branches still advance the compiler's internal line counter.
Without a resync marker immediately after the expansion, every
subsequent statement is attributed to the wrong source line, producing
misleading compile-time diagnostics that point to completely unrelated
code.

Minimal reproducer

! test.fpp
#:def GUARD()
#ifdef NEVER
! stripped line
#endif
#:enddef
real :: x
$:GUARD()
x = 1.0  ! <-- error here should point to this line

Without the fix, gfortran -fsyntax-only -cpp reports errors on the
wrong lines because the three physical lines of the skipped #ifdef
block are not backed out of the compiler's line counter. With the fix,
the resync marker # 9 "test.fpp" is emitted after the expansion and
gfortran lands on the correct line.

Fix

Remove the if unsync or multiline: guard so a resync linemarker is
always emitted after every eval directive when --line-numbering
is active. The max(span[1], span[0] + 1) expression that correctly
handles the span[0] == span[1] case for inline directives was already
present inside the guard — it just needed to be unconditional.

# Before
if unsync or multiline:
    nextline = max(span[1], span[0] + 1)
    trailing += self._linenumdir(nextline, fname)

# After
nextline = max(span[1], span[0] + 1)
trailing += self._linenumdir(nextline, fname)

Tests

  • Updated expected values for 12 existing LINENUM_TESTS / IncludeTest
    entries that were previously not expecting the now-always-present
    trailing resync.
  • Added new test eval_resync_after_preprocessor_block that directly
    exercises the macro-with-#ifdef/#endif body scenario.
  • All 438 tests pass (run from test/ as the runtests.sh script
    requires, so that include-path relative lookups resolve correctly).

Previously, _postprocess_eval_line only emitted a trailing resync
linemarker when the output was either multi-line (span[1] > span[0])
or the last folded continuation line was out-of-sync with the line
counter.  This left single-line $: / @: / inline #{...}# calls
without a resync marker.

The missing resync causes silent line-number corruption when the
expanded macro body contains preprocessor conditionals (#ifdef /
#endif).  Downstream Fortran preprocessors (e.g. gfortran -cpp) skip
the content of never-taken branches, but the physical source lines
inside those branches still advance the compiler's internal line
counter.  Without a resync marker immediately after the expansion,
every subsequent Fortran statement is attributed to the wrong source
line, producing misleading compile-time diagnostics.

Fix: remove the `if unsync or multiline:` guard entirely so that a
resync linemarker is always emitted after every eval directive when
line-number directives are enabled.  Update the test suite expected
values to match the new (correct) output, and add a dedicated test
case that exercises the preprocessor-conditional scenario.
@sbryngelson sbryngelson force-pushed the fix/eval-resync-linemarker branch from ebacec2 to 2cfa96f Compare May 13, 2026 15:24
@sbryngelson
Copy link
Copy Markdown
Author

For context on real-world impact: this bug affects any project that uses fypp with --line-numbering and expands macros containing #ifdef/#endif blocks. A concrete example is MFC, an exascale CFD solver that uses $:GPU_PARALLEL_LOOP(...) throughout its simulation kernel. That macro expands to an #if defined(MFC_OpenACC) ... #elif defined(MFC_OpenMP) ... #endif block. Without the resync marker, every compiler error or warning after one of those calls is attributed to the wrong .fpp source line, making diagnostics unreliable.

MFC has deployed a build-time workaround patch (applied to the installed fypp in its virtualenv) while waiting for an upstream fix: MFlowCode/MFC#1436. The workaround works but is obviously not the right long-term solution.

Happy to address any review feedback.

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

Successfully merging this pull request may close these issues.

1 participant