Triage pass: every issue file in `issues/` was re-verified against HEAD. Three (0041, 0042, 0043) reproduce no longer — they were silently fixed by adjacent work since the issue was filed. 0047 landed in the previous commit. All four header sections now lead with **FIXED** + a one-line locator so the next reader doesn't re-investigate. After this, `issues/` is the actual open-issue list: | Issue | Status | |---|---| | 0041 | FIXED (silently, by alias/parser work) | | 0042 | FIXED (silently, type_alias_map lookup landed) | | 0043 | FIXED (silently, lazy-lower foreign-class dispatch) | | 0044 | FIXED | | 0045 | FIXED | | 0046 | FIXED | | 0047 | FIXED (commit0119c9c) | | 0048 | FIXED (commit0ede097) | | 0049 | FIXED (commitb5301c4) | | 0050 | FIXED (commit5316bf7) | No open issues remain. The files stay in tree as a record; new issues take the next free number (0051).
5.8 KiB
issue-0041 — Pointer types don't parse as expressions / type-argument positions
FIXED. size_of(*u8), align_of(*u8), and the alias form
Ptr :: *u8; all parse and lower correctly today. The fix is
in tree as part of broader parser/lowering work — no specific
commit isolates it, but the original repro now prints 8 and
returns 0 exit.
Below preserved as a record of the original problem.
Symptom
A pointer type like *u8 or *void does not parse in positions
where a type expression is expected as a value, e.g.:
- As an argument to a
$T: Typebuiltin:size_of(*u8),align_of(*u8). - On the RHS of a type alias:
Ptr :: *u8;.
In each case the parser emits error: unexpected token in expression
at the column of the *.
Pointer types DO parse correctly in dedicated type-annotation
positions: function parameters ((p: *u8)), struct fields
(field: *u8;), variable annotations (p: *u8 = ...;). So the bug
is a parsing inconsistency between "type-annotation context" and
"expression context where a type is expected".
This is pre-existing — it affects size_of (already shipping) and
was just made more visible by adding align_of in Phase 0.6 of the
MEM plan. Not a regression introduced by 0.6, but a real limitation
worth pinning down because:
- Phase 1+ of the MEM plan will need
size_of(*T)/align_of(*T)in user-facing allocator helpers if we want to stay terse — e.g. serializing a pointer-typed field infield_value_intpatterns. - It's a discoverability cliff. New users WILL write
size_of(*u8), see "unexpected token", and have to learn the workaround.
Reproduction
#import "modules/std.sx";
main :: () -> s32 {
n := size_of(*u8); // error: unexpected token in expression
print("{}\n", n);
0;
}
Also fails on the alias form:
#import "modules/std.sx";
Ptr :: *u8; // error: unexpected token in expression
main :: () -> s32 { 0; }
Both sx run and sx build reject identically.
Confirmed working workarounds
A pointer type DOES resolve when bound through a *void-style
variable type and then cast, or routed via a helper:
// Workaround A: anonymous struct holding the pointer field, then
// pull alignment from the wrapping struct (clumsy).
Wrap :: struct { p: *u8; }
n := align_of(Wrap); // 8 — correct for pointer alignment.
// Workaround B: explicit *void
n := size_of(*void); // ALSO fails — same parse error.
Workaround B is NOT functional — it has the same parse error. Only the wrap-in-struct or type-alias-via-typedef trick is currently viable for code that needs pointer size/alignment.
There is no clean way today to write size_of(*u8). The whole
class of "ptr type as type-expression value" is unsupported.
Investigation prompt
Pointer types parse via a dedicated
parseTypeExpr(or similar) path that the parser invokes in type-annotation positions (param lists, field declarations, variable annotations). The expression grammar used in argument positions (e.g. insidesize_of(...)) dispatches throughparseExprinstead, which treats*as "either prefix unary deref or infix multiplication" — neither matches the desired "type literal" interpretation.The fix likely belongs in the call-argument parser path: when the callee is a builtin that takes
$T: Type, OR more broadly whenever the parser sees a*at the start of an expression followed by an identifier that resolves to a type, it should dispatch toparseTypeExprinstead ofparsePrefixUnary.Implementation sketch:
- Check
src/parser.zigfor the expression entry point that handles*prefix. Today it likely returns aunary_op { op = deref, operand = … }AST node.- Look at how lower.zig's
resolveTypeArgconsumes the AST node forsize_of(s32)— what AST shape does it expect for a type literal? Probably anidentifierwhose name resolves to a type.- The fix should extend
resolveTypeArgto also accept aunary_op { op = deref, ... }and treat it as "pointer to resolved type" — equivalent toPtr$Tin spec terms.- For the type-alias case (
Ptr :: *u8;), the RHS of a::const decl is parsed as an expression. The parser needs to recognize that the LHS-determined shape (type-level alias) should bias the RHS parser towardparseTypeExpr. Or: extend the constant-fold path to interpretunary_op { deref, T }as a type literal when used as a type.Verification:
- Add
examples/issue-0041.sxwith the repro above andtests/expected/issue-0041.txtcapturing the expected output (size_of(*u8) → 8).- Confirm
bash tests/run_examples.shstill passes everything else (151 tests currently).- Run
tools/verify-step.shto confirm chess on three platforms.- Also bake into
examples/50-smoke.sxnear the existingalign_oflines — addalign_of(*u8),size_of(*u8),align_of(*void)and regen.Hazard: any change to expression parsing affects a huge surface. Watch for these contexts to make sure they still work post-fix:
a * b(multiplication)*p(prefix deref read)*p = …(prefix deref write)func(a, *b)(deref as argument) A surgical "is the next token a built-in type identifier" lookahead at the*site is probably less invasive than a wholesale type-expression-in-expression-position rewrite.
Plan-level impact
None for Phase 0.6 — align_of shipped and works for every shape
that size_of works for (primitives, structs, type aliases through
non-pointer types). The 50-smoke test addition uses only
non-pointer types, so it's stable.
Phase 1+ should bake an align_of(*u8) test once the parser fix
lands, since the allocator API will want to round-trip pointer
alignments at some call sites.