Files
sx/examples/0641-comptime-empty-types-valid.sx
agra 538349611e comptime: empty-member types are valid for all kinds; keep never-defined declare rejected
A comptime-constructed type with NO members is now VALID for every kind
(empty struct, empty tuple, empty enum, empty tagged_union) — only a bare
`declare("X")` placeholder that is never completed by a matching `define`
stays rejected (it would panic codegen).

- comptime_vm.zig registerTypeVm: drop the blanket "a type with no members
  is never valid" rejection. The per-kind loops are vacuous for an empty
  member list and the dup-name checks stay correct.
- types.zig TaggedUnionInfo: add `defined: bool = true`. Every real
  construction (normal unions, error sets, register_type completion) is
  "defined" by default; only the two declare-PLACEHOLDER sites set it false:
  comptime_vm.declareNominal and lower/comptime.preregisterForwardTypes.
- lower/comptime.checkComptimeTypeResult: reject on `!defined` (never-defined
  placeholder) instead of `fields.len == 0`, so an explicitly-defined empty
  union passes through while a never-completed declare is still gated.
- types.zig typeSizeBytes(tagged_union): floor the payload area at 8 bytes
  when no field carries a payload, mirroring the LLVM lowering — fixes a
  verifySizes panic on an empty/all-void tagged_union (IR sized to tag-only,
  LLVM laid out tag + [8 x i8]).

Tests:
- examples/1179: repurposed from "empty enum rejected" (now valid) to the
  never-defined `declare` case (the remaining rejection); preserves its
  issue-0140 regression role.
- examples/1180 (duplicate variant): still rejected, unchanged output.
- examples/0641 (new): construct empty struct/tuple/enum/tagged_union via
  define/declare; instantiate the constructible ones; exit 0.
2026-06-19 21:41:07 +03:00

37 lines
1.7 KiB
Plaintext

// A comptime-constructed type with NO members is VALID for every kind:
// - an empty struct `struct {}` and empty tuple `()` are zero-size aggregates
// you can instantiate (`.{}`),
// - an empty enum and an empty tagged_union are valid uninhabited / zero-member
// types — legitimate to NAME and reference even though they have no
// constructible value (no variant to construct).
//
// This mirrors normal sx, where `struct {}` and `enum {}` already codegen fine;
// the metatype `define`/`declare` path now agrees (the old blanket
// "a type with no members is never valid" rejection is gone).
//
// The ONLY thing still rejected on this path is a bare `declare("X")` that is
// never completed by a matching `define` — an INCOMPLETE forward slot that would
// panic codegen. That case is exercised by examples/1179.
#import "modules/std.sx";
#import "modules/std/meta.sx";
// Explicitly-defined empty types of every kind.
EmptyStruct :: define(declare("EmptyStruct"), .struct(.{ fields = .[] }));
EmptyTuple :: define(declare("EmptyTuple"), .tuple(.{ elements = .[] }));
EmptyEnum :: define(declare("EmptyEnum"), .enum(.{ variants = .[] }));
// An empty tagged_union (kind 3): no variants, but a valid named type. (The
// `define` DSL maps an all-void variant set to a payloadless enum, so reach for
// the register_type primitive directly to mint a 0-variant tagged_union.)
EmptyUnion :: register_type(declare("EmptyUnion"), 3, .[]);
main :: () -> i32 {
// Instantiate the constructible ones.
s : EmptyStruct = .{};
t : EmptyTuple = .{};
_ = s;
_ = t;
// EmptyEnum / EmptyUnion are uninhabited — valid as types, no value to make.
print("empty struct/tuple/enum/tagged_union are all valid\n");
return 0;
}