-1

Simple question: shall a #pragma leading to nonstandard behavior cause __STDC__ macro not to be defined to 1? (Does the C standard explicitly prescribes that? If yes, then in which section? If no, then why?) Reason of the question: see below.

Sample code (t28.c):

#pragma warning( disable : 34 )
typedef int T[];

int main()
{
    int rc = sizeof(T);
#if __STDC__ == 1
    rc = 0;
#else
    rc = 1;
#endif
    return rc;
}

Invocation: cl t28.c /std:c11 /Za && t28 ; echo $?

Expected result: 1

Actual result: 0

Compiler version:

cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29913 for x64

Note: C11 (6.5.3.4 The sizeof and _Alignof operators) (emphasis added):

The sizeof operator shall not be applied to an expression that has function type or an incomplete type, ...

Here we see that #pragma leads to nonstandard behavior: "shall requirement" is violated, diagnostic message is not generated, compiler's backend is called, .exe is produced and successfully executed. However, this nonstandard behavior does not cause __STDC__ macro not to be defined to 1.

Reason of the question: tests. One test, similar to t28.c is failing because it expects return code 1 (__STDC__ is not defined to 1). Which part of the system contains the bug: test or compiler (or both)?

pmor
  • 5,392
  • 4
  • 17
  • 36
  • (a) `#pragma` not followed by `STDC` causes the implementation to behave in an implementation-defined manner. That could include changing the behavior of `__STDC__` but likely does not in this case. But answering further requires statement of the implementation documentation for this pragma. (b) What is `__STDC__` replace with normally (that is, when there is no `#pragma` and are no code causing compilation warnings or errors) in this C implementation? – Eric Postpischil Apr 17 '21 at 12:13
  • @thebusybee: How is that relevant to the question about `__STDC__`? – Eric Postpischil Apr 17 '21 at 12:20
  • Why does your question ask about `__STDC__` not being defined to 1 when the results indicate it is 1? Your post says the actual result of the program is 0, which means the “then” portion of the `#if` was used, which means `__STDC__ == 1` was true. – Eric Postpischil Apr 17 '21 at 12:38
  • @EricPostpischil (a) _likely does not in this case_: in which cases `#pragma` can cause `__STDC__` to be defined to `0`? (b) In this C implementation normally `__STDC__` is replaced with `1`. – pmor May 24 '21 at 17:21
  • @EricPostpischil _which means \_\_STDC\_\_ == 1 was true_: in this example I expect `__STDC__` to be `0`, because I expect that `#pragma` leading to non-standard behavior cause implementation to be non-conforming. In `t28.c` conforming implementation requires to produce a diagnostic message, however, due to `#pragma` such diagnostic message is not produced, however, `__STDC__` is still `1`. – pmor May 24 '21 at 17:24
  • 1
    "conforming implementation" is a property of the implementation, not your code. The presence of pragmas or whatever does not change the conformance of the implementation. – M.M May 25 '21 at 00:30
  • @M.M Thanks, makes sense. As I understood: in _cause the translator or the resulting program to behave in a non-conforming manner_ (6.10.6.1) the _translator_ can be _conforming implementation_. Hence, it can be rephrased as: _cause the conforming implementation to behave in a non-conforming manner_. – pmor May 25 '21 at 12:57
  • @M.M However, for such scenario how can we distinguish (w/o knowing that the `#pragma` was used, for example, somewhere deep in included headers) between "conforming implementation behaving in a non-conforming manner" (`__STDC__` is `1`) and "conforming implementation behaving in a conforming manner" (`__STDC__` is `1`)? – pmor May 25 '21 at 13:10
  • @M.M _The presence of pragmas or whatever does not change the conformance of the implementation_: FYI: w.r.t. `__STDC_IEC_559__`: quick test [shows](https://stackoverflow.com/q/69525665/1778275) that `__STDC_IEC_559__` is conditionally defined depending on option values (at least). Meaning that option values do change the "conformance to the specifications in annex F". – pmor Oct 11 '21 at 17:19
  • 1
    @pmor: Pragmas and command-line switches are different things. Each individual combination of switches with a compiler is considered to be a different C implementation, for purposes of evaluating conformance. The presence of a pragma inside source being compiled does not make the compiler a different C implementation. – Eric Postpischil Oct 11 '21 at 17:32
  • @EricPostpischil About pragmas. If a pragma followed by non-`STDC` may "cause the translator or the resulting program to behave in a non-conforming manner" (C11), then I draw the conclusion that such a program may have been generated by a non-conforming implementation (translator), which needs to indicate that via "`__STDC__ != 1` evaluates to true". How can you comment on that? – pmor Oct 11 '21 at 20:32
  • @pmor: Concluding that because a program contains `#` `pragma` not followed by `STDC` it was “generated” by a non-conforming implementation is not a valid inference. What do you mean by “generated”? Do you mean “translated”? If a program containing `#` `#pragma` not followed by `STDC` is translated by a conforming implementation, then it is translated by a conforming implementation. As we have discussed before, the fact that the implementation behaves in an implementation-defined manner that may include **otherwise** non-conforming behavior does not mean the implementation is not conforming. – Eric Postpischil Oct 11 '21 at 20:42
  • @EricPostpischil Re: What do you mean by “generated”. 1) "(resulting) program" == "executable" (binary code), 2) "generated" == "produced by compiler (including assembler and linker)". Re: does not mean the implementation is not conforming. Yes, indeed, a non-`STDC` pragma, which causes a conforming implementation to behave in a non-conforming manner does not make such implementation non-conforming. This may look non-trivial at the first glance. – pmor Oct 18 '21 at 21:03
  • @EricPostpischil Does it mean that currently the C standard provides no way to determine "whether a conforming implementation behaves is a (non-)conforming manner" via, for example, `__STDC__CONFORM__` (which is dynamic, i.e. its value is changed depending on pragmas)? – pmor Oct 18 '21 at 21:13

