sema/10-types #15

Merged
crowmaki merged 6 commits from sema/10-types into main 2026-08-24 04:04:09 +00:00
Owner
No description provided.
`sema::types::Type` and its normalizing constructors, per
docs/semantics/10-types.md. The first thing in the compiler that can say
`int` and `s32` are the same type, and that `?int` and `int | null` are
the same type.

`Type` has no optional case, no alias case, and no error case. `?T` is
`union_of([T, Null])`; `int` is `Int { bits: 32, signed: true }`; a body
that fails to type is dropped from the HIR rather than poisoning every
rule with an arm for "we gave up".

`union_of` is the only way to build a union. It flattens, deduplicates,
and sorts by the derived `Ord`, so `(a|b)|c` and `a|(b|c)` are one value
and `==` is the whole story.

`ids.rs` carries `DefId` and `TypeVar`, which `types` and (at step 20)
`hir` both need. `TargetLayout`/`TARGET` is the interim answer to open
spec question 4 and the single call site for `size_t`'s width.

Six inline unit tests, no fixtures. Suite unchanged apart from those:
the one failure is still the `.known-red` fixture driver.
Question 4 — target model. A target is an LLVM target triple, and a
compiler invoked without one compiles for the host. `language-spec.md`
gains § Targets: the architecture fixes the pointer width, the data
model (ILP32 / LLP64 / LP64) fixes `c_long`, and every other `c_*` width
is target-independent except `c_longdouble`, which each platform ABI
fixes for itself. An unknown triple is rejected before compilation
rather than compiled against a guess. Step 10 is unblocked.

Question 6 — interface conformance. Illustrative shorthand. An `impl`
body must supply every method its interface requires; the spec's empty
`ICollection<T>` body is marked as elided rather than rewritten, so the
fixture lifted from it stays the spec's own code. Still out of scope for
this phase, now as a scheduling decision rather than an open rule.

Question 8 — string interpolation. Scheduled as step 95, a new
`95-interpolation.md`. The parser expands an interpolated literal into
its parts and re-enters `expression` for each hole; semantics types
them. `$` joins the escape table, and the restriction that a hole may
not contain a string literal is recorded rather than left to be
discovered. `string-interpolation.cat` moves from `lex/` to
`semantics/`, gains a `readLine` declaration and three lines covering an
expression hole, a hole-free literal, and `\$`.

The step also surfaced a second gap: `string` is a type no step
declares. Step 95 adds it to step 30's prelude table.

Question 5 — a bare type name in value position. Still open; it is a
language decision, not a gap in the write-up. Sharpened into the six
things that have to be decided, with a proposal.

Counts updated: 22 semantic fixtures, 52 parse-phase fixtures, seven
fixtures needing declarations, twelve steps, two parser defects.
Open spec question 4 resolved, so step 10's `TargetLayout { pointer_bits:
64 }` placeholder goes away. `sema/src/target.rs` resolves an LLVM target
triple into an `Arch`, a `DataModel`, and a `c_longdouble` width, and
refuses one it cannot answer for with a message naming what it could not
answer. `build.rs` writes the host triple into the binary, so the default
is the platform the compiler was actually built for and the suite checks
it rather than taking it on faith.

Every target-dependent width moves onto `TargetLayout`: `size_t`,
`ptrdiff_t`, and the eight integral `c_*` types. `IntType` keeps `s8 ..
u128`, which no target changes. That closes the `c_*` gap step 10 shipped
with — the twelve ABI types its Rules cite but its Functions list had no
home for.

`FloatWidth` grows `F80` and `F128` so `c_longdouble` has somewhere to
land. Neither is spellable: there is no `f80` keyword and no plan for one.

`TARGET` stays a global because nothing threads a target yet.
`TargetLayout` is an ordinary value and `from_triple` is public, so the
CLI's eventual `--target` passes one down and the static goes away.

Five new unit tests in `target.rs`. Suite otherwise unchanged; the one
failure is still the `.known-red` fixture driver.
`KeywordKind` carries c_short, c_ushort, c_int, c_uint, c_long,
c_ulong, c_longlong, c_ulonglong, c_longdouble, and c_void. Step 10's
Rules said twelve, which no table in language-spec.md supports.
A type expression in value position is a default-initialized instance,
on the stack without `new` and on the heap with it. Written up in
`language-spec.md` § Default Initialization, with the three decisions
that make it typeable.

One namespace. A binding may not take a name a type in scope already
has; the collision is an error naming both declarations, not a shadow.
Without it, `const v = Vector3;` means one thing or another depending on
a `let` elsewhere in the file. § Syntax ▸ Identifiers.

No default, no default-initialization. A type whose default value is
undefined cannot be default-initialized, and the attempt names the part
that has none. No zero-fill fallback. § Default Initialization carries
the table; the sharp case is the spec's own `Transform`, whose `owned
position: *Vector3` is a pointer and a pointer cannot be null.

`type T` is the type itself, so `const v1 = Vector3` and `const t1 =
type Vector3` stop competing for one syntax. Documented in § Type
References and deliberately NOT added to the grammar — a production no
phase honours is the defect step 00a and step 95 both exist to fix.

Step 40 is unblocked; its Rules now cite the two spec sections instead
of the open question. Step 20's `ExprKind::DefaultInit` and
`ExprKind::New` were already the right nodes.

Three fixtures. `struct-allocation.cat` promoted from `parse/` to
`semantics/`, gaining the `Vector3` declaration the compile phase needs
and the two array forms. Negatives `no-default-value.cat` and
`type-name-collision.cat` are new. Counts follow: 25 semantic fixtures,
54 parse-phase, eight needing declarations.

Recorded but not decided: `new` is the only construction form taking an
initializer, so a struct with no default cannot be stack-allocated at
all.
Three fixes, found while verifying the numbers against the tree.

The parse-phase fixture count read 51 before this phase opened, when the
true number was 56, and my last two commits incremented that wrong base
to 54 against a real 59. A count nothing checks goes stale, and this one
already had twice, so the sentence now says "every" and carries no
number. The 25/20/5 count above it stays, because that one is the
phase's progress bar and `.known-red` reads against it; it is correct.

`parser/src/ast.rs` has not existed since step 00 moved it into the
`ast` crate, but "What already exists" still pointed at it in the
present tense with a line count from before the move.

Question 1 closed with "this phase's surface is 17 fixtures rather than
16", a number two later resolutions had already overtaken. The sentence
only ever meant that a newly documented rule earns a fixture, so it now
says that and stops counting.
crowmaki deleted branch sema/10-types 2026-08-24 04:04:09 +00:00
crowmaki referenced this pull request from a commit 2026-08-24 04:04:09 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crowmaki/catlang!15
No description provided.