// `Vec(T)` from std.collections; `Option(T)` from std.option. const { assert } = import("test"); const { Vec } = import("std").collections; const { Option } = import("std").option; const VecI32 = Vec(i32); const VecU8 = Vec(u8); // Small helper: unwrap Option(i32) or fall back. Real code pattern- // matches directly; the tests use this so they can compare to ints. fn unwrapI32(o: Option(i32), fallback: i32) i32 { return match (o) { Option(i32).Some(x) { x } Option(i32).None { fallback } }; } fn unwrapU8(o: Option(u8), fallback: u8) u8 { return match (o) { Option(u8).Some(x) { x } Option(u8).None { fallback } }; } tfn pushAndGetI32() { var v: VecI32 = VecI32.empty(); v.push(42); v.push(43); v.push(44); assert(v.len() as i32, 3); assert(unwrapI32(v.get(0), 0), 42); assert(unwrapI32(v.get(1), 0), 43); assert(unwrapI32(v.get(2), 0), 44); } tfn pushBeyondInitialCapacity() { // Initial cap is 8; push 20 items to exercise the grow path // through at least two doublings (8 → 16 → 32). var v: VecI32 = VecI32.empty(); var i: i32 = 0; while (i < 20) { v.push(i); i = i + 1; } assert(v.len() as i32, 20); assert(unwrapI32(v.get(0), -1), 0); assert(unwrapI32(v.get(10), -1), 10); assert(unwrapI32(v.get(19), -1), 19); } tfn popReducesLength() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); assert(unwrapI32(v.pop(), 0), 3); assert(unwrapI32(v.pop(), 0), 2); assert(v.len() as i32, 1); assert(unwrapI32(v.get(0), 0), 1); } tfn popEmptyReturnsNone() { var v: VecI32 = VecI32.empty(); // Initial length is 0; pop returns None, not garbage. var sentinel: i32 = -42; assert(unwrapI32(v.pop(), sentinel), sentinel); } tfn getOutOfBoundsReturnsNone() { var v: VecI32 = VecI32.empty(); v.push(7); // len == 1; v.get(5) is out of bounds → None → sentinel. var sentinel: i32 = -42; assert(unwrapI32(v.get(5), sentinel), sentinel); } tfn vecOfU8() { // Different element type — verifies generic instantiation flows // through and @sizeOf(T) picks up the right size per // instantiation. var v: VecU8 = VecU8.empty(); v.push(10); v.push(20); v.push(30); assert(unwrapU8(v.get(0), 0) as i32, 10); assert(unwrapU8(v.get(1), 0) as i32, 20); assert(unwrapU8(v.get(2), 0) as i32, 30); } tfn initialCapacityForU8() { // sizeOf(u8) == 1 ⇒ initial cap 8 (Rust's heuristic). var v: VecU8 = VecU8.empty(); assert(v.capacity as i32, 8); } tfn initialCapacityForI32() { // sizeOf(i32) == 4, ≤ 1024 ⇒ initial cap 4. var v: VecI32 = VecI32.empty(); assert(v.capacity as i32, 4); } tfn withCapacityPreallocates() { var v: VecI32 = VecI32.withCapacity(100); // No pushes yet; length is zero but capacity reflects the // preallocation. push(...) up to 100 elements must not regrow. assert(v.len() as i32, 0); assert(v.capacity as i32, 100); var i: i32 = 0; while (i < 100) { v.push(i); i = i + 1; } // Capacity unchanged after 100 pushes (no regrow happened). assert(v.capacity as i32, 100); assert(v.len() as i32, 100); assert(unwrapI32(v.get(99), -1), 99); } tfn withCapacityZeroClampsToOne() { // Caller asked for 0 slots; we clamp to 1 to avoid the // implementation-defined malloc(0) result. var v: VecI32 = VecI32.withCapacity(0); assert(v.capacity as i32, 1); v.push(42); assert(unwrapI32(v.get(0), 0), 42); } tfn clearResetsLength() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); v.clear(); assert(v.len() as i32, 0); // Buffer is reusable: pushing again works without reallocating. v.push(99); assert(v.len() as i32, 1); assert(unwrapI32(v.get(0), 0), 99); } tfn clearKeepsCapacity() { var v: VecI32 = VecI32.withCapacity(50); v.push(7); v.clear(); // clear() must not free or shrink — capacity survives. assert(v.capacity as i32, 50); } tfn truncateShrinks() { var v: VecI32 = VecI32.empty(); v.push(10); v.push(20); v.push(30); v.push(40); v.truncate(2); assert(v.len() as i32, 2); assert(unwrapI32(v.get(0), -1), 10); assert(unwrapI32(v.get(1), -1), 20); // Index 2 is past the new length even though the byte is still // there in the buffer. var sentinel: i32 = -42; assert(unwrapI32(v.get(2), sentinel), sentinel); } tfn truncateNoOpWhenLarger() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.truncate(99); assert(v.len() as i32, 2); } tfn setLenSetsExactLength() { var v: VecI32 = VecI32.withCapacity(10); // Caller fills the buffer through `.ptr` then announces how // much they wrote. v.ptr[0] = 100; v.ptr[1] = 200; v.ptr[2] = 300; v.setLen(3); assert(v.len() as i32, 3); assert(unwrapI32(v.get(2), 0), 300); } tfn setLenClampsToCapacity() { var v: VecI32 = VecI32.withCapacity(5); // Overshooting must not silently corrupt — clamp to capacity. v.setLen(99); assert(v.len() as i32, 5); } tfn reserveGrowsCapacity() { // Empty VecI32 starts at capacity 4; doubling to cover an // extra 100 elements lands at 128 (4 → 8 → 16 → 32 → 64 → 128). var v: VecI32 = VecI32.empty(); v.reserve(100); assert(v.capacity as i32, 128); assert(v.len() as i32, 0); } tfn reserveNoOpWhenEnough() { var v: VecI32 = VecI32.withCapacity(100); v.reserve(50); // length 0 + additional 50 ≤ capacity 100, so no growth. assert(v.capacity as i32, 100); } tfn reserveAccountsForCurrentLength() { var v: VecI32 = VecI32.withCapacity(10); v.push(1); v.push(2); // length 2 + additional 9 = 11 > 10, so grow once (10 → 20). v.reserve(9); assert(v.capacity as i32, 20); assert(v.len() as i32, 2); } tfn appendSliceFromStringLiteral() { var v: VecU8 = VecU8.empty(); var s: []u8 = "hi"; v.appendSlice(s); assert(v.len() as i32, 2); // 'h' == 104, 'i' == 105. assert(unwrapU8(v.get(0), 0) as i32, 104); assert(unwrapU8(v.get(1), 0) as i32, 105); } tfn appendSliceTriggersGrow() { // VecU8 starts at capacity 8; appending 20 bytes forces grow // through 8 → 16 → 32. Pre-reserved path means only one // realloc, not log(N). var v: VecU8 = VecU8.empty(); var s: []u8 = "abcdefghijklmnopqrst"; v.appendSlice(s); assert(v.len() as i32, 20); assert(v.capacity as i32, 32); assert(unwrapU8(v.get(0), 0) as i32, 97); // 'a' assert(unwrapU8(v.get(19), 0) as i32, 116); // 't' } tfn appendSliceAfterExistingData() { var v: VecU8 = VecU8.empty(); v.push(65); // 'A' v.push(66); // 'B' var s: []u8 = "CD"; v.appendSlice(s); assert(v.len() as i32, 4); assert(unwrapU8(v.get(0), 0) as i32, 65); assert(unwrapU8(v.get(3), 0) as i32, 68); } // `v[i] = x` and `x = v[i]` route through the compiler's special // `at(self, i) *mut T` dispatch — no `.ptr` reach-through in user // code. The semantics match raw `*mut[] T` indexing (no bounds // check); bounds-checked reads remain available via `get(i)`. tfn indexedWriteAndRead() { var v: VecI32 = VecI32.withCapacity(4); v[0] = 10; v[1] = 20; v[2] = 30; v[3] = 40; // Direct reads through the same `at` path. assert(v[0], 10); assert(v[1], 20); assert(v[2], 30); assert(v[3], 40); } tfn indexedReadAfterPush() { var v: VecI32 = VecI32.empty(); v.push(7); v.push(8); v.push(9); // Pushed values are visible through indexed reads (the buffer // is the same — push just bumps length). assert(v[0], 7); assert(v[1], 8); assert(v[2], 9); } tfn indexedWriteVisibleThroughGet() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); v[1] = 99; // `get(i)` reads through the same backing store, so the // indexed write is visible — confirms both sides agree on the // pointer-math vs the Vec's element view. assert(unwrapI32(v.get(1), 0), 99); } tfn indexedU8Vec() { // VecU8 — different T, exercises generic instantiation of // `at` plus the compiler's u64→u32 narrowing on the index. var v: VecU8 = VecU8.withCapacity(3); v[0] = 100; v[1] = 200; v[2] = 250; assert(v[0] as i32, 100); assert(v[1] as i32, 200); assert(v[2] as i32, 250); } tfn indexedCompoundExpression() { // The index can be an arbitrary u32-typed expression, not // just a literal. Verifies the index parameter is lowered // through the normal expression path before `at` is called. var v: VecI32 = VecI32.withCapacity(8); var i: u32 = 0; while (i < 8) { v[i] = (i as i32) * 10; i = i + 1; } assert(v[0], 0); assert(v[3], 30); assert(v[7], 70); } // Inline `Vec(T)` (no alias) goes through the same `at` dispatch. // The use-site annotation produces a TypeKind::GenericCall instead // of TypeKind::Named, so the compiler's struct-dispatch lookup has // to chase through lookupStruct's GenericCall arm. tfn inlineVecTypeAnnotation() { var v: Vec(i32) = Vec(i32).withCapacity(4); v[0] = 11; v[1] = 22; v[2] = 33; v[3] = 44; assert(v[0], 11); assert(v[3], 44); } tfn inlineVecU8WithCapacity() { var v: Vec(u8) = Vec(u8).withCapacity(3); v[0] = 1; v[1] = 2; v[2] = 3; assert(v[0] as i32, 1); assert(v[2] as i32, 3); } tfn inlineVecEmpty() { // Empty constructor exercises the same instantiation path — // confirms `at` is wired up regardless of which factory the // Vec came from. var v: Vec(i32) = Vec(i32).empty(); v.push(7); v.push(8); assert(v[0], 7); assert(v[1], 8); v[0] = 70; assert(v[0], 70); } tfn inlineVecMethodsStillWork() { // Non-index methods (push, len, get) still resolve when the // declared type is the inline `Vec(T)` form. var v: Vec(u8) = Vec(u8).empty(); v.push(100); v.push(101); assert(v.len() as i32, 2); assert(unwrapU8(v.get(0), 0) as i32, 100); }