2 Answers2

2

Here we see that #pragma leads to nonstandard behavior: "shall requirement" is violated, diagnostic message is not generated, compiler's backend is called, .exe is produced and successfully executed. However, this nonstandard behavior does not cause __STDC__ macro not to be defined to 1.

First, the __STDC__ macro is not intended to be a mechanism for reporting “something happened during this compilation that resulted in nonstandard behavior.” It is not dynamic (changing) in that way. The desire for __STDC__ is simply for it to report that the C implementation is conforming. (It is only a desire because the C standard can require that conforming implementations define __STDC__ to be 1 but has no control over what nonconforming implementations define it to be.) If an implementation is conforming, __STDC__ will always be 1 regardless of what happens in any particular compilation.

(Note that one compiler may include multiple C implementations selected by various command-line switches, such as requesting different sizes for various types, enabling or disabling conforming extensions, enabling non-conforming variations from the C standard, and so on. Some combinations of switches may result in __STDC__ being defined to be 1 while others do not.)

Second, the #pragma warning( disable : 34 ) does not leading to nonstandard behavior. Per C 2018 6.10.6 1, this directive “causes the implementation to behave in an implementation-defined manner. The behavior might cause translation to fail or cause the translator or the resulting program to behave in [an otherwise] non-conforming manner.” So, supposing the pragma is documented by the implementation to suppress the warning about violation of the constraint in 6.5.3.4 1 about the sizeof operator, then this is permitted by the C standard. This rule in 6.10.6 1 overrides the constraint in 6.5.3.4 1. The behavior is permitted by the C standard, so it is conforming.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-1

The standard reserves #pragma STDC ... for future language extensions - all other pragmas are implementation-defined (C17 6.10.6).

Not to be confused which __STDC__, which is set to 1 to mark the compiler as a conforming implementation (C17 6.10.8.1).

This term is in turn defined in C17 4/5 and 4/6:

A strictly conforming program shall use only those features of the language and library specified in this International Standard

The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program. /--/ A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.

This doesn't mean that __STDC__ is set whenever the application is a strictly conforming program, but rather that it is set fixed to 1 or 0 if the compiler, with its current options, is a conforming implementation.


For example, a compiler might include POSIX libraries that dump non-standard identifiers in standard headers. In case those identifiers affect the behavior of a strictly conforming program through namespace collisions, it shall not set __STDC__ to 1. A non-conforming example is when I compile this with gcc -std=gnu17:

#include <string.h>
#if __STDC__== 1
int strdup; // file scope declaration
#endif

I get "error: 'strdup' redeclared as different kind of symbol". This is non-compliant behavior. Arguably, it is a bug that __STDC__ is set to 1 when running under -std=gnu17 since this is not a conforming implementation.

If I shift to -std=c17, it compiles cleanly as it should - and then gcc is a conforming implementation.


Otherwise, as we can tell from the quoted part above, a conforming implementation can still have extensions.

In your specific case, you chose to disable diagnostics as a non-standard extension. That's a call made by the programmer and not something that makes the compiler non-compliant. It simply makes the application non-conforming, but the compiler may still be able to compile a strictly conforming program even though you aren't feeding it one.

If you wish to demonstrate just how non-conforming VS is, then this is a better program:

#include <stdio.h>

int main()
{
  int x = __STDC__;
  #if defined(__STDC_NO_VLA__) && __STDC_NO_VLA__==1
    int arr [2];
  #else
    int arr [x];
  #endif

  printf("%zu", sizeof arr / sizeof *arr);
}

A conforming implementation should print 1 in case VLA is supported, otherwise 2. Broken implementations will give a tonne of strange diagnostics.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for the detailed answer. FYI: `cl t318.c /std:c11 /Za && t318.exe` produces no errors, no warnings and prints `2`. Version: 19.28.29913. – pmor May 24 '21 at 17:12
  • Still don't get: why `#pragma` leading to non-standard behavior does not cause implementation to be non-conforming? In the example `t28.c` conforming implementation requires to produce a diagnostic message, however, due to `#pragma` such diagnostic message is not produced, however, `__STDC__` is still 1. – pmor May 24 '21 at 17:17