lib/ww/syntax: export the 16 public types consumed by the wcc backend (#72)
After the frontend consolidated into one syntax package, the wcc backend
imports syntax and calls its exported fns — whose signatures reference
types that were unexported. Producing syntax's .wwi interface re-triggered
check_exported_type ("exported declaration references unexported type"):
the residual of BUG-A at the one surviving syntax->wcc boundary. Export
the 16 types that appear in syntax's wcc-facing public surface (directly
in an exported signature, or via a recursively-referenced exported struct
field): nkind, node, lex, tok, tkind, parser, scope, sym, skind, tinfo,
tykind, tfield, tparam, ttupleelem, tctx, tinfocacheent. The set is
minimal (unexporting any one re-breaks the producer) and complete; pos
stays internal. Pure source change — exporting a type emits no code, so
the bootstrap binaries are byte-identical (verified against a clean base
build); only syntax's .wwi gains the type decls.
Post-frontend-reorg residual (#74). syntax now sep-produces clean both
stages. The separate concern of wcc's currently-unqualified refs to
syntax symbols (#75) is a distinct follow-up. Gate 989_syntaxexport_run.
This commit is contained in:
13
Makefile
13
Makefile
@@ -562,6 +562,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_sepbuild_run \
|
$(BIN)/test_sepbuild_run \
|
||||||
$(BIN)/test_sepdotpath_run \
|
$(BIN)/test_sepdotpath_run \
|
||||||
$(BIN)/test_seproot_export_run \
|
$(BIN)/test_seproot_export_run \
|
||||||
|
$(BIN)/test_syntaxexport_run \
|
||||||
$(BIN)/test_sepstructdef_run \
|
$(BIN)/test_sepstructdef_run \
|
||||||
$(BIN)/test_sepcycle_dup \
|
$(BIN)/test_sepcycle_dup \
|
||||||
$(BIN)/test_separchive_run \
|
$(BIN)/test_separchive_run \
|
||||||
@@ -3131,6 +3132,18 @@ $(BIN)/test_seproot_export_run: test/wcc/989_seproot_export_run.c $(BIN)/ww $(BI
|
|||||||
$(BIN)/w6l $(BIN)/w6l_ww $(LIB)/libwwrt.a | $(BIN)
|
$(BIN)/w6l $(BIN)/w6l_ww $(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
# 989_syntaxexport_run — BUG-A residual (#72) at the surviving syntax->wcc
|
||||||
|
# boundary after the #74 consolidation: syntax's exported fns name frontend
|
||||||
|
# types, so the producer's check_exported_type (recursing exported-struct
|
||||||
|
# FIELDS too) rejects until those 16 types carry `export`. Drives BOTH driver
|
||||||
|
# stages on the real lib/ww/syntax via a qualified-ref root + replays the
|
||||||
|
# producer on the produced unit (both compilers, export-strip = the bug), so
|
||||||
|
# it needs both driver + both compiler + both linker stages + libwwrt.
|
||||||
|
$(BIN)/test_syntaxexport_run: test/wcc/989_syntaxexport_run.c $(BIN)/ww $(BIN)/ww_ww \
|
||||||
|
$(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6a_ww \
|
||||||
|
$(BIN)/w6l $(BIN)/w6l_ww $(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
# 989_sepstructdef_run — sep-build of a dep exporting aggregate-initializer
|
# 989_sepstructdef_run — sep-build of a dep exporting aggregate-initializer
|
||||||
# defs (N_STRUCTLIT info / N_ARRLIT arr) (#70, BUG-2): the producer must emit a
|
# defs (N_STRUCTLIT info / N_ARRLIT arr) (#70, BUG-2): the producer must emit a
|
||||||
# value-LESS prototype `export def X: T;` for an aggregate-init def (DATA-global
|
# value-LESS prototype `export def X: T;` for an aggregate-init def (DATA-global
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import strconv;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs
|
// explicit and must stay in sync — the 990_selfhost test diffs
|
||||||
// astprint against the C side byte-for-byte. Tail-appended entries
|
// astprint against the C side byte-for-byte. Tail-appended entries
|
||||||
// (TYPETEST onward) preserve every prior N_* value.
|
// (TYPETEST onward) preserve every prior N_* value.
|
||||||
type nkind = enum i32 {
|
export type nkind = enum i32 {
|
||||||
N_NONE = 0,
|
N_NONE = 0,
|
||||||
|
|
||||||
N_INTLIT = 1,
|
N_INTLIT = 1,
|
||||||
@@ -115,7 +115,7 @@ type nkind = enum i32 {
|
|||||||
|
|
||||||
// ---- Node -------------------------------------------------------------
|
// ---- Node -------------------------------------------------------------
|
||||||
|
|
||||||
type node = struct {
|
export type node = struct {
|
||||||
kind: nkind,
|
kind: nkind,
|
||||||
file: str,
|
file: str,
|
||||||
line: i32,
|
line: i32,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn hexval(c: rune) (i32 | void) = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
type lex = struct {
|
export type lex = struct {
|
||||||
file: str,
|
file: str,
|
||||||
src: *u8, // raw bytes; not necessarily NUL-terminated
|
src: *u8, // raw bytes; not necessarily NUL-terminated
|
||||||
srclen: u64,
|
srclen: u64,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ package syntax;
|
|||||||
import os;
|
import os;
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
type parser = struct {
|
export type parser = struct {
|
||||||
l: *lex,
|
l: *lex,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
// nocast: while inside `[...]` we treat ':' as the slice
|
// nocast: while inside `[...]` we treat ':' as the slice
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
package syntax;
|
package syntax;
|
||||||
|
|
||||||
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
||||||
type skind = enum i32 {
|
export type skind = enum i32 {
|
||||||
SK_NONE = 0,
|
SK_NONE = 0,
|
||||||
SK_VAR = 1,
|
SK_VAR = 1,
|
||||||
SK_PARAM = 2,
|
SK_PARAM = 2,
|
||||||
@@ -18,7 +18,7 @@ type skind = enum i32 {
|
|||||||
SK_FIELD = 7,
|
SK_FIELD = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
type sym = struct {
|
export type sym = struct {
|
||||||
name: str,
|
name: str,
|
||||||
skind: skind,
|
skind: skind,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
@@ -43,7 +43,7 @@ type sym = struct {
|
|||||||
|
|
||||||
def NBUCKETS: i32 = 16;
|
def NBUCKETS: i32 = 16;
|
||||||
|
|
||||||
type scope = struct {
|
export type scope = struct {
|
||||||
parent: *scope,
|
parent: *scope,
|
||||||
first: *sym,
|
first: *sym,
|
||||||
last: *sym,
|
last: *sym,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import strings;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
||||||
// output against the C side, byte for byte.
|
// output against the C side, byte for byte.
|
||||||
|
|
||||||
type tkind = enum i32 {
|
export type tkind = enum i32 {
|
||||||
TK_NONE = 0,
|
TK_NONE = 0,
|
||||||
TK_EOF = 1,
|
TK_EOF = 1,
|
||||||
TK_ERR = 2,
|
TK_ERR = 2,
|
||||||
@@ -143,7 +143,7 @@ type pos = struct {
|
|||||||
col: i32,
|
col: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tok = struct {
|
export type tok = struct {
|
||||||
kind: tkind,
|
kind: tkind,
|
||||||
file: str, // path of the source the token came from
|
file: str, // path of the source the token came from
|
||||||
line: i32,
|
line: i32,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import os;
|
|||||||
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
||||||
// are explicit and must stay in sync — the selfhost selfcheck and
|
// are explicit and must stay in sync — the selfhost selfcheck and
|
||||||
// typed-AST printers depend on matching numeric layout.
|
// typed-AST printers depend on matching numeric layout.
|
||||||
type tykind = enum i32 {
|
export type tykind = enum i32 {
|
||||||
TY_NONE = 0,
|
TY_NONE = 0,
|
||||||
TY_VOID = 1,
|
TY_VOID = 1,
|
||||||
TY_BOOL = 2,
|
TY_BOOL = 2,
|
||||||
@@ -69,14 +69,14 @@ def SIZE_UNDEFINED: u64 = 18446744073709551615;
|
|||||||
|
|
||||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||||
|
|
||||||
type tfield = struct {
|
export type tfield = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *tfield,
|
tnext: *tfield,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tparam = struct {
|
export type tparam = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
||||||
@@ -98,13 +98,13 @@ type tparam = struct {
|
|||||||
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
||||||
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
||||||
// (variants can be named; positionals never can).
|
// (variants can be named; positionals never can).
|
||||||
type ttupleelem = struct {
|
export type ttupleelem = struct {
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *ttupleelem,
|
tnext: *ttupleelem,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tinfo = struct {
|
export type tinfo = struct {
|
||||||
kind: tykind,
|
kind: tykind,
|
||||||
size: u64,
|
size: u64,
|
||||||
align: u64,
|
align: u64,
|
||||||
@@ -154,7 +154,7 @@ type tinfo = struct {
|
|||||||
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
||||||
// only one bucket -> O(1) amortized. Identical lookup results (same
|
// only one bucket -> O(1) amortized. Identical lookup results (same
|
||||||
// *tinfo for the same node), so emitted asm is byte-identical.
|
// *tinfo for the same node), so emitted asm is byte-identical.
|
||||||
type tinfocacheent = struct {
|
export type tinfocacheent = struct {
|
||||||
key: *node,
|
key: *node,
|
||||||
val: *tinfo,
|
val: *tinfo,
|
||||||
cnext: *tinfocacheent,
|
cnext: *tinfocacheent,
|
||||||
@@ -168,7 +168,7 @@ def NBUCKETS_TINFO: u64 = 8192u64;
|
|||||||
|
|
||||||
// ---- tctx — the box of primitive types -------------------------------
|
// ---- tctx — the box of primitive types -------------------------------
|
||||||
|
|
||||||
type tctx = struct {
|
export type tctx = struct {
|
||||||
tyvoid: *tinfo,
|
tyvoid: *tinfo,
|
||||||
tybool: *tinfo,
|
tybool: *tinfo,
|
||||||
tyrune: *tinfo,
|
tyrune: *tinfo,
|
||||||
|
|||||||
@@ -6441,7 +6441,7 @@ import strconv;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs
|
// explicit and must stay in sync — the 990_selfhost test diffs
|
||||||
// astprint against the C side byte-for-byte. Tail-appended entries
|
// astprint against the C side byte-for-byte. Tail-appended entries
|
||||||
// (TYPETEST onward) preserve every prior N_* value.
|
// (TYPETEST onward) preserve every prior N_* value.
|
||||||
type nkind = enum i32 {
|
export type nkind = enum i32 {
|
||||||
N_NONE = 0,
|
N_NONE = 0,
|
||||||
|
|
||||||
N_INTLIT = 1,
|
N_INTLIT = 1,
|
||||||
@@ -6535,7 +6535,7 @@ type nkind = enum i32 {
|
|||||||
|
|
||||||
// ---- Node -------------------------------------------------------------
|
// ---- Node -------------------------------------------------------------
|
||||||
|
|
||||||
type node = struct {
|
export type node = struct {
|
||||||
kind: nkind,
|
kind: nkind,
|
||||||
file: str,
|
file: str,
|
||||||
line: i32,
|
line: i32,
|
||||||
@@ -7521,7 +7521,7 @@ fn hexval(c: rune) (i32 | void) = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
type lex = struct {
|
export type lex = struct {
|
||||||
file: str,
|
file: str,
|
||||||
src: *u8, // raw bytes; not necessarily NUL-terminated
|
src: *u8, // raw bytes; not necessarily NUL-terminated
|
||||||
srclen: u64,
|
srclen: u64,
|
||||||
@@ -8372,7 +8372,7 @@ package syntax;
|
|||||||
import os;
|
import os;
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
type parser = struct {
|
export type parser = struct {
|
||||||
l: *lex,
|
l: *lex,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
// nocast: while inside `[...]` we treat ':' as the slice
|
// nocast: while inside `[...]` we treat ':' as the slice
|
||||||
@@ -9323,7 +9323,7 @@ fn parsestmt(p: *parser) *node = {
|
|||||||
package syntax;
|
package syntax;
|
||||||
|
|
||||||
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
||||||
type skind = enum i32 {
|
export type skind = enum i32 {
|
||||||
SK_NONE = 0,
|
SK_NONE = 0,
|
||||||
SK_VAR = 1,
|
SK_VAR = 1,
|
||||||
SK_PARAM = 2,
|
SK_PARAM = 2,
|
||||||
@@ -9334,7 +9334,7 @@ type skind = enum i32 {
|
|||||||
SK_FIELD = 7,
|
SK_FIELD = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
type sym = struct {
|
export type sym = struct {
|
||||||
name: str,
|
name: str,
|
||||||
skind: skind,
|
skind: skind,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
@@ -9359,7 +9359,7 @@ type sym = struct {
|
|||||||
|
|
||||||
def NBUCKETS: i32 = 16;
|
def NBUCKETS: i32 = 16;
|
||||||
|
|
||||||
type scope = struct {
|
export type scope = struct {
|
||||||
parent: *scope,
|
parent: *scope,
|
||||||
first: *sym,
|
first: *sym,
|
||||||
last: *sym,
|
last: *sym,
|
||||||
@@ -9671,7 +9671,7 @@ import strings;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
||||||
// output against the C side, byte for byte.
|
// output against the C side, byte for byte.
|
||||||
|
|
||||||
type tkind = enum i32 {
|
export type tkind = enum i32 {
|
||||||
TK_NONE = 0,
|
TK_NONE = 0,
|
||||||
TK_EOF = 1,
|
TK_EOF = 1,
|
||||||
TK_ERR = 2,
|
TK_ERR = 2,
|
||||||
@@ -9794,7 +9794,7 @@ type pos = struct {
|
|||||||
col: i32,
|
col: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tok = struct {
|
export type tok = struct {
|
||||||
kind: tkind,
|
kind: tkind,
|
||||||
file: str, // path of the source the token came from
|
file: str, // path of the source the token came from
|
||||||
line: i32,
|
line: i32,
|
||||||
@@ -10074,7 +10074,7 @@ import os;
|
|||||||
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
||||||
// are explicit and must stay in sync — the selfhost selfcheck and
|
// are explicit and must stay in sync — the selfhost selfcheck and
|
||||||
// typed-AST printers depend on matching numeric layout.
|
// typed-AST printers depend on matching numeric layout.
|
||||||
type tykind = enum i32 {
|
export type tykind = enum i32 {
|
||||||
TY_NONE = 0,
|
TY_NONE = 0,
|
||||||
TY_VOID = 1,
|
TY_VOID = 1,
|
||||||
TY_BOOL = 2,
|
TY_BOOL = 2,
|
||||||
@@ -10125,14 +10125,14 @@ def SIZE_UNDEFINED: u64 = 18446744073709551615;
|
|||||||
|
|
||||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||||
|
|
||||||
type tfield = struct {
|
export type tfield = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *tfield,
|
tnext: *tfield,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tparam = struct {
|
export type tparam = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
||||||
@@ -10154,13 +10154,13 @@ type tparam = struct {
|
|||||||
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
||||||
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
||||||
// (variants can be named; positionals never can).
|
// (variants can be named; positionals never can).
|
||||||
type ttupleelem = struct {
|
export type ttupleelem = struct {
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *ttupleelem,
|
tnext: *ttupleelem,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tinfo = struct {
|
export type tinfo = struct {
|
||||||
kind: tykind,
|
kind: tykind,
|
||||||
size: u64,
|
size: u64,
|
||||||
align: u64,
|
align: u64,
|
||||||
@@ -10210,7 +10210,7 @@ type tinfo = struct {
|
|||||||
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
||||||
// only one bucket -> O(1) amortized. Identical lookup results (same
|
// only one bucket -> O(1) amortized. Identical lookup results (same
|
||||||
// *tinfo for the same node), so emitted asm is byte-identical.
|
// *tinfo for the same node), so emitted asm is byte-identical.
|
||||||
type tinfocacheent = struct {
|
export type tinfocacheent = struct {
|
||||||
key: *node,
|
key: *node,
|
||||||
val: *tinfo,
|
val: *tinfo,
|
||||||
cnext: *tinfocacheent,
|
cnext: *tinfocacheent,
|
||||||
@@ -10224,7 +10224,7 @@ def NBUCKETS_TINFO: u64 = 8192u64;
|
|||||||
|
|
||||||
// ---- tctx — the box of primitive types -------------------------------
|
// ---- tctx — the box of primitive types -------------------------------
|
||||||
|
|
||||||
type tctx = struct {
|
export type tctx = struct {
|
||||||
tyvoid: *tinfo,
|
tyvoid: *tinfo,
|
||||||
tybool: *tinfo,
|
tybool: *tinfo,
|
||||||
tyrune: *tinfo,
|
tyrune: *tinfo,
|
||||||
|
|||||||
@@ -6441,7 +6441,7 @@ import strconv;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs
|
// explicit and must stay in sync — the 990_selfhost test diffs
|
||||||
// astprint against the C side byte-for-byte. Tail-appended entries
|
// astprint against the C side byte-for-byte. Tail-appended entries
|
||||||
// (TYPETEST onward) preserve every prior N_* value.
|
// (TYPETEST onward) preserve every prior N_* value.
|
||||||
type nkind = enum i32 {
|
export type nkind = enum i32 {
|
||||||
N_NONE = 0,
|
N_NONE = 0,
|
||||||
|
|
||||||
N_INTLIT = 1,
|
N_INTLIT = 1,
|
||||||
@@ -6535,7 +6535,7 @@ type nkind = enum i32 {
|
|||||||
|
|
||||||
// ---- Node -------------------------------------------------------------
|
// ---- Node -------------------------------------------------------------
|
||||||
|
|
||||||
type node = struct {
|
export type node = struct {
|
||||||
kind: nkind,
|
kind: nkind,
|
||||||
file: str,
|
file: str,
|
||||||
line: i32,
|
line: i32,
|
||||||
@@ -7521,7 +7521,7 @@ fn hexval(c: rune) (i32 | void) = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
type lex = struct {
|
export type lex = struct {
|
||||||
file: str,
|
file: str,
|
||||||
src: *u8, // raw bytes; not necessarily NUL-terminated
|
src: *u8, // raw bytes; not necessarily NUL-terminated
|
||||||
srclen: u64,
|
srclen: u64,
|
||||||
@@ -8372,7 +8372,7 @@ package syntax;
|
|||||||
import os;
|
import os;
|
||||||
import strings;
|
import strings;
|
||||||
|
|
||||||
type parser = struct {
|
export type parser = struct {
|
||||||
l: *lex,
|
l: *lex,
|
||||||
errs: i32,
|
errs: i32,
|
||||||
// nocast: while inside `[...]` we treat ':' as the slice
|
// nocast: while inside `[...]` we treat ':' as the slice
|
||||||
@@ -9323,7 +9323,7 @@ fn parsestmt(p: *parser) *node = {
|
|||||||
package syntax;
|
package syntax;
|
||||||
|
|
||||||
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
|
||||||
type skind = enum i32 {
|
export type skind = enum i32 {
|
||||||
SK_NONE = 0,
|
SK_NONE = 0,
|
||||||
SK_VAR = 1,
|
SK_VAR = 1,
|
||||||
SK_PARAM = 2,
|
SK_PARAM = 2,
|
||||||
@@ -9334,7 +9334,7 @@ type skind = enum i32 {
|
|||||||
SK_FIELD = 7,
|
SK_FIELD = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
type sym = struct {
|
export type sym = struct {
|
||||||
name: str,
|
name: str,
|
||||||
skind: skind,
|
skind: skind,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
@@ -9359,7 +9359,7 @@ type sym = struct {
|
|||||||
|
|
||||||
def NBUCKETS: i32 = 16;
|
def NBUCKETS: i32 = 16;
|
||||||
|
|
||||||
type scope = struct {
|
export type scope = struct {
|
||||||
parent: *scope,
|
parent: *scope,
|
||||||
first: *sym,
|
first: *sym,
|
||||||
last: *sym,
|
last: *sym,
|
||||||
@@ -9671,7 +9671,7 @@ import strings;
|
|||||||
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
// explicit and must stay in sync — the 990_selfhost test diffs wwdump
|
||||||
// output against the C side, byte for byte.
|
// output against the C side, byte for byte.
|
||||||
|
|
||||||
type tkind = enum i32 {
|
export type tkind = enum i32 {
|
||||||
TK_NONE = 0,
|
TK_NONE = 0,
|
||||||
TK_EOF = 1,
|
TK_EOF = 1,
|
||||||
TK_ERR = 2,
|
TK_ERR = 2,
|
||||||
@@ -9794,7 +9794,7 @@ type pos = struct {
|
|||||||
col: i32,
|
col: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tok = struct {
|
export type tok = struct {
|
||||||
kind: tkind,
|
kind: tkind,
|
||||||
file: str, // path of the source the token came from
|
file: str, // path of the source the token came from
|
||||||
line: i32,
|
line: i32,
|
||||||
@@ -10074,7 +10074,7 @@ import os;
|
|||||||
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
// Mirror of the C `TypeKind` enum in cmd/wcc/ww.h. Numeric values
|
||||||
// are explicit and must stay in sync — the selfhost selfcheck and
|
// are explicit and must stay in sync — the selfhost selfcheck and
|
||||||
// typed-AST printers depend on matching numeric layout.
|
// typed-AST printers depend on matching numeric layout.
|
||||||
type tykind = enum i32 {
|
export type tykind = enum i32 {
|
||||||
TY_NONE = 0,
|
TY_NONE = 0,
|
||||||
TY_VOID = 1,
|
TY_VOID = 1,
|
||||||
TY_BOOL = 2,
|
TY_BOOL = 2,
|
||||||
@@ -10125,14 +10125,14 @@ def SIZE_UNDEFINED: u64 = 18446744073709551615;
|
|||||||
|
|
||||||
// ---- tinfo / tfield / tparam -----------------------------------------
|
// ---- tinfo / tfield / tparam -----------------------------------------
|
||||||
|
|
||||||
type tfield = struct {
|
export type tfield = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *tfield,
|
tnext: *tfield,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tparam = struct {
|
export type tparam = struct {
|
||||||
name: str,
|
name: str,
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
// #61a: per-variant `!T` error mark for TY_TAGGED variants.
|
||||||
@@ -10154,13 +10154,13 @@ type tparam = struct {
|
|||||||
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
|
||||||
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
// from #50's TTAGGED-on-tparam would conflate two semantic axes
|
||||||
// (variants can be named; positionals never can).
|
// (variants can be named; positionals never can).
|
||||||
type ttupleelem = struct {
|
export type ttupleelem = struct {
|
||||||
type_: *tinfo,
|
type_: *tinfo,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
tnext: *ttupleelem,
|
tnext: *ttupleelem,
|
||||||
};
|
};
|
||||||
|
|
||||||
type tinfo = struct {
|
export type tinfo = struct {
|
||||||
kind: tykind,
|
kind: tykind,
|
||||||
size: u64,
|
size: u64,
|
||||||
align: u64,
|
align: u64,
|
||||||
@@ -10210,7 +10210,7 @@ type tinfo = struct {
|
|||||||
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
// (rule-12): cnext chains WITHIN a bucket; lookup/bind hash then touch
|
||||||
// only one bucket -> O(1) amortized. Identical lookup results (same
|
// only one bucket -> O(1) amortized. Identical lookup results (same
|
||||||
// *tinfo for the same node), so emitted asm is byte-identical.
|
// *tinfo for the same node), so emitted asm is byte-identical.
|
||||||
type tinfocacheent = struct {
|
export type tinfocacheent = struct {
|
||||||
key: *node,
|
key: *node,
|
||||||
val: *tinfo,
|
val: *tinfo,
|
||||||
cnext: *tinfocacheent,
|
cnext: *tinfocacheent,
|
||||||
@@ -10224,7 +10224,7 @@ def NBUCKETS_TINFO: u64 = 8192u64;
|
|||||||
|
|
||||||
// ---- tctx — the box of primitive types -------------------------------
|
// ---- tctx — the box of primitive types -------------------------------
|
||||||
|
|
||||||
type tctx = struct {
|
export type tctx = struct {
|
||||||
tyvoid: *tinfo,
|
tyvoid: *tinfo,
|
||||||
tybool: *tinfo,
|
tybool: *tinfo,
|
||||||
tyrune: *tinfo,
|
tyrune: *tinfo,
|
||||||
|
|||||||
282
test/wcc/989_syntaxexport_run.c
Normal file
282
test/wcc/989_syntaxexport_run.c
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
* 989_syntaxexport_run — BUG-A residual at the surviving syntax->wcc
|
||||||
|
* package boundary (#72), post the #74 frontend consolidation.
|
||||||
|
*
|
||||||
|
* Root cause it guards: `lib/ww/syntax/` exports the fns wcc calls, and
|
||||||
|
* those fns' signatures name frontend types (tok, tinfo, tctx, ...). When
|
||||||
|
* the `--sep` producer emits syntax's `.wwi`, check_exported_type recurses
|
||||||
|
* every exported decl — and, for an exported struct, its FIELDS too (the
|
||||||
|
* harec STORAGE_STRUCT precedent, wwi.c wwi_check_type N_TSTRUCT). Any
|
||||||
|
* referenced type that is itself unexported aborts the producer with
|
||||||
|
* "exported declaration references unexported type". Pre-fix, syntax's
|
||||||
|
* surface left 16 such types unexported, so syntax's `.wwi` never produced
|
||||||
|
* -> every wcc sep-build was blocked. Fix: `export` those 16 type decls
|
||||||
|
* (10 fn-signature-direct + 6 surfaced by the struct-field recursion:
|
||||||
|
* tykind/tfield/tparam/ttupleelem via tinfo, tctx via typesinit, and
|
||||||
|
* tinfocacheent via tctx). Exporting a TYPE emits no code -> the bug is
|
||||||
|
* byte-id-neutral; this gate proves the PRODUCER residual is closed.
|
||||||
|
*
|
||||||
|
* The consumer here uses QUALIFIED refs (`syntax.tokname`, `syntax.tkind`)
|
||||||
|
* to isolate the producer fix from the separate unqualified-ref question
|
||||||
|
* (#75). It happens to link + run end-to-end, which also shows the
|
||||||
|
* qualified-ref path resolves across the sep boundary.
|
||||||
|
*
|
||||||
|
* Asserts (all COLD — per-stage WW_PKGCACHE wipes the package cache):
|
||||||
|
* 1. Build + run, BOTH stages -> exit EXPECT_EXIT (`syntax.tokname(
|
||||||
|
* TK_INT)` == "int", len 3). Pre-fix the syntax producer exited 1, so
|
||||||
|
* the build never produced a binary.
|
||||||
|
* 2. syntax's `.wwi` is produced and carries the 16 `export type` decls.
|
||||||
|
* 3. cs==ww (rule 10): syntax's `.wwi`/`.s`/`.unit.ww` AND the final
|
||||||
|
* binary are byte-identical between `ww --sep` and `ww_ww --sep`.
|
||||||
|
* 4. NON-VACUITY (the bug, isolated, BOTH compilers): replay the producer
|
||||||
|
* `w6c -c -I` on the produced `syntax.unit.ww` -> exits 0 (exports
|
||||||
|
* present). On a copy with `export ` stripped from the type decls ->
|
||||||
|
* MUST exit non-zero (the export-check fires). Proven for w6c + w6c_ww.
|
||||||
|
*
|
||||||
|
* Light wwstage-driver test (CLAUDE.md rule 14): all intermediates are
|
||||||
|
* `-o`-redirected to /tmp, so it is phase-1 parallel-safe; it only READS
|
||||||
|
* the in-tree `lib/ww/syntax`. Models 989_seproot_export_run.c.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define EXPECT_EXIT 3
|
||||||
|
|
||||||
|
static int
|
||||||
|
runwait(const char *cmd)
|
||||||
|
{
|
||||||
|
int rc = system(cmd);
|
||||||
|
if (rc == -1) return -1;
|
||||||
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
absbin(void)
|
||||||
|
{
|
||||||
|
const char *b = getenv("BIN");
|
||||||
|
if (!b) b = "out/bin";
|
||||||
|
if (b[0] == '/') return b;
|
||||||
|
static char buf[2048];
|
||||||
|
char cwd[1024];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return NULL;
|
||||||
|
snprintf(buf, sizeof buf, "%s/%s", cwd, b);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
slurp(const char *path, char **outbuf, size_t *outlen)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long n = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
if (n < 0) { fclose(f); return -1; }
|
||||||
|
char *b = malloc((size_t)n + 1);
|
||||||
|
if (!b) { fclose(f); return -1; }
|
||||||
|
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
||||||
|
b[n] = '\0';
|
||||||
|
fclose(f);
|
||||||
|
*outbuf = b;
|
||||||
|
*outlen = (size_t)n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
files_eq(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
char *ba = NULL, *bb = NULL;
|
||||||
|
size_t na = 0, nb = 0;
|
||||||
|
if (slurp(a, &ba, &na) < 0 || slurp(b, &bb, &nb) < 0) {
|
||||||
|
free(ba); free(bb);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int eq = (na == nb && memcmp(ba, bb, na) == 0);
|
||||||
|
free(ba); free(bb);
|
||||||
|
return eq ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 0 if `needle` occurs in the file at `path`, 1 if absent, -1 on read err. */
|
||||||
|
static int
|
||||||
|
file_contains(const char *path, const char *needle)
|
||||||
|
{
|
||||||
|
char *b = NULL;
|
||||||
|
size_t n = 0;
|
||||||
|
if (slurp(path, &b, &n) < 0) return -1;
|
||||||
|
int found = (strstr(b, needle) != NULL);
|
||||||
|
free(b);
|
||||||
|
return found ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
write_file(const char *path, const char *body)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(body, f);
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = absbin();
|
||||||
|
if (!bin) return 1;
|
||||||
|
char root[1024];
|
||||||
|
if (getcwd(root, sizeof root) == NULL) return 1;
|
||||||
|
|
||||||
|
char td[64], cmd[8192];
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
snprintf(td, sizeof td, "/tmp/wwsynexp_%d", getpid());
|
||||||
|
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||||
|
runwait(cmd);
|
||||||
|
mkdir(td, 0755);
|
||||||
|
|
||||||
|
/* Root: QUALIFIED refs into the real syntax package — `syntax.tokname`
|
||||||
|
* over `syntax.tkind`. The producer must serialize syntax's `.wwi`
|
||||||
|
* (exported fns over frontend types) WITHOUT the unexported-type abort. */
|
||||||
|
char rootww[1024];
|
||||||
|
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
|
||||||
|
if (write_file(rootww,
|
||||||
|
"package main;\n"
|
||||||
|
"import syntax;\n"
|
||||||
|
"fn main() i32 = {\n"
|
||||||
|
"\tlet s: str = syntax.tokname(syntax.tkind.TK_INT);\n"
|
||||||
|
"\treturn s.len: i32;\n"
|
||||||
|
"};\n"))
|
||||||
|
{ fail++; goto out; }
|
||||||
|
|
||||||
|
struct { const char *drv, *tag; char prog[1024]; }
|
||||||
|
stg[] = { { "ww", "cs", {0} }, { "ww_ww", "ww", {0} } };
|
||||||
|
|
||||||
|
for (int s = 0; s < 2; s++) {
|
||||||
|
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag);
|
||||||
|
/* COLD: per-stage WW_PKGCACHE -> syntax compiles fresh, so the
|
||||||
|
* `.wwi`/`.s`/`.unit.ww` this gate inspects are always produced. */
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"WW_PKGCACHE='%s/cache.%s' timeout 240 %s/%s build --sep "
|
||||||
|
"-I %s/lib/ww -o %s %s >%s/build.%s.log 2>&1",
|
||||||
|
td, stg[s].tag, bin, stg[s].drv, root, stg[s].prog, rootww,
|
||||||
|
td, stg[s].tag);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s build --sep (pre-fix: syntax "
|
||||||
|
"producer rejects export-fn-over-unexported-type)\n", stg[s].drv);
|
||||||
|
fail++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* the producer abort must not appear in the build log. */
|
||||||
|
char log[1024], wwi[1024];
|
||||||
|
snprintf(log, sizeof log, "%s/build.%s.log", td, stg[s].tag);
|
||||||
|
if (file_contains(log, "unexported type") == 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s build log shows "
|
||||||
|
"'unexported type' (producer residual not closed)\n", stg[s].drv);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
snprintf(wwi, sizeof wwi, "%s/prog.%s.sepwork/syntax.wwi", td, stg[s].tag);
|
||||||
|
if (access(wwi, 0) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s produced no syntax.wwi\n",
|
||||||
|
stg[s].drv);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
int rc = runwait(stg[s].prog);
|
||||||
|
if (rc != EXPECT_EXIT) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s prog exit=%d expected %d\n",
|
||||||
|
stg[s].drv, rc, EXPECT_EXIT);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* syntax's `.wwi` carries the now-exported frontend types — spot-check
|
||||||
|
* a representative set spanning each surfacing path (fn-direct + the
|
||||||
|
* struct-field and tctx-field cascades). */
|
||||||
|
{
|
||||||
|
char wwi[1024];
|
||||||
|
snprintf(wwi, sizeof wwi, "%s/prog.cs.sepwork/syntax.wwi", td);
|
||||||
|
const char *want[] = {
|
||||||
|
"export type tok ", "export type tkind ", "export type node ",
|
||||||
|
"export type nkind ", "export type tinfo ", "export type tfield ",
|
||||||
|
"export type tctx ", "export type tinfocacheent ",
|
||||||
|
};
|
||||||
|
for (int i = 0; i < (int)(sizeof want / sizeof want[0]); i++)
|
||||||
|
if (file_contains(wwi, want[i]) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: syntax.wwi lacks '%s'\n",
|
||||||
|
want[i]);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cs==ww (rule 10): syntax's per-package artefacts + the final binary. */
|
||||||
|
{
|
||||||
|
const char *suf[] = { ".wwi", ".s", ".unit.ww" };
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
char a[1024], b[1024];
|
||||||
|
snprintf(a, sizeof a, "%s/prog.cs.sepwork/syntax%s", td, suf[k]);
|
||||||
|
snprintf(b, sizeof b, "%s/prog.ww.sepwork/syntax%s", td, suf[k]);
|
||||||
|
if (files_eq(a, b) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: cs!=ww for syntax%s "
|
||||||
|
"(rule 10)\n", suf[k]);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files_eq(stg[0].prog, stg[1].prog) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: cs exe != ww exe (rule 10)\n");
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NON-VACUITY (the bug, isolated): replay the producer on the produced
|
||||||
|
* `syntax.unit.ww`. With the exports present `w6c -c -I` exits 0; with
|
||||||
|
* `export ` stripped from the type decls it MUST exit non-zero (the
|
||||||
|
* export-check fires). Proven for BOTH compilers. */
|
||||||
|
{
|
||||||
|
char unit[1024], ne[1024];
|
||||||
|
snprintf(unit, sizeof unit, "%s/prog.cs.sepwork/syntax.unit.ww", td);
|
||||||
|
snprintf(ne, sizeof ne, "%s/syntax.noexport.ww", td);
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"sed 's/export type /type /g' %s > %s", unit, ne);
|
||||||
|
if (runwait(cmd) != 0) { fail++; goto out; }
|
||||||
|
|
||||||
|
struct { const char *comp; } cc[] = { { "w6c" }, { "w6c_ww" } };
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
char wwi[1024], asmf[1024];
|
||||||
|
snprintf(wwi, sizeof wwi, "%s/nv.%s.wwi", td, cc[j].comp);
|
||||||
|
snprintf(asmf, sizeof asmf, "%s/nv.%s.s", td, cc[j].comp);
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/%s -c -I %s -o %s %s >/dev/null 2>&1",
|
||||||
|
bin, cc[j].comp, wwi, asmf, unit);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s -c -I rejected the "
|
||||||
|
"EXPORTED unit (post-fix producer must succeed)\n",
|
||||||
|
cc[j].comp);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd, "%s/%s -c -I %s -o %s %s >/dev/null 2>&1",
|
||||||
|
bin, cc[j].comp, wwi, asmf, ne);
|
||||||
|
if (runwait(cmd) == 0) {
|
||||||
|
fprintf(stderr, "syntaxexport FAIL: %s -c -I accepted the "
|
||||||
|
"UNEXPORTED-type unit (bug not reproduced -> vacuous "
|
||||||
|
"gate)\n", cc[j].comp);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
|
||||||
|
runwait(cmd);
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr, "syntaxexport: %d check(s) failed\n", fail);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("syntaxexport: syntax->wcc boundary sep-PRODUCES clean (16 export "
|
||||||
|
"type decls) + qualified-ref consumer build+run (exit %d) + cs==ww "
|
||||||
|
"syntax.wwi/.s/.unit.ww/binary + non-vacuity (export strip rejects, "
|
||||||
|
"both compilers)\n", EXPECT_EXIT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user