julia-script / julia-script/silk

type-system: Add a nominal C-compatible union

Open
#16 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

backend diagnostics enhancement memory new feature P3 parser spec-change syntax type-system
Dominant language
TypeScript
Stars
48
Forks
0
Avg merge
4h 49m
Merged PRs (30d)
213

Description

Context

Silk calls C code and reads C data structures. Many C headers declare a union, in which
every member starts at offset 0 and the structure carries no tag. Silk has no type with
that layout. A user who reads such a structure today must copy bytes by hand. This ticket
adds a nominal union type for interoperation with C.

Current behavior

Silk unions are structural and the compiler owns their layout. unionEntry in
packages/compiler/src/Layout.ts:497-530 gives every union a 32-bit tag at offset 0,
then aligns the payload after it. The size is the largest member plus the tag and the
padding.

The two layouts differ:

  • A Silk structural union has a 32-bit tag at offset 0. The payload starts after the tag,
    at an aligned offset. The size includes the tag.
  • A C union has no tag. Every member starts at offset 0. The size is the size of the
    largest member, rounded up to the largest member alignment.

A user therefore cannot map a C union onto a Silk union. The offsets do not agree and the
sizes do not agree.

Requirements

  1. The parser must accept a nominal union declaration with a name and a list of members.
  2. Each member must have a name and a type, as a struct field does.
  3. The layout must place every member at offset 0.
  4. The layout must not add any tag.
  5. The size must equal the size of the largest member, rounded up to the alignment.
  6. The alignment must equal the largest member alignment.
  7. A read of a member must be an unsafe operation, because the compiler cannot know
    which member holds the current value.
  8. A write of a member must be an ordinary safe operation.
  9. The compiler must reject a member type that needs a drop operation.
  10. The compiler must reject a member type that carries a reference.
  11. The type must be distinct from a structural union and must not convert to one.
  12. The LLVM backend and the Wasm backend must agree on the layout.

Example

pub union Word {
  bytes: [u8; 4]
  value: u32
}

pub fn lowByte(word: &Word) -> u8 {
  unsafe {
    return word.bytes[counted(0)]
  }
}

Out of scope

  • A tagged nominal enum. #15 covers it.
  • A change to the existing structural union layout.
  • A bit-field member.
  • An automatic import of a C header.
  • A check that a read selects the member that the last write set.

Implementation note

The safety rule is the reason this type needs its own ticket. A structural union is safe
to read, because the tag states which member holds the value. A nominal union has no tag,
so a read can observe bytes that a different member wrote. The read is therefore an
unsafe operation and the user must state the invariant.

Two member restrictions follow from the same fact. A member that needs a drop operation
cannot be dropped correctly, because the compiler cannot know whether that member holds
the current value. A member that carries a reference can produce a reference from bytes
that a numeric write set, which breaks the borrow rules. Both must be rejected at the
declaration, not at the use.

The layout code must not reuse unionEntry. That function always adds the tag
(packages/compiler/src/Layout.ts:512-514). A nominal union needs a separate layout
entry with its own representation tag, so that the backends can tell the two apart.

Dependencies

None.

Acceptance criteria

  • The parser accepts a union declaration and keeps the syntax tree lossless.
  • A layout test shows that every member has offset 0.
  • A layout test shows that the size has no tag and no tag padding.
  • A layout test compares one Silk union against the C layout of the same members.
  • A semantic test shows that a read outside an unsafe block gets a diagnostic.
  • A semantic test shows that a member needing a drop gets a diagnostic.
  • A semantic test shows that a member carrying a reference gets a diagnostic.
  • An LLVM test and a Wasm test show the same size and alignment.
  • openspec/specs/bootstrap-target-layout/spec.md gets a new layout requirement.
  • openspec/specs/bootstrap-structural-unions/spec.md gets an amendment that states
    the nominal union is a separate type with no tag.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/compiler/src/Layout.ts, especially unionEntry, then trace the parser, semantic checks, LLVM backend, and Wasm backend involved in union declarations. Use the requested layout and semantic tests to verify zero offsets, untagged size and alignment, unsafe reads, and rejected members. Update openspec/specs/bootstrap-target-layout/spec.md and openspec/specs/bootstrap-structural-unions/spec.md as specified.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, wasm
Domain
backend, compilers, documentation, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.