'#line directive in #ifdef block

How is the C processor/compiler supposed to handle code like this:

/*1:*/
#line 3 "line.w"

#include <stdio.h> 
#include <stdlib.h> 

int main(void)
{
printf("Before #ifdef: line %d of %s\n",__LINE__,__FILE__);
#ifdef STAT
printf("Inside #ifdef before section: line %d of %s\n",__LINE__,__FILE__);
/*2:*/
#line 19 "line.w"

printf("Inside section: line %d of %s\n",__LINE__,__FILE__);

/*:2*/
#line 12 "line.w"

printf("Inside #ifdef after section: line %d of %s\n",__LINE__,__FILE__);
#endif
printf("After #ifdef: line %d of %s\n",__LINE__,__FILE__);
return EXIT_SUCCESS;
}

/*:1*/

C file line.c was created from CWEB file line.w by ctangle. Note the two #line directives inside the #ifdef ... #endif block.

When I run gcc -E line.c -o line-noSTAT.i and gcc -E -DSTAT line.c -o line-STAT.i, the two expanded *.i files differ in their output in the final printf after the #endif:

4d3
< # 31 "<command-line>"
6c5
< # 32 "<command-line>" 2
---
> # 1 "<command-line>" 2
1827,1828c1826,1835
< # 22 "line.w"
< printf("After #ifdef: line %d of %s\n",22,"line.w");
---
> 
> printf("Inside #ifdef before section: line %d of %s\n",11,"line.w");
> # 19 "line.w"
> 
> printf("Inside section: line %d of %s\n",20,"line.w");
> # 12 "line.w"
> 
> printf("Inside #ifdef after section: line %d of %s\n",13,"line.w");
> 
> printf("After #ifdef: line %d of %s\n",15,"line.w");
1830c1837
< # 23 "line.w" 3 4
---
> # 16 "line.w" 3 4
1832c1839
< # 23 "line.w"
---
> # 16 "line.w"

and the compiled program deviates, too:

$ gcc line.c
$ ./a.out 
Before #ifdef: line 9 of line.w
After #ifdef: line 22 of line.w
$ gcc -DSTAT line.c
$ ./a.out 
Before #ifdef: line 9 of line.w
Inside #ifdef before section: line 11 of line.w
Inside section: line 20 of line.w
Inside #ifdef after section: line 13 of line.w
After #ifdef: line 15 of line.w

Note the significant difference in the After #ifdef output.

AFAIKS, the incorrect After #ifdef: line 22 of line.w comes from the fact that the compiler/preprocessor picks the twenty-second line from line.c, not the fifteenth line from line.w.

This MWE was created with

$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

but I have been informed that the MS Visual Studio debugger also has problems with similar (production) code. Also Apple clang version 13.0.0 (clang-1300.0.29.30) produces these differing outcomes.

Shouldn't all code between #ifdef STAT and #endif be eliminated when STAT is not defined?



Solution 1:[1]

The C preprocessor – i.e., any variant, hence the consistent behaviour across systems – in fact eliminates the two #line statements in the #ifdef block, if STAT is undefined.

Starting from the first #line 3 "line.w" it continues through line.c, skips all of the #ifdef block, and catches the final printf in the twenty-second line of the C source file, which had been reset to "line.w" at the very beginning.

Duh, this appears to be a deficiency in CWEB's ctangle, which should add a correct #line statement after the #endif.


An intermediate manual way to keep the line numbers in sync is:

  1. Add #line __LINE__ after #endif in the CWEB code line.w
  2. Replace the concrete line number with perl -pi -e "s/#line __LINE__/#line $./" line.w
  3. Convert CWEB to C with ctangle line
  4. Compile C code with/out -DSTAT

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1