Add slice capacity field
d1aed3369d270e931023f93b1cf9abfd7630f335dae55624d73870aa02470b5e
Change slice layout from { ptr, len, <pad> } to { ptr, len, cap },
reusing the existing 4 bytes of padding so the size stays at 16 bytes.
This is the first part of changes to introduce growable slices.
1 parent
7fa02c34
lib/std/arch/rv64/tests/slice.cap.rad
added
+52 -0
| 1 | + | //! Test slice capacity field and @sliceOf builtin. |
|
| 2 | + | ||
| 3 | + | @default fn main() -> i32 { |
|
| 4 | + | // Test that regular slices have cap == len. |
|
| 5 | + | let mut arr: [i32; 4] = [10, 20, 30, 40]; |
|
| 6 | + | let s = &mut arr[..]; |
|
| 7 | + | ||
| 8 | + | if s.cap != 4 { |
|
| 9 | + | return 1; |
|
| 10 | + | } |
|
| 11 | + | if s.len != s.cap { |
|
| 12 | + | return 2; |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | // Test @sliceOf with explicit capacity. |
|
| 16 | + | let ptr = &mut arr[0]; |
|
| 17 | + | let s2 = @sliceOf(ptr, 2, 4); |
|
| 18 | + | ||
| 19 | + | if s2.len != 2 { |
|
| 20 | + | return 3; |
|
| 21 | + | } |
|
| 22 | + | if s2.cap != 4 { |
|
| 23 | + | return 4; |
|
| 24 | + | } |
|
| 25 | + | if s2[0] != 10 { |
|
| 26 | + | return 5; |
|
| 27 | + | } |
|
| 28 | + | if s2[1] != 20 { |
|
| 29 | + | return 6; |
|
| 30 | + | } |
|
| 31 | + | ||
| 32 | + | // Test @sliceOf with zero length. |
|
| 33 | + | let s3 = @sliceOf(ptr, 0, 4); |
|
| 34 | + | ||
| 35 | + | if s3.len != 0 { |
|
| 36 | + | return 7; |
|
| 37 | + | } |
|
| 38 | + | if s3.cap != 4 { |
|
| 39 | + | return 8; |
|
| 40 | + | } |
|
| 41 | + | ||
| 42 | + | // Test @sliceOf still works and has cap == len. |
|
| 43 | + | let s4 = @sliceOf(ptr, 3); |
|
| 44 | + | ||
| 45 | + | if s4.len != 3 { |
|
| 46 | + | return 9; |
|
| 47 | + | } |
|
| 48 | + | if s4.cap != 3 { |
|
| 49 | + | return 10; |
|
| 50 | + | } |
|
| 51 | + | return 0; |
|
| 52 | + | } |
lib/std/lang/ast.rad
+1 -1
| 218 | 218 | pub union Builtin { |
|
| 219 | 219 | /// Size of type in bytes (`@sizeOf`). |
|
| 220 | 220 | SizeOf, |
|
| 221 | 221 | /// Alignment requirement of type (`@alignOf`). |
|
| 222 | 222 | AlignOf, |
|
| 223 | - | /// Construct a slice from pointer and length (`@sliceOf`). |
|
| 223 | + | /// Construct a slice from pointer, length, and optional capacity (`@sliceOf`). |
|
| 224 | 224 | SliceOf, |
|
| 225 | 225 | } |
|
| 226 | 226 | ||
| 227 | 227 | /// Source extent for a node measured in bytes. |
|
| 228 | 228 | pub record Span { |
lib/std/lang/lower.rad
+31 -14
| 226 | 226 | ||
| 227 | 227 | /// Slice data pointer offset. |
|
| 228 | 228 | const SLICE_PTR_OFFSET: i32 = 0; |
|
| 229 | 229 | /// Offset of slice length in slice data structure. |
|
| 230 | 230 | const SLICE_LEN_OFFSET: i32 = 8; |
|
| 231 | + | /// Offset of slice capacity in slice data structure. |
|
| 232 | + | const SLICE_CAP_OFFSET: i32 = 12; |
|
| 231 | 233 | ||
| 232 | 234 | // Trait Object Layout |
|
| 233 | 235 | // |
|
| 234 | 236 | // A trait object is a fat pointer consisting of a data pointer and a |
|
| 235 | 237 | // v-table pointer. `{ data: *T, vtable: *VTable }`. |
| 1307 | 1309 | isUndefined: result.isUndefined, |
|
| 1308 | 1310 | values: result.values, |
|
| 1309 | 1311 | }); |
|
| 1310 | 1312 | } |
|
| 1311 | 1313 | ||
| 1312 | - | /// Emit the in-memory representation of a slice header: `{ ptr, len, padding }`. |
|
| 1314 | + | /// Emit the in-memory representation of a slice header: `{ ptr, len, cap }`. |
|
| 1313 | 1315 | fn dataSliceHeader(b: *mut DataValueBuilder, dataSym: *[u8], len: u32) { |
|
| 1314 | 1316 | dataBuilderPush(b, il::DataValue { |
|
| 1315 | 1317 | item: il::DataItem::Sym(dataSym), |
|
| 1316 | 1318 | count: 1 |
|
| 1317 | 1319 | }); |
| 1321 | 1323 | val: len as i64 |
|
| 1322 | 1324 | }, |
|
| 1323 | 1325 | count: 1 |
|
| 1324 | 1326 | }); |
|
| 1325 | 1327 | dataBuilderPush(b, il::DataValue { |
|
| 1326 | - | item: il::DataItem::Undef, |
|
| 1327 | - | count: 4 // 4 byte padding. |
|
| 1328 | + | item: il::DataItem::Val { |
|
| 1329 | + | typ: il::Type::W32, |
|
| 1330 | + | val: len as i64 |
|
| 1331 | + | }, |
|
| 1332 | + | count: 1 |
|
| 1328 | 1333 | }); |
|
| 1329 | 1334 | } |
|
| 1330 | 1335 | ||
| 1331 | 1336 | /// Lower a compile-time `&[...]` expression to a concrete slice header. |
|
| 1332 | 1337 | fn lowerConstAddressSliceInto( |
| 1826 | 1831 | // Get data address. |
|
| 1827 | 1832 | let ptrReg = nextReg(self); |
|
| 1828 | 1833 | emit(self, il::Instr::Copy { dst: ptrReg, val: il::Val::DataSym(dataName) }); |
|
| 1829 | 1834 | ||
| 1830 | 1835 | return try buildSliceValue( |
|
| 1831 | - | self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64) |
|
| 1836 | + | self, elemTy, mutable, il::Val::Reg(ptrReg), il::Val::Imm(length as i64), il::Val::Imm(length as i64) |
|
| 1832 | 1837 | ); |
|
| 1833 | 1838 | } |
|
| 1834 | 1839 | ||
| 1835 | 1840 | /// Generate a unique data name for inline literals, eg. `fnName/N` |
|
| 1836 | 1841 | fn nextDataName(self: *mut FnLowerer) -> *[u8] throws (LowerError) { |
| 2292 | 2297 | let lenReg = nextReg(self); |
|
| 2293 | 2298 | emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET); |
|
| 2294 | 2299 | return il::Val::Reg(lenReg); |
|
| 2295 | 2300 | } |
|
| 2296 | 2301 | ||
| 2302 | + | /// Load the capacity from a slice value. |
|
| 2303 | + | fn loadSliceCap(self: *mut FnLowerer, sliceReg: il::Reg) -> il::Val { |
|
| 2304 | + | let capReg = nextReg(self); |
|
| 2305 | + | emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET); |
|
| 2306 | + | return il::Val::Reg(capReg); |
|
| 2307 | + | } |
|
| 2308 | + | ||
| 2297 | 2309 | /// Emit a load instruction for a scalar value at `src` plus `offset`. |
|
| 2298 | 2310 | /// For reading values that may be aggregates, use `emitRead` instead. |
|
| 2299 | 2311 | fn emitLoad(self: *mut FnLowerer, src: il::Reg, offset: i32, typ: resolver::Type) -> il::Val { |
|
| 2300 | 2312 | let dst = nextReg(self); |
|
| 2301 | 2313 | let ilTyp = ilType(self.low, typ); |
| 4055 | 4067 | }; |
|
| 4056 | 4068 | if let case resolver::Type::Pointer { .. } = *inner { |
|
| 4057 | 4069 | return il::Val::Imm(0); |
|
| 4058 | 4070 | } |
|
| 4059 | 4071 | if let case resolver::Type::Slice { item, mutable } = *inner { |
|
| 4060 | - | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4072 | + | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4061 | 4073 | } |
|
| 4062 | 4074 | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4063 | 4075 | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4064 | 4076 | } |
|
| 4065 | 4077 |
| 4075 | 4087 | successType, &self.fnType.throwList[..self.fnType.throwListLen] |
|
| 4076 | 4088 | ); |
|
| 4077 | 4089 | return try buildTagged(self, layout, tag, payload, payloadType, 8, RESULT_VAL_OFFSET); |
|
| 4078 | 4090 | } |
|
| 4079 | 4091 | ||
| 4080 | - | /// Build a slice aggregate from a data pointer and length. |
|
| 4092 | + | /// Build a slice aggregate from a data pointer, length and capacity. |
|
| 4081 | 4093 | fn buildSliceValue( |
|
| 4082 | 4094 | self: *mut FnLowerer, |
|
| 4083 | 4095 | elemTy: *resolver::Type, |
|
| 4084 | 4096 | mutable: bool, |
|
| 4085 | 4097 | ptrVal: il::Val, |
|
| 4086 | - | lenVal: il::Val |
|
| 4098 | + | lenVal: il::Val, |
|
| 4099 | + | capVal: il::Val |
|
| 4087 | 4100 | ) -> il::Val throws (LowerError) { |
|
| 4088 | 4101 | let sliceType = resolver::Type::Slice { item: elemTy, mutable }; |
|
| 4089 | 4102 | let dst = try emitReserve(self, sliceType); |
|
| 4090 | 4103 | let ptrTy = resolver::Type::Pointer { target: elemTy, mutable }; |
|
| 4091 | 4104 | ||
| 4092 | 4105 | try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal); |
|
| 4093 | 4106 | try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal); |
|
| 4107 | + | try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal); |
|
| 4094 | 4108 | ||
| 4095 | 4109 | return il::Val::Reg(dst); |
|
| 4096 | 4110 | } |
|
| 4097 | 4111 | ||
| 4098 | 4112 | /// Build a trait object fat pointer from a data pointer and a v-table. |
| 4806 | 4820 | b: startVal, |
|
| 4807 | 4821 | }); |
|
| 4808 | 4822 | sliceLen = il::Val::Reg(lenReg); |
|
| 4809 | 4823 | } |
|
| 4810 | 4824 | return try buildSliceValue( |
|
| 4811 | - | self, info.itemType, info.mutable, il::Val::Reg(dataReg), sliceLen |
|
| 4825 | + | self, info.itemType, info.mutable, il::Val::Reg(dataReg), sliceLen, sliceLen |
|
| 4812 | 4826 | ); |
|
| 4813 | 4827 | } |
|
| 4814 | 4828 | ||
| 4815 | 4829 | /// Lower an address-of (`&x`) expression. |
|
| 4816 | 4830 | fn lowerAddressOf(self: *mut FnLowerer, node: *ast::Node, addr: ast::AddressOf) -> il::Val throws (LowerError) { |
| 4892 | 4906 | }; |
|
| 4893 | 4907 | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 4894 | 4908 | throw LowerError::UnexpectedType(&sliceTy); |
|
| 4895 | 4909 | }; |
|
| 4896 | 4910 | if elements.len == 0 { // Empty slices don't need to be stored as data. |
|
| 4897 | - | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4911 | + | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4898 | 4912 | } |
|
| 4899 | 4913 | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 4900 | 4914 | let elemLayout = resolver::getTypeLayout(*item); |
|
| 4901 | 4915 | return try lowerConstSliceLiteral(self, item, mutable, elements, elemLayout); |
|
| 4902 | 4916 | } else { |
| 4949 | 4963 | ||
| 4950 | 4964 | try emitStore(self, arrayReg, offset as i32, *elemTy, elemVal); |
|
| 4951 | 4965 | } |
|
| 4952 | 4966 | let lenVal = il::Val::Imm(elements.len as i64); |
|
| 4953 | 4967 | ||
| 4954 | - | return try buildSliceValue(self, elemTy, mutable, il::Val::Reg(arrayReg), lenVal); |
|
| 4968 | + | return try buildSliceValue(self, elemTy, mutable, il::Val::Reg(arrayReg), lenVal, lenVal); |
|
| 4955 | 4969 | } |
|
| 4956 | 4970 | ||
| 4957 | 4971 | /// Lower the common element pointer computation for subscript operations. |
|
| 4958 | 4972 | /// Handles both arrays and slices by resolving the container type, extracting |
|
| 4959 | 4973 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
| 5918 | 5932 | return try constValueToVal(self, constVal, node); |
|
| 5919 | 5933 | } |
|
| 5920 | 5934 | } |
|
| 5921 | 5935 | } |
|
| 5922 | 5936 | ||
| 5923 | - | /// Lower a `@sliceOf(ptr, len)` builtin call. |
|
| 5937 | + | /// Lower a `@sliceOf(ptr, len)` or `@sliceOf(ptr, len, cap)` builtin call. |
|
| 5924 | 5938 | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: ast::NodeList) -> il::Val throws (LowerError) { |
|
| 5925 | - | if args.len != 2 { |
|
| 5939 | + | if args.len != 2 and args.len != 3 { |
|
| 5926 | 5940 | throw LowerError::InvalidArgCount; |
|
| 5927 | 5941 | } |
|
| 5928 | 5942 | let sliceTy = resolver::typeFor(self.low.resolver, node) else { |
|
| 5929 | 5943 | throw LowerError::MissingType(node); |
|
| 5930 | 5944 | }; |
|
| 5931 | 5945 | let case resolver::Type::Slice { item, mutable } = sliceTy else { |
|
| 5932 | 5946 | throw LowerError::ExpectedSliceOrArray; |
|
| 5933 | 5947 | }; |
|
| 5934 | 5948 | let ptrVal = try lowerExpr(self, args.list[0]); |
|
| 5935 | 5949 | let lenVal = try lowerExpr(self, args.list[1]); |
|
| 5936 | - | ||
| 5937 | - | return try buildSliceValue(self, item, mutable, ptrVal, lenVal); |
|
| 5950 | + | let mut capVal = lenVal; |
|
| 5951 | + | if args.len == 3 { |
|
| 5952 | + | capVal = try lowerExpr(self, args.list[2]); |
|
| 5953 | + | } |
|
| 5954 | + | return try buildSliceValue(self, item, mutable, ptrVal, lenVal, capVal); |
|
| 5938 | 5955 | } |
|
| 5939 | 5956 | ||
| 5940 | 5957 | /// Lower a `try` expression. |
|
| 5941 | 5958 | fn lowerTry(self: *mut FnLowerer, node: *ast::Node, t: ast::Try) -> il::Val throws (LowerError) { |
|
| 5942 | 5959 | let case ast::NodeValue::Call(callExpr) = t.expr.value else { |
lib/std/lang/lower/tests/array.slice.full.ril
+1 -0
| 1 | 1 | fn w64 $sliceFull(w64 %0, w64 %1) { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %2 16 8; |
|
| 4 | 4 | store w64 %1 %2 0; |
|
| 5 | 5 | store w32 4 %2 8; |
|
| 6 | + | store w32 4 %2 12; |
|
| 6 | 7 | blit %0 %2 16; |
|
| 7 | 8 | ret %0; |
|
| 8 | 9 | } |
lib/std/lang/lower/tests/array.slice.openend.ril
+1 -0
| 4 | 4 | add w64 %4 %1 %3; |
|
| 5 | 5 | sub w32 %5 4 %2; |
|
| 6 | 6 | reserve %6 16 8; |
|
| 7 | 7 | store w64 %4 %6 0; |
|
| 8 | 8 | store w32 %5 %6 8; |
|
| 9 | + | store w32 %5 %6 12; |
|
| 9 | 10 | blit %0 %6 16; |
|
| 10 | 11 | ret %0; |
|
| 11 | 12 | } |
lib/std/lang/lower/tests/array.slice.openstart.ril
+1 -0
| 1 | 1 | fn w64 $sliceOpenStart(w64 %0, w64 %1, w32 %2) { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %3 16 8; |
|
| 4 | 4 | store w64 %1 %3 0; |
|
| 5 | 5 | store w32 %2 %3 8; |
|
| 6 | + | store w32 %2 %3 12; |
|
| 6 | 7 | blit %0 %3 16; |
|
| 7 | 8 | ret %0; |
|
| 8 | 9 | } |
lib/std/lang/lower/tests/const.array.strings.slice.ril
+3 -3
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | data $WORDS align 8 { |
|
| 10 | 10 | sym $WORDS$0; |
|
| 11 | 11 | w32 5; |
|
| 12 | - | undef * 4; |
|
| 12 | + | w32 5; |
|
| 13 | 13 | sym $WORDS$1; |
|
| 14 | 14 | w32 6; |
|
| 15 | - | undef * 4; |
|
| 15 | + | w32 6; |
|
| 16 | 16 | sym $WORDS$0; |
|
| 17 | 17 | w32 5; |
|
| 18 | - | undef * 4; |
|
| 18 | + | w32 5; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | fn w32 $totalLen() { |
|
| 22 | 22 | @entry0 |
|
| 23 | 23 | copy %0 $WORDS; |
lib/std/lang/lower/tests/const.record.union.ril
+3 -3
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | data $KEYWORDS align 8 { |
|
| 14 | 14 | sym $KEYWORDS$0; |
|
| 15 | 15 | w32 2; |
|
| 16 | - | undef * 4; |
|
| 16 | + | w32 2; |
|
| 17 | 17 | w8 0; |
|
| 18 | 18 | undef * 7; |
|
| 19 | 19 | sym $KEYWORDS$1; |
|
| 20 | 20 | w32 3; |
|
| 21 | - | undef * 4; |
|
| 21 | + | w32 3; |
|
| 22 | 22 | w8 1; |
|
| 23 | 23 | undef * 7; |
|
| 24 | 24 | sym $KEYWORDS$2; |
|
| 25 | 25 | w32 2; |
|
| 26 | - | undef * 4; |
|
| 26 | + | w32 2; |
|
| 27 | 27 | w8 2; |
|
| 28 | 28 | undef * 7; |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | fn w32 $getFirstTag() { |
lib/std/lang/lower/tests/const.slice.of.slices.ril
+5 -5
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | data $GROUPS$2 align 8 { |
|
| 10 | 10 | sym $GROUPS$0; |
|
| 11 | 11 | w32 2; |
|
| 12 | - | undef * 4; |
|
| 12 | + | w32 2; |
|
| 13 | 13 | sym $GROUPS$1; |
|
| 14 | 14 | w32 2; |
|
| 15 | - | undef * 4; |
|
| 15 | + | w32 2; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | data $GROUPS$3 align 1 { |
|
| 19 | 19 | str "efg"; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | data $GROUPS$4 align 8 { |
|
| 23 | 23 | sym $GROUPS$3; |
|
| 24 | 24 | w32 3; |
|
| 25 | - | undef * 4; |
|
| 25 | + | w32 3; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | data $GROUPS align 8 { |
|
| 29 | 29 | sym $GROUPS$2; |
|
| 30 | 30 | w32 2; |
|
| 31 | - | undef * 4; |
|
| 31 | + | w32 2; |
|
| 32 | 32 | sym $GROUPS$4; |
|
| 33 | 33 | w32 1; |
|
| 34 | - | undef * 4; |
|
| 34 | + | w32 1; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | fn w32 $totalLen() { |
|
| 38 | 38 | @entry0 |
|
| 39 | 39 | copy %0 $GROUPS; |
lib/std/lang/lower/tests/const.string.ril
+2 -1
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | data $HELLO align 8 { |
|
| 6 | 6 | sym $HELLO$0; |
|
| 7 | 7 | w32 5; |
|
| 8 | - | undef * 4; |
|
| 8 | + | w32 5; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn w32 $getLen() { |
|
| 12 | 12 | @entry0 |
|
| 13 | 13 | copy %0 $HELLO$0; |
|
| 14 | 14 | reserve %1 16 8; |
|
| 15 | 15 | store w64 %0 %1 0; |
|
| 16 | 16 | store w32 5 %1 8; |
|
| 17 | + | store w32 5 %1 12; |
|
| 17 | 18 | load w32 %2 %1 8; |
|
| 18 | 19 | ret %2; |
|
| 19 | 20 | } |
lib/std/lang/lower/tests/const.string.scoped.names.ril
+6 -4
| 3 | 3 | } |
|
| 4 | 4 | ||
| 5 | 5 | data $HELLO1 align 8 { |
|
| 6 | 6 | sym $HELLO1$0; |
|
| 7 | 7 | w32 5; |
|
| 8 | - | undef * 4; |
|
| 8 | + | w32 5; |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | data $HELLO2 align 8 { |
|
| 12 | 12 | sym $HELLO1$0; |
|
| 13 | 13 | w32 5; |
|
| 14 | - | undef * 4; |
|
| 14 | + | w32 5; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | data $MSGS$3 align 1 { |
|
| 18 | 18 | str "world"; |
|
| 19 | 19 | } |
|
| 20 | 20 | ||
| 21 | 21 | data $MSGS align 8 { |
|
| 22 | 22 | sym $HELLO1$0; |
|
| 23 | 23 | w32 5; |
|
| 24 | - | undef * 4; |
|
| 24 | + | w32 5; |
|
| 25 | 25 | sym $MSGS$3; |
|
| 26 | 26 | w32 5; |
|
| 27 | - | undef * 4; |
|
| 27 | + | w32 5; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | fn w32 $totalLen() { |
|
| 31 | 31 | @entry0 |
|
| 32 | 32 | copy %0 $HELLO1$0; |
|
| 33 | 33 | reserve %1 16 8; |
|
| 34 | 34 | store w64 %0 %1 0; |
|
| 35 | 35 | store w32 5 %1 8; |
|
| 36 | + | store w32 5 %1 12; |
|
| 36 | 37 | load w32 %2 %1 8; |
|
| 37 | 38 | copy %3 $HELLO1$0; |
|
| 38 | 39 | reserve %4 16 8; |
|
| 39 | 40 | store w64 %3 %4 0; |
|
| 40 | 41 | store w32 5 %4 8; |
|
| 42 | + | store w32 5 %4 12; |
|
| 41 | 43 | load w32 %5 %4 8; |
|
| 42 | 44 | add w32 %6 %2 %5; |
|
| 43 | 45 | copy %7 $MSGS; |
|
| 44 | 46 | load w32 %8 %7 8; |
|
| 45 | 47 | add w32 %9 %6 %8; |
lib/std/lang/lower/tests/field.aggregate.ril
+1 -0
| 33 | 33 | store w32 3 %0 8; |
|
| 34 | 34 | reserve %1 24 8; |
|
| 35 | 35 | reserve %2 16 8; |
|
| 36 | 36 | store w64 %0 %2 0; |
|
| 37 | 37 | store w32 3 %2 8; |
|
| 38 | + | store w32 3 %2 12; |
|
| 38 | 39 | blit %1 %2 16; |
|
| 39 | 40 | store w32 77 %1 16; |
|
| 40 | 41 | load w32 %3 %1 8; |
|
| 41 | 42 | ret %3; |
|
| 42 | 43 | } |
lib/std/lang/lower/tests/literal.slice.bytes.ril
+1 -0
| 8 | 8 | @entry0 |
|
| 9 | 9 | copy %0 $byteSlice0; |
|
| 10 | 10 | reserve %1 16 8; |
|
| 11 | 11 | store w64 %0 %1 0; |
|
| 12 | 12 | store w32 3 %1 8; |
|
| 13 | + | store w32 3 %1 12; |
|
| 13 | 14 | load w32 %2 %1 8; |
|
| 14 | 15 | ret %2; |
|
| 15 | 16 | } |
lib/std/lang/lower/tests/literal.slice.dedup.ril
+2 -0
| 8 | 8 | @entry0 |
|
| 9 | 9 | copy %0 $dedupSlice0; |
|
| 10 | 10 | reserve %1 16 8; |
|
| 11 | 11 | store w64 %0 %1 0; |
|
| 12 | 12 | store w32 3 %1 8; |
|
| 13 | + | store w32 3 %1 12; |
|
| 13 | 14 | copy %2 $dedupSlice0; |
|
| 14 | 15 | reserve %3 16 8; |
|
| 15 | 16 | store w64 %2 %3 0; |
|
| 16 | 17 | store w32 3 %3 8; |
|
| 18 | + | store w32 3 %3 12; |
|
| 17 | 19 | load w32 %4 %1 8; |
|
| 18 | 20 | load w32 %5 %3 8; |
|
| 19 | 21 | add w32 %6 %4 %5; |
|
| 20 | 22 | ret %6; |
|
| 21 | 23 | } |
lib/std/lang/lower/tests/literal.slice.empty.ril
+1 -0
| 1 | 1 | fn w32 $emptySlice() { |
|
| 2 | 2 | @entry0 |
|
| 3 | 3 | reserve %0 16 8; |
|
| 4 | 4 | store w64 0 %0 0; |
|
| 5 | 5 | store w32 0 %0 8; |
|
| 6 | + | store w32 0 %0 12; |
|
| 6 | 7 | load w32 %1 %0 8; |
|
| 7 | 8 | ret %1; |
|
| 8 | 9 | } |
lib/std/lang/lower/tests/literal.slice.multi.ril
+2 -0
| 13 | 13 | @entry0 |
|
| 14 | 14 | copy %0 $multiSlice0; |
|
| 15 | 15 | reserve %1 16 8; |
|
| 16 | 16 | store w64 %0 %1 0; |
|
| 17 | 17 | store w32 2 %1 8; |
|
| 18 | + | store w32 2 %1 12; |
|
| 18 | 19 | copy %2 $multiSlice1; |
|
| 19 | 20 | reserve %3 16 8; |
|
| 20 | 21 | store w64 %2 %3 0; |
|
| 21 | 22 | store w32 3 %3 8; |
|
| 23 | + | store w32 3 %3 12; |
|
| 22 | 24 | load w32 %4 %1 8; |
|
| 23 | 25 | load w32 %5 %3 8; |
|
| 24 | 26 | add w32 %6 %4 %5; |
|
| 25 | 27 | ret %6; |
|
| 26 | 28 | } |
lib/std/lang/lower/tests/literal.slice.record.ril
+1 -0
| 9 | 9 | @entry0 |
|
| 10 | 10 | copy %0 $localSliceOfRecords0; |
|
| 11 | 11 | reserve %1 16 8; |
|
| 12 | 12 | store w64 %0 %1 0; |
|
| 13 | 13 | store w32 2 %1 8; |
|
| 14 | + | store w32 2 %1 12; |
|
| 14 | 15 | load w32 %2 %1 8; |
|
| 15 | 16 | br.ult w32 0 %2 @guard#pass1 @guard#trap2; |
|
| 16 | 17 | @guard#pass1 |
|
| 17 | 18 | load w64 %3 %1 0; |
|
| 18 | 19 | sload w32 %4 %3 0; |
lib/std/lang/lower/tests/literal.slice.ril
+1 -0
| 8 | 8 | @entry0 |
|
| 9 | 9 | copy %0 $sliceLiteral0; |
|
| 10 | 10 | reserve %1 16 8; |
|
| 11 | 11 | store w64 %0 %1 0; |
|
| 12 | 12 | store w32 3 %1 8; |
|
| 13 | + | store w32 3 %1 12; |
|
| 13 | 14 | load w32 %2 %1 8; |
|
| 14 | 15 | ret %2; |
|
| 15 | 16 | } |
lib/std/lang/lower/tests/literal.string.dedup.ril
+2 -0
| 6 | 6 | @entry0 |
|
| 7 | 7 | copy %0 $dedupString0; |
|
| 8 | 8 | reserve %1 16 8; |
|
| 9 | 9 | store w64 %0 %1 0; |
|
| 10 | 10 | store w32 5 %1 8; |
|
| 11 | + | store w32 5 %1 12; |
|
| 11 | 12 | copy %2 $dedupString0; |
|
| 12 | 13 | reserve %3 16 8; |
|
| 13 | 14 | store w64 %2 %3 0; |
|
| 14 | 15 | store w32 5 %3 8; |
|
| 16 | + | store w32 5 %3 12; |
|
| 15 | 17 | load w32 %4 %1 8; |
|
| 16 | 18 | load w32 %5 %3 8; |
|
| 17 | 19 | add w32 %6 %4 %5; |
|
| 18 | 20 | ret %6; |
|
| 19 | 21 | } |
lib/std/lang/lower/tests/literal.string.empty.ril
+1 -0
| 6 | 6 | @entry0 |
|
| 7 | 7 | copy %0 $emptyString0; |
|
| 8 | 8 | reserve %1 16 8; |
|
| 9 | 9 | store w64 %0 %1 0; |
|
| 10 | 10 | store w32 0 %1 8; |
|
| 11 | + | store w32 0 %1 12; |
|
| 11 | 12 | load w32 %2 %1 8; |
|
| 12 | 13 | ret %2; |
|
| 13 | 14 | } |
lib/std/lang/lower/tests/literal.string.fns.ril
+2 -0
| 10 | 10 | @entry0 |
|
| 11 | 11 | copy %0 $first0; |
|
| 12 | 12 | reserve %1 16 8; |
|
| 13 | 13 | store w64 %0 %1 0; |
|
| 14 | 14 | store w32 3 %1 8; |
|
| 15 | + | store w32 3 %1 12; |
|
| 15 | 16 | load w32 %2 %1 8; |
|
| 16 | 17 | ret %2; |
|
| 17 | 18 | } |
|
| 18 | 19 | ||
| 19 | 20 | fn w32 $second() { |
|
| 20 | 21 | @entry0 |
|
| 21 | 22 | copy %0 $second0; |
|
| 22 | 23 | reserve %1 16 8; |
|
| 23 | 24 | store w64 %0 %1 0; |
|
| 24 | 25 | store w32 3 %1 8; |
|
| 26 | + | store w32 3 %1 12; |
|
| 25 | 27 | load w32 %2 %1 8; |
|
| 26 | 28 | ret %2; |
|
| 27 | 29 | } |
lib/std/lang/lower/tests/literal.string.multi.ril
+2 -0
| 10 | 10 | @entry0 |
|
| 11 | 11 | copy %0 $multiString0; |
|
| 12 | 12 | reserve %1 16 8; |
|
| 13 | 13 | store w64 %0 %1 0; |
|
| 14 | 14 | store w32 5 %1 8; |
|
| 15 | + | store w32 5 %1 12; |
|
| 15 | 16 | copy %2 $multiString1; |
|
| 16 | 17 | reserve %3 16 8; |
|
| 17 | 18 | store w64 %2 %3 0; |
|
| 18 | 19 | store w32 5 %3 8; |
|
| 20 | + | store w32 5 %3 12; |
|
| 19 | 21 | load w32 %4 %1 8; |
|
| 20 | 22 | load w32 %5 %3 8; |
|
| 21 | 23 | add w32 %6 %4 %5; |
|
| 22 | 24 | ret %6; |
|
| 23 | 25 | } |
lib/std/lang/lower/tests/literal.string.ril
+1 -0
| 6 | 6 | @entry0 |
|
| 7 | 7 | copy %0 $stringLen0; |
|
| 8 | 8 | reserve %1 16 8; |
|
| 9 | 9 | store w64 %0 %1 0; |
|
| 10 | 10 | store w32 5 %1 8; |
|
| 11 | + | store w32 5 %1 12; |
|
| 11 | 12 | load w32 %2 %1 8; |
|
| 12 | 13 | ret %2; |
|
| 13 | 14 | } |
lib/std/lang/lower/tests/opt.slice.npo.ril
+1 -0
| 7 | 7 | fn w64 $nilSlice(w64 %0) { |
|
| 8 | 8 | @entry0 |
|
| 9 | 9 | reserve %1 16 8; |
|
| 10 | 10 | store w64 0 %1 0; |
|
| 11 | 11 | store w32 0 %1 8; |
|
| 12 | + | store w32 0 %1 12; |
|
| 12 | 13 | blit %0 %1 16; |
|
| 13 | 14 | ret %0; |
|
| 14 | 15 | } |
|
| 15 | 16 | ||
| 16 | 17 | fn w8 $nilCheck(w64 %0) { |
lib/std/lang/lower/tests/slice.basic.rad
+10 -0
| 1 | 1 | /// Returns the length field from a slice header. |
|
| 2 | 2 | fn sliceLen(s: *[i32]) -> u32 { |
|
| 3 | 3 | return s.len; |
|
| 4 | 4 | } |
|
| 5 | 5 | ||
| 6 | + | /// Returns the capacity field from a slice header. |
|
| 7 | + | fn sliceCap(s: *[i32]) -> u32 { |
|
| 8 | + | return s.cap; |
|
| 9 | + | } |
|
| 10 | + | ||
| 6 | 11 | /// Returns the pointer field from a slice header. |
|
| 7 | 12 | fn slicePtr(s: *[i32]) -> *i32 { |
|
| 8 | 13 | return s.ptr; |
|
| 9 | 14 | } |
|
| 10 | 15 |
| 16 | 21 | ||
| 17 | 22 | /// Builds a slice header from a pointer and length. |
|
| 18 | 23 | fn sliceOf(ptr: *i32, len: u32) -> *[i32] { |
|
| 19 | 24 | return @sliceOf(ptr, len); |
|
| 20 | 25 | } |
|
| 26 | + | ||
| 27 | + | /// Builds a slice header from pointer, length, and capacity. |
|
| 28 | + | fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] { |
|
| 29 | + | return @sliceOf(ptr, len, cap); |
|
| 30 | + | } |
lib/std/lang/lower/tests/slice.basic.ril
+17 -0
| 2 | 2 | @entry0 |
|
| 3 | 3 | load w32 %1 %0 8; |
|
| 4 | 4 | ret %1; |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | + | fn w32 $sliceCap(w64 %0) { |
|
| 8 | + | @entry0 |
|
| 9 | + | load w32 %1 %0 12; |
|
| 10 | + | ret %1; |
|
| 11 | + | } |
|
| 12 | + | ||
| 7 | 13 | fn w64 $slicePtr(w64 %0) { |
|
| 8 | 14 | @entry0 |
|
| 9 | 15 | load w64 %1 %0 0; |
|
| 10 | 16 | ret %1; |
|
| 11 | 17 | } |
| 21 | 27 | fn w64 $sliceOf(w64 %0, w64 %1, w32 %2) { |
|
| 22 | 28 | @entry0 |
|
| 23 | 29 | reserve %3 16 8; |
|
| 24 | 30 | store w64 %1 %3 0; |
|
| 25 | 31 | store w32 %2 %3 8; |
|
| 32 | + | store w32 %2 %3 12; |
|
| 26 | 33 | blit %0 %3 16; |
|
| 27 | 34 | ret %0; |
|
| 28 | 35 | } |
|
| 36 | + | ||
| 37 | + | fn w64 $sliceOfWithCap(w64 %0, w64 %1, w32 %2, w32 %3) { |
|
| 38 | + | @entry0 |
|
| 39 | + | reserve %4 16 8; |
|
| 40 | + | store w64 %1 %4 0; |
|
| 41 | + | store w32 %2 %4 8; |
|
| 42 | + | store w32 %3 %4 12; |
|
| 43 | + | blit %0 %4 16; |
|
| 44 | + | ret %0; |
|
| 45 | + | } |
lib/std/lang/lower/tests/slice.range.ril
+5 -0
| 6 | 6 | add w64 %7 %4 %6; |
|
| 7 | 7 | sub w32 %8 %3 %2; |
|
| 8 | 8 | reserve %9 16 8; |
|
| 9 | 9 | store w64 %7 %9 0; |
|
| 10 | 10 | store w32 %8 %9 8; |
|
| 11 | + | store w32 %8 %9 12; |
|
| 11 | 12 | blit %0 %9 16; |
|
| 12 | 13 | ret %0; |
|
| 13 | 14 | } |
|
| 14 | 15 | ||
| 15 | 16 | fn w64 $sliceRangeOpenEnd(w64 %0, w64 %1, w32 %2) { |
| 20 | 21 | add w64 %6 %3 %5; |
|
| 21 | 22 | sub w32 %7 %4 %2; |
|
| 22 | 23 | reserve %8 16 8; |
|
| 23 | 24 | store w64 %6 %8 0; |
|
| 24 | 25 | store w32 %7 %8 8; |
|
| 26 | + | store w32 %7 %8 12; |
|
| 25 | 27 | blit %0 %8 16; |
|
| 26 | 28 | ret %0; |
|
| 27 | 29 | } |
|
| 28 | 30 | ||
| 29 | 31 | fn w64 $sliceRangeOpenStart(w64 %0, w64 %1, w32 %2) { |
| 31 | 33 | load w64 %3 %1 0; |
|
| 32 | 34 | load w32 %4 %1 8; |
|
| 33 | 35 | reserve %5 16 8; |
|
| 34 | 36 | store w64 %3 %5 0; |
|
| 35 | 37 | store w32 %2 %5 8; |
|
| 38 | + | store w32 %2 %5 12; |
|
| 36 | 39 | blit %0 %5 16; |
|
| 37 | 40 | ret %0; |
|
| 38 | 41 | } |
|
| 39 | 42 | ||
| 40 | 43 | fn w64 $sliceRangeFull(w64 %0, w64 %1) { |
| 42 | 45 | load w64 %2 %1 0; |
|
| 43 | 46 | load w32 %3 %1 8; |
|
| 44 | 47 | reserve %4 16 8; |
|
| 45 | 48 | store w64 %2 %4 0; |
|
| 46 | 49 | store w32 %3 %4 8; |
|
| 50 | + | store w32 %3 %4 12; |
|
| 47 | 51 | blit %0 %4 16; |
|
| 48 | 52 | ret %0; |
|
| 49 | 53 | } |
|
| 50 | 54 | ||
| 51 | 55 | fn w64 $sliceArray(w64 %0, w64 %1) { |
| 54 | 58 | add w64 %3 %1 %2; |
|
| 55 | 59 | sub w32 %4 3 1; |
|
| 56 | 60 | reserve %5 16 8; |
|
| 57 | 61 | store w64 %3 %5 0; |
|
| 58 | 62 | store w32 %4 %5 8; |
|
| 63 | + | store w32 %4 %5 12; |
|
| 59 | 64 | blit %0 %5 16; |
|
| 60 | 65 | ret %0; |
|
| 61 | 66 | } |
lib/std/lang/lower/tests/slice.runtime.i32.ril
+3 -0
| 3 | 3 | reserve %2 4 4; |
|
| 4 | 4 | store w32 %1 %2 0; |
|
| 5 | 5 | reserve %3 16 8; |
|
| 6 | 6 | store w64 %2 %3 0; |
|
| 7 | 7 | store w32 1 %3 8; |
|
| 8 | + | store w32 1 %3 12; |
|
| 8 | 9 | blit %0 %3 16; |
|
| 9 | 10 | ret %0; |
|
| 10 | 11 | } |
|
| 11 | 12 | ||
| 12 | 13 | fn w64 $sliceWithVars(w64 %0, w32 %1, w32 %2) { |
| 15 | 16 | store w32 %1 %3 0; |
|
| 16 | 17 | store w32 %2 %3 4; |
|
| 17 | 18 | reserve %4 16 8; |
|
| 18 | 19 | store w64 %3 %4 0; |
|
| 19 | 20 | store w32 2 %4 8; |
|
| 21 | + | store w32 2 %4 12; |
|
| 20 | 22 | blit %0 %4 16; |
|
| 21 | 23 | ret %0; |
|
| 22 | 24 | } |
|
| 23 | 25 | ||
| 24 | 26 | fn w64 $sliceWithMixed(w64 %0, w32 %1) { |
| 28 | 30 | store w32 42 %2 4; |
|
| 29 | 31 | store w32 %1 %2 8; |
|
| 30 | 32 | reserve %3 16 8; |
|
| 31 | 33 | store w64 %2 %3 0; |
|
| 32 | 34 | store w32 3 %3 8; |
|
| 35 | + | store w32 3 %3 12; |
|
| 33 | 36 | blit %0 %3 16; |
|
| 34 | 37 | ret %0; |
|
| 35 | 38 | } |
lib/std/lang/lower/tests/slice.runtime.literal.ril
+2 -0
| 3 | 3 | reserve %2 1 1; |
|
| 4 | 4 | store w8 %1 %2 0; |
|
| 5 | 5 | reserve %3 16 8; |
|
| 6 | 6 | store w64 %2 %3 0; |
|
| 7 | 7 | store w32 1 %3 8; |
|
| 8 | + | store w32 1 %3 12; |
|
| 8 | 9 | blit %0 %3 16; |
|
| 9 | 10 | ret %0; |
|
| 10 | 11 | } |
|
| 11 | 12 | ||
| 12 | 13 | fn w64 $sliceWithVars(w64 %0, w8 %1, w8 %2) { |
| 15 | 16 | store w8 %1 %3 0; |
|
| 16 | 17 | store w8 %2 %3 1; |
|
| 17 | 18 | reserve %4 16 8; |
|
| 18 | 19 | store w64 %3 %4 0; |
|
| 19 | 20 | store w32 2 %4 8; |
|
| 21 | + | store w32 2 %4 12; |
|
| 20 | 22 | blit %0 %4 16; |
|
| 21 | 23 | ret %0; |
|
| 22 | 24 | } |
lib/std/lang/resolver.rad
+17 -8
| 89 | 89 | ||
| 90 | 90 | /// Identifier for the synthetic `len` field. |
|
| 91 | 91 | pub const LEN_FIELD: *[u8] = "len"; |
|
| 92 | 92 | /// Identifier for the synthetic `ptr` field. |
|
| 93 | 93 | pub const PTR_FIELD: *[u8] = "ptr"; |
|
| 94 | + | /// Identifier for the synthetic `cap` field. |
|
| 95 | + | pub const CAP_FIELD: *[u8] = "cap"; |
|
| 94 | 96 | ||
| 95 | 97 | /// Maximum `u16` value. |
|
| 96 | 98 | const U16_MAX: u16 = 0xFFFF; |
|
| 97 | 99 | /// Maximum `u8` value. |
|
| 98 | 100 | const U8_MAX: u16 = 0xFF; |
| 1929 | 1931 | case 1 => return RecordField { |
|
| 1930 | 1932 | name: LEN_FIELD, |
|
| 1931 | 1933 | fieldType: Type::U32, |
|
| 1932 | 1934 | offset: PTR_SIZE as i32, |
|
| 1933 | 1935 | }, |
|
| 1936 | + | case 2 => return RecordField { |
|
| 1937 | + | name: CAP_FIELD, |
|
| 1938 | + | fieldType: Type::U32, |
|
| 1939 | + | offset: PTR_SIZE as i32 + 4, |
|
| 1940 | + | }, |
|
| 1934 | 1941 | else => return nil, |
|
| 1935 | 1942 | } |
|
| 1936 | 1943 | } |
|
| 1937 | 1944 | else => return nil, |
|
| 1938 | 1945 | } |
| 4649 | 4656 | self: *mut Resolver, |
|
| 4650 | 4657 | node: *ast::Node, |
|
| 4651 | 4658 | kind: ast::Builtin, |
|
| 4652 | 4659 | args: *ast::NodeList |
|
| 4653 | 4660 | ) -> Type throws (ResolveError) { |
|
| 4654 | - | // Handle `@sliceOf` separately since it takes two runtime arguments. |
|
| 4661 | + | // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`. |
|
| 4655 | 4662 | if kind == ast::Builtin::SliceOf { |
|
| 4656 | - | if args.len != 2 { |
|
| 4663 | + | if args.len != 2 and args.len != 3 { |
|
| 4657 | 4664 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 4658 | 4665 | expected: 2, |
|
| 4659 | 4666 | actual: args.len as u32, |
|
| 4660 | 4667 | })); |
|
| 4661 | 4668 | } |
|
| 4662 | - | // Analyze pointer expression. |
|
| 4663 | 4669 | let ptrType = try visit(self, args.list[0], Type::Unknown); |
|
| 4664 | 4670 | let case Type::Pointer { target, mutable } = ptrType else { |
|
| 4665 | 4671 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
|
| 4666 | 4672 | }; |
|
| 4667 | - | // Analyze length expression. |
|
| 4668 | 4673 | let _ = try checkAssignable(self, args.list[1], Type::U32); |
|
| 4669 | - | // Result is a slice of the pointer's element type. |
|
| 4670 | - | let sliceType = Type::Slice { item: target, mutable }; |
|
| 4671 | - | ||
| 4672 | - | return try setNodeType(self, node, sliceType); |
|
| 4674 | + | if args.len == 3 { |
|
| 4675 | + | let _ = try checkAssignable(self, args.list[2], Type::U32); |
|
| 4676 | + | } |
|
| 4677 | + | return try setNodeType(self, node, Type::Slice { item: target, mutable }); |
|
| 4673 | 4678 | } |
|
| 4674 | 4679 | if args.len != 1 { |
|
| 4675 | 4680 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 4676 | 4681 | expected: 1, |
|
| 4677 | 4682 | actual: args.len as u32, |
| 5263 | 5268 | } |
|
| 5264 | 5269 | if mem::eq(fieldName, LEN_FIELD) { |
|
| 5265 | 5270 | try setRecordFieldIndex(self, fieldNode, 1); |
|
| 5266 | 5271 | return try setNodeType(self, node, Type::U32); |
|
| 5267 | 5272 | } |
|
| 5273 | + | if mem::eq(fieldName, CAP_FIELD) { |
|
| 5274 | + | try setRecordFieldIndex(self, fieldNode, 2); |
|
| 5275 | + | return try setNodeType(self, node, Type::U32); |
|
| 5276 | + | } |
|
| 5268 | 5277 | throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName)); |
|
| 5269 | 5278 | } |
|
| 5270 | 5279 | case Type::TraitObject { traitInfo, .. } => { |
|
| 5271 | 5280 | let fieldName = try nodeName(self, access.child); |
|
| 5272 | 5281 | let method = findTraitMethod(traitInfo, fieldName) |
lib/std/lang/resolver/tests.rad
+37 -2
| 4055 | 4055 | try testing::expect(mismatch.actual == 1); |
|
| 4056 | 4056 | } |
|
| 4057 | 4057 | // Too many arguments. |
|
| 4058 | 4058 | { |
|
| 4059 | 4059 | let mut a = testResolver(); |
|
| 4060 | - | let program = "fn f(ptr: *u8, len: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, extra); }"; |
|
| 4060 | + | let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }"; |
|
| 4061 | 4061 | let result = try resolveProgramStr(&mut a, program); |
|
| 4062 | 4062 | let err = try expectError(&result); |
|
| 4063 | 4063 | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
|
| 4064 | 4064 | else throw testing::TestError::Failed; |
|
| 4065 | 4065 | try testing::expect(mismatch.expected == 2); |
|
| 4066 | - | try testing::expect(mismatch.actual == 3); |
|
| 4066 | + | try testing::expect(mismatch.actual == 4); |
|
| 4067 | 4067 | } |
|
| 4068 | 4068 | } |
|
| 4069 | 4069 | ||
| 4070 | 4070 | /// Test @sliceOf with wrong argument types produces errors. |
|
| 4071 | 4071 | @test fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
| 4105 | 4105 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4106 | 4106 | else throw testing::TestError::Failed; |
|
| 4107 | 4107 | } |
|
| 4108 | 4108 | } |
|
| 4109 | 4109 | ||
| 4110 | + | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
|
| 4111 | + | @test fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4112 | + | { |
|
| 4113 | + | let mut a = testResolver(); |
|
| 4114 | + | let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4115 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4116 | + | try expectNoErrors(&result); |
|
| 4117 | + | } |
|
| 4118 | + | // Mutable pointer produces mutable slice. |
|
| 4119 | + | { |
|
| 4120 | + | let mut a = testResolver(); |
|
| 4121 | + | let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4122 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4123 | + | try expectNoErrors(&result); |
|
| 4124 | + | } |
|
| 4125 | + | } |
|
| 4126 | + | ||
| 4127 | + | /// Test @sliceOf with 3 arguments but wrong cap type. |
|
| 4128 | + | @test fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
|
| 4129 | + | let mut a = testResolver(); |
|
| 4130 | + | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4131 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4132 | + | let err = try expectError(&result); |
|
| 4133 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4134 | + | else throw testing::TestError::Failed; |
|
| 4135 | + | } |
|
| 4136 | + | ||
| 4137 | + | /// Test .cap field access on slices resolves to u32. |
|
| 4138 | + | @test fn testResolveSliceCapField() throws (testing::TestError) { |
|
| 4139 | + | let mut a = testResolver(); |
|
| 4140 | + | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
|
| 4141 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4142 | + | try expectNoErrors(&result); |
|
| 4143 | + | } |
|
| 4144 | + | ||
| 4110 | 4145 | /// Test `match &opt` produces immutable pointer bindings. |
|
| 4111 | 4146 | @test fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4112 | 4147 | let mut a = testResolver(); |
|
| 4113 | 4148 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4114 | 4149 | let result = try resolveProgramStr(&mut a, program); |