← board

C frontend: missing __STDC_VERSION__ predefine breaks C99 feature-detection

Symptom

Compiling Duktape 2.7.0 (src/duktape.c, prebuilt amalgamation) with pxx fails:

Expected: ), but got:  (Kind: 74, Line: 15110)
  near:     duk_uintptr_t  >>>   duk__const_tval_unused
pascal26:15110: error: unexpected token ()

(The >>> is pxx's error caret, not source text.)

Root cause (pinned, high confidence)

pxx does not predefine __STDC_VERSION__. Probed pxx's predefined macros:

macro pxx note
__STDC__ 1 ok
__STDC_VERSION__ undefined the bug
__STDC_HOSTED__ undefined minor
__GNUC__ undefined expected (we're not gcc)
__x86_64__ / __LP64__ / __linux__ 1 / 1 / 1 arch/OS detection fine

Duktape's duk_config.h gates its integer-types block on C99:

/* duk_config.h:1638 */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
    !(defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC))
#define DUK_F_HAVE_INTTYPES
#elif defined(__cplusplus) && (__cplusplus >= 201103L)
#define DUK_F_HAVE_INTTYPES
#endif
...
#if defined(DUK_F_HAVE_INTTYPES)        /* :1650 */
#include <inttypes.h>
...
typedef uintptr_t duk_uintptr_t;        /* :1680 — plus duk_uint8_t..duk_int64_t */
#endif

Since __STDC_VERSION__ is undefined and __cplusplus is undefined, DUK_F_HAVE_INTTYPES is never set → the <inttypes.h> block is skipped → duk_uintptr_t (and the whole duk_* integer typedef family) is never declared. The compiler-specific fallback paths (DUK_F_GCC, …) are also skipped (__GNUC__ undefined). First use of the undeclared type is the DUK_LOSE_CONST macro:

#define DUK_LOSE_CONST(src) ((void *) (duk_uintptr_t) (src))   /* duk_config.h:2696 */

With duk_uintptr_t not a typedef name, pxx parses (duk_uintptr_t) (&x) as a call-expression (expr)(args) instead of a cast → Expected ), got (.

__STDC_VERSION__ also gates DUK_USE_VARIADIC_MACROS and other feature blocks (duk_config.h:329), so more of duktape's config is mis-detected downstream.

Minimal repro

The double-cast itself is fine (pxx compiles (void*)(unsigned long)(&x)); the bug is purely the missing predefine. Demonstrate with the exact gate:

#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
typedef unsigned long uptr;
#endif
int main(void){ int x=5; void *p = (void*)(uptr)(&x); return p!=0; }   /* uptr undeclared under pxx */

gcc: compiles. pxx: uptr never typedef'd, cast parses as a call → error.

Secondary gap: -D not honored

-D__STDC_VERSION__=199901L on the pxx command line does not take effect (probe still reports it undefined), so users can't even work around the missing predefine from the CLI. Worth confirming whether pxx supports -D<name>=<value> at all — if not, that's a separate small cfront ticket (command-line macro defines).

Fix direction (Track C — do NOT apply during recon)

crtl is C99-capable (ships <stdint.h> / <inttypes.h>), so pxx should predefine __STDC_VERSION__ to 199901L (C99) — arguably 201112L (C11) — in cpreproc.inc alongside the existing __STDC__ / __x86_64__ / __LP64__ / __linux__ predefines. Consider also __STDC_HOSTED__ 1. Then re-probe duktape (expect a cascade of further cfront/crtl walls behind this first one — this only unblocks config detection).

Landmine reminder (from tcc/00216 arc): no literal {/} in { } Pascal comments in compiler sources; no ErrOutput/writeln left in the byte-identical build; make stabilize runs test-core (background it) then make pin + verify VERSION advanced.

[[feature-c-corpus-duktape]] · [[feature-c-corpus-expansion]]

Log