Repository navigation
Construct fixed size Vec with a list literal - #96
Merged
Merged
Conversation
Vec with a list literal
benjohnbarnes
force-pushed
the
fix/shape-indexed-literal
branch
2 times, most recently
from
October 9, 2026 11:53
754a9a3 to
ec765c5
Compare
A list literal in a Vec position takes its length from the literal's own element count, so takes3([1, 2, 3]) fills a Vec(Int, 3) and [] is the empty vector. A count that disagrees with a written dimension is refused naming both lengths, and a count meeting a variable dimension solves it, so one function serves every arity. Nothing new is spelled: the spelling is the ordinary list literal, and the expected type is what gives it its length, as it already does when a bare 1 adopts a Float context. The rule is Data.Vec.Vec and nothing else, and that is deliberate. A dimension is erased before Core, so no criterion can tell a Nat that measures a container from one that means something else, and a predicate keyed on a declaration's shape would offer the form to any type with a Nat in it, whatever its author meant by it. Vec is the one type where the offer is worth making, because MkVec is opaque and pinned hidden by stdlib_invariant_constructors_are_hidden: outside its module a literal is the only route to a value, which makes the count the only thing holding the length honest. A user's own type of the same shape keeps its constructor visible, so nothing is checked there either way and the form would only appear to promise something. What it does not do. Growth stays declined: a literal can write a longer vector than the present constructors can, but it cannot compute one. And a size written in the literal itself is still a syntax error, which is the alternative this does not take. Outstanding. docs/src/stdlib/data-vec.md is generated from Data/Vec.pr, whose prose this changes, and regenerating it needs the native toolchain. And tests/frontend.rs reaches prism::build, so the fifteen cases in tests/language/shape_indexed_literal.rs were run as a temporary standalone target rather than by the committed one; a checkout with LLVM runs them in place.
benjohnbarnes
force-pushed
the
fix/shape-indexed-literal
branch
from
October 9, 2026 11:55
ec765c5 to
67debf9
Compare
sdiehl
reviewed
Oct 9, 2026
sdiehl
reviewed
Oct 9, 2026
sdiehl
reviewed
Oct 9, 2026
| /// Those are the only declarations of their name, so a bare name is enough for | ||
| /// them; a user is free to declare a `Vec` of their own, and the list-literal | ||
| /// rule that reads this must not catch it. | ||
| pub(crate) const TY_VEC: &str = "Data.Vec.Vec"; |
Owner
There was a problem hiding this comment.
Let me have a think about this, not sure I'm in love with having qualified literals as wired in types. I think this probably belongs in the desugaring pass.
benjohnbarnes
added a commit
to benjohnbarnes/prism
that referenced
this pull request
Oct 9, 2026
Review on sdiehl#96 asked for less exposition in the language spec. The paragraph goes rather than shrinks: the section is about the kinds, and how a vector is written is described where the form is defined, in Data/Vec.pr, which is what the library docs are generated from. Nothing else in the spec changes, so the branch no longer touches spec.md at all. The fifteen cases spell their prism programs as multiline literals in place of escaped one-liners, which is how the two runtime cases were already written. Every embedded program is byte-for-byte what it was, so nothing about what is checked changes.
Owner
|
The lean fuzzer seems like some upstream network timeout unrelated to this PR. The scoreboard issue can be fixed with |
benjohnbarnes
force-pushed
the
fix/shape-indexed-literal
branch
3 times, most recently
from
October 11, 2026 13:03
36b995d to
ed9d11e
Compare
Stephen read the proposal and replied. Two of his points were decisions. The checker is the right home for the rule: he had suggested desugaring, thought about it, and withdrew that, because desugaring runs before types exist and cannot pin a length anywhere — there is no expression-level ascription to emit, and no ordinary function returns `Vec(a, 3)` for an arbitrary count without dimension arithmetic. And the name-matched version may land first, provided it says it is a stepping stone toward a conformance a type opts into, as Swift's `ExpressibleByArrayLiteral` is, with `Vec` as its first instance. Here it says so, in `wired.rs`. The rest were things to do. The extension of the language spec is gone rather than trimmed, since the section it sat in is about the kinds. The test programs are multiline literals in place of escaped one-liners. The rule is decided once, before the match, instead of tested by a guard and re-run by the body. A constructor with no registered tag is an internal invariant rather than a silent tag `0`. And the comments come down to what the code needs — the module prose with them: a list literal may be passed where a `Vec` is expected, and a list value cannot, since a list has no length in its type. The thing that is not apparent is the inference order, and it is pinned rather than noted. The rule needs the expected type at the literal's own site, so `takes3([1, 2, 3])` works while `let v = [1, 2, 3]` and then `takes3(v)` infers a `List` and is refused as a `List` against `Vec` mismatch. A test holds it, and `Data/Vec.pr` says it, which is what he asked for. `docs/src/stdlib/data-vec.md` is regenerated from that prose, since the page is generated: `prism docs --stdlib --out docs/src/stdlib` changed it and no other page of the 95, which is the one `--check` had named. `Check stdlib docs` is green here with LLVM 22, both halves of it.
benjohnbarnes
force-pushed
the
fix/shape-indexed-literal
branch
from
October 11, 2026 13:11
ed9d11e to
9a54e9d
Compare
sdiehl
marked this pull request as ready for review
October 11, 2026 22:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Constructing
Vecwith a list literalThis is just an initial proposal, not a finished patch. It is offered so the idea can be validated before anything is polished. It type-checks and has a test suite, but some points are Outstanding at the end.
The problem
Vec(a, n : Nat)exists and dimensions unify by equality, but nothing can build a vector longer thanone.
MkVecisopaqueand pinned hidden bystdlib_invariant_constructors_are_hidden;vemptyandvsinglepin lengths0and1;vmapandvzippreserve or demand equality; and a length-changingvconsneedsn + 1, which dimension arithmetic declines. The index is therefore habitable inprinciple and unreachable in practice.
The change
A list literal in a
Vecposition takes its length from the literal's own element count. Nothing new isspelled — these are ordinary list literals, and the position's type is what gives each one its length.
These compile.
These do not compile.
And these, because the rule is
Vec's alone. A user's own type of the same shape — and even one thatspells the same name — is not offered the form:
A size written in the literal still does not parse, which is the alternative this proposal does not
take:
Why
Vec, and what a generalisation could bePrimarily because it is the only type where the check is real.
MkVecisopaqueand pinned hidden, sooutside its module a literal is the only route to a value — which makes the count the only thing holding
the length honest. The predicate therefore names
Data.Vec.Vec, matched on its qualified name inwired.rsbeside the compiler's other standard-library hooks.Potentially Prism could in future have a more general feature to allow other types to construct from a list (and refuse this at compile time if the list is not a valid length). Swift has
ExpressibleByArrayLiteralfor this sort of thing. A generalisation like this would hopefully then be additive.What it touches
One arm in
check; one predicate, an identity test onVec; one per-node fact for elaboration(
NodeRes::ShapeIndexed, following theunboxed_fieldprecedent); and one wrapper in the existingExpr::Listelaboration arm, since the representation is a singleList(a)field. Nothing reachesCore — dimensions are erased before it, so there is no representation or codegen change.
Tests
tests/language/shape_indexed_literal.rs, wired intotests/frontend.rs: sixteen cases — lengths0,1,2,3and12; a length clash asserted on its wording; a variable length solved from the count;a rigid length refused; the element lane adopted; both directions of the list/vector boundary; a
user-declared type of the same shape being refused rather than offered the form; and a literal bound
before its position being inferred as a
Listrather than a vector.Two of them are runtime cases, and they exist because
vto_listdestructures the vector's ownconstructor: a bare list would fail to match rather than quietly resemble one. That is the property a
checking-only change would get wrong, so it is a committed test rather than a probe.
Outstanding
Nothing below is outstanding any more. Each is kept as the record of what it was, and the branch
carries the fix.
docs/src/stdlib/data-vec.mdis regenerated rather than hand-edited:prism docs --stdlib --check --out docs/src/stdlibis green on this branch with LLVM 22, and it reporteddata-vec.mdas the only page that had gone stale.
prism docs --stdlib --testis green with it — 566 doctestsand 538 expectations, none failed.
cargo test --test frontend shape_indexedis green onthis branch — sixteen cases, the two that reach the vector's own constructor included.
shape_indexed_ctorcall is gone: the rule is decided before the match, so thepredicate is computed once rather than tested by a guard and re-run by the body.
Not part of this
BytesandTensoralready have list constructors; making a literal buildthose is a class plus instances plus elaboration, is not about the size at all, and would be
unchecked by nature.
vconsand append stay declined. A literal can write a longer vectorthan the present constructors can; it cannot compute one.