compiler/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
tests/
printer.rad
24.6 KiB
tests.rad
406.6 KiB
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
20.3 KiB
lower.rad
315.6 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
91.2 KiB
resolver.rad
452.9 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/lang/resolver/tests.rad
raw
| 1 | //! Resolver tests. |
| 2 | |
| 3 | /// Region identity, borrowing, and nominal application tests. |
| 4 | @test mod regions; |
| 5 | |
| 6 | use std::mem; |
| 7 | use std::testing; |
| 8 | use std::lang::alloc; |
| 9 | use std::lang::ast; |
| 10 | use std::lang::types; |
| 11 | use std::lang::parser; |
| 12 | use std::lang::scanner; |
| 13 | use std::lang::module; |
| 14 | use std::lang::strings; |
| 15 | |
| 16 | /// Interning preserves exact identity across populated collision chains. |
| 17 | @test unsafe fn testTypeInterningIdentity() throws (testing::TestError) { |
| 18 | let mut arena = testArena(); |
| 19 | let storage: 'test = &mut arena in { |
| 20 | let mut res = testResolver(storage); |
| 21 | let item = super::allocType(&mut res, super::Type::U32); |
| 22 | let mut entries: [?*super::Type; 2048] = [nil; 2048]; |
| 23 | for i in 0..entries.len { |
| 24 | let ty = super::Type::Array(super::ArrayType { item, length: i }); |
| 25 | set entries[i] = super::allocType(&mut res, ty); |
| 26 | } |
| 27 | for i in 0..entries.len { |
| 28 | let ty = super::Type::Array(super::ArrayType { item, length: i }); |
| 29 | let entry = entries[i] else throw testing::TestError::Failed; |
| 30 | assert super::allocType(&mut res, ty) == entry; |
| 31 | assert *entry == ty; |
| 32 | } |
| 33 | let values = [ |
| 34 | super::Type::U32, super::Type::U64, |
| 35 | super::Type::Optional(item), |
| 36 | super::Type::Range { start: nil, end: nil }, |
| 37 | super::Type::Range { start: item, end: nil }, |
| 38 | super::Type::Range { start: nil, end: item }, |
| 39 | super::Type::Range { start: item, end: item }, |
| 40 | super::Type::Cell { class: types::PointerClass::Ref, payload: item }, |
| 41 | super::Type::Pointer { class: types::PointerClass::Ref, target: item, mutable: false }, |
| 42 | super::Type::Pointer { class: types::PointerClass::Ref, target: item, mutable: true }, |
| 43 | super::Type::Pointer { class: types::PointerClass::Owned, target: item, mutable: false }, |
| 44 | super::Type::Pointer { class: types::PointerClass::Unsafe, target: item, mutable: false }, |
| 45 | super::Type::Slice { class: types::PointerClass::Ref, item, mutable: false }, |
| 46 | super::Type::Slice { class: types::PointerClass::Ref, item, mutable: true }, |
| 47 | ]; |
| 48 | for ty, i in values { |
| 49 | let entry = super::allocType(&mut res, ty); |
| 50 | assert super::allocType(&mut res, ty) == entry; |
| 51 | for other, j in values { |
| 52 | assert (entry == super::allocType(&mut res, other)) == (i == j); |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | /// Reused ownership slots retain the current binding's exact-use diagnostics. |
| 59 | @test unsafe fn testLinearScratchSlotReuse() throws (testing::TestError) { |
| 60 | let mut arena = testArena(); |
| 61 | let storage: 'test = &mut arena in { |
| 62 | let mut res = testResolver(storage); |
| 63 | let result = try resolveProgramStr(&mut res, |
| 64 | "union Token: Once { Value(u32) } fn consume(t: Token) { match t { case Token::Value(_) => {} } } fn f() { { let first = Token::Value(1); consume(first); } let second = Token::Value(2); consume(second); consume(second); }" |
| 65 | ); |
| 66 | try expectErrorKind(&result, super::ErrorKind::LinearUseAfterConsume("second")); |
| 67 | } |
| 68 | let mut branchArena = testArena(); |
| 69 | let branchStorage: 'branch = &mut branchArena in { |
| 70 | let mut res = testResolver(branchStorage); |
| 71 | let result = try resolveProgramStr(&mut res, |
| 72 | "union Token: Once { Value(u32) } fn consume(t: Token) { match t { case Token::Value(_) => {} } } fn f(choice: bool) { { let first = Token::Value(1); consume(first); } let second = Token::Value(2); if choice { consume(second); } }" |
| 73 | ); |
| 74 | try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("second")); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /// Nested loops keep separate break state and restore the stack after errors. |
| 79 | @test unsafe fn testLoopContextState() throws (testing::TestError) { |
| 80 | for program, index in [ |
| 81 | "fn f() { loop { break; } loop { loop { break; } continue; } }", |
| 82 | "fn f() { loop { loop { missing; } } }", |
| 83 | "fn f() { loop { break; } break; }", |
| 84 | "fn f() { loop { break; } continue; }", |
| 85 | "break;", |
| 86 | "continue;", |
| 87 | "loop { break }", |
| 88 | "while true { continue }", |
| 89 | ] { |
| 90 | let mut arena = testArena(); |
| 91 | let storage: 'test = &mut arena in { |
| 92 | let mut res = testResolver(storage); |
| 93 | let result = try resolveProgramStr(&mut res, program); |
| 94 | assert res.loopDepth == 0; |
| 95 | if index == 0 { |
| 96 | try expectNoErrors(&result); |
| 97 | let body = try getFnBody(&res, result.root, "f"); |
| 98 | let first = super::typeFor(&res, body.statements[0]) else throw testing::TestError::Failed; |
| 99 | let second = super::typeFor(&res, body.statements[1]) else throw testing::TestError::Failed; |
| 100 | let case super::Type::Void = first else throw testing::TestError::Failed; |
| 101 | let case super::Type::Never = second else throw testing::TestError::Failed; |
| 102 | } else if index == 1 { |
| 103 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("missing")); |
| 104 | } else if index < 6 { |
| 105 | try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl); |
| 106 | } else { |
| 107 | try expectNoErrors(&result); |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /// Loop bindings and lowering metadata retain the same item type and names. |
| 114 | @test unsafe fn testForLoopMetadata() throws (testing::TestError) { |
| 115 | let mut arena = testArena(); |
| 116 | let storage: 'test = &mut arena in { |
| 117 | let mut res = testResolver(storage); |
| 118 | let result = try resolveProgramStr(&mut res, |
| 119 | "fn f(s: &[u8], start: u64, end: u64) { let xs: [u16; 2] = [1, 2]; for value, idx in xs {} for byte in s {} for _, idx in start..end {} }" |
| 120 | ); |
| 121 | try expectNoErrors(&result); |
| 122 | let body = try getFnBody(&res, result.root, "f"); |
| 123 | for index in [1 as u32, 2, 3] { |
| 124 | let node = body.statements[index]; |
| 125 | let case ast::NodeValue::For(loopNode) = node.value else throw testing::TestError::Failed; |
| 126 | let info = super::forLoopInfoFor(&res, node) else throw testing::TestError::Failed; |
| 127 | let bindingType = super::typeFor(&res, loopNode.binding) else throw testing::TestError::Failed; |
| 128 | match info { |
| 129 | case super::ForLoopInfo::Collection { elemType, length, bindingName, indexName } => { |
| 130 | assert bindingType == *elemType; |
| 131 | let name = bindingName else throw testing::TestError::Failed; |
| 132 | if index == 1 { |
| 133 | assert *elemType == super::Type::U16; |
| 134 | let count = length else throw testing::TestError::Failed; |
| 135 | assert count == 2; |
| 136 | assert mem::eq(name, "value"); |
| 137 | let name = indexName else throw testing::TestError::Failed; |
| 138 | assert mem::eq(name, "idx"); |
| 139 | } else { |
| 140 | assert index == 2; |
| 141 | assert *elemType == super::Type::U8; |
| 142 | assert length == nil; |
| 143 | assert mem::eq(name, "byte"); |
| 144 | assert indexName == nil; |
| 145 | } |
| 146 | } |
| 147 | case super::ForLoopInfo::Range { valType, range, bindingName, indexName } => { |
| 148 | assert index == 3; |
| 149 | assert bindingType == *valType; |
| 150 | assert *valType == super::Type::U64; |
| 151 | assert bindingName == nil; |
| 152 | let name = indexName else throw testing::TestError::Failed; |
| 153 | assert mem::eq(name, "idx"); |
| 154 | let case ast::NodeValue::Range(source) = loopNode.iterable.value else throw testing::TestError::Failed; |
| 155 | assert range == source; |
| 156 | } |
| 157 | } |
| 158 | if let binding = loopNode.index { |
| 159 | let ty = super::typeFor(&res, binding) else throw testing::TestError::Failed; |
| 160 | assert ty == super::Type::U32; |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | /// Invalid iterables and unsafe slice iteration retain their diagnostics. |
| 167 | @test unsafe fn testForLoopMetadataErrors() throws (testing::TestError) { |
| 168 | for program in [ |
| 169 | "fn f() { for x in true {} }", |
| 170 | "fn f(end: u32) { for x in ..end {} }", |
| 171 | "fn f(start: u32, end: u32) { let range = start..end; for x in range {} }", |
| 172 | ] { |
| 173 | let mut arena = testArena(); |
| 174 | let storage: 'test = &mut arena in { |
| 175 | let mut res = testResolver(storage); |
| 176 | let result = try resolveProgramStr(&mut res, program); |
| 177 | try expectErrorKind(&result, super::ErrorKind::ExpectedIterable); |
| 178 | } |
| 179 | } |
| 180 | let mut arena = testArena(); |
| 181 | let storage: 'test = &mut arena in { |
| 182 | let mut res = testResolver(storage); |
| 183 | let result = try resolveProgramStr(&mut res, "fn f(s: *unsafe [u32]) { for x in s {} }"); |
| 184 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 185 | } |
| 186 | try expectAnalyzeOk("unsafe fn f(s: *unsafe [u32]) { for x in s {} }"); |
| 187 | } |
| 188 | |
| 189 | /// Slice operations retain initialized element, capacity, and access metadata. |
| 190 | @test unsafe fn testSliceRangeMetadata() throws (testing::TestError) { |
| 191 | let mut arena = testArena(); |
| 192 | let storage: 'test = &mut arena in { |
| 193 | let mut res = testResolver(storage); |
| 194 | let result = try resolveProgramStr(&mut res, |
| 195 | "fn f() { let mut xs: [u32; 4] = [1, 2, 3, 4]; set xs[1..3] = 7; let view = &mut xs[1..3]; set view[..] = 3; let shared = &view[..]; }" |
| 196 | ); |
| 197 | try expectNoErrors(&result); |
| 198 | let body = try getFnBody(&res, result.root, "f"); |
| 199 | for index in [1 as u32, 3] { |
| 200 | let stmt = body.statements[index]; |
| 201 | let case ast::NodeValue::Assign(_) = stmt.value else throw testing::TestError::Failed; |
| 202 | let info = super::sliceRangeInfoFor(&res, stmt) else throw testing::TestError::Failed; |
| 203 | assert *info.itemType == super::Type::U32; |
| 204 | assert info.mutable; |
| 205 | let capacity: ?u32 = 4 if index == 1 else nil; |
| 206 | assert info.capacity == capacity; |
| 207 | } |
| 208 | for index in [2 as u32, 4] { |
| 209 | let stmt = body.statements[index]; |
| 210 | let case ast::NodeValue::Let(binding) = stmt.value else throw testing::TestError::Failed; |
| 211 | let case ast::NodeValue::AddressOf(_) = binding.value.value else throw testing::TestError::Failed; |
| 212 | let info = super::sliceRangeInfoFor(&res, binding.value) else throw testing::TestError::Failed; |
| 213 | assert *info.itemType == super::Type::U32; |
| 214 | assert info.mutable == (index == 2); |
| 215 | let capacity: ?u32 = 4 if index == 2 else nil; |
| 216 | assert info.capacity == capacity; |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | /// Slice assignment and borrowing enforce static bounds and source access. |
| 222 | @test unsafe fn testSliceRangeErrors() throws (testing::TestError) { |
| 223 | for program in [ |
| 224 | "fn f() { let mut xs: [u32; 2] = [1, 2]; set xs[..3] = 0; }", |
| 225 | "fn f() { let mut xs: [u32; 2] = [1, 2]; set xs[2..1] = 0; }", |
| 226 | "fn f() { let mut xs: [u32; 2] = [1, 2]; let view = &mut xs[..3]; }", |
| 227 | ] { |
| 228 | let mut arena = testArena(); |
| 229 | let storage: 'test = &mut arena in { |
| 230 | let mut res = testResolver(storage); |
| 231 | let result = try resolveProgramStr(&mut res, program); |
| 232 | let err = try expectError(&result); |
| 233 | assert err.kind == super::ErrorKind::SliceRangeOutOfBounds, program; |
| 234 | } |
| 235 | } |
| 236 | for program in [ |
| 237 | "fn f() { let mut value: u32 = 1; set value[..] = 0; }", |
| 238 | "fn f() { let mut value: u32 = 1; let view = &value[..]; }", |
| 239 | ] { |
| 240 | let mut arena = testArena(); |
| 241 | let storage: 'test = &mut arena in { |
| 242 | let mut res = testResolver(storage); |
| 243 | let result = try resolveProgramStr(&mut res, program); |
| 244 | let err = try expectError(&result); |
| 245 | assert err.kind == super::ErrorKind::ExpectedIndexable, program; |
| 246 | } |
| 247 | } |
| 248 | for program in [ |
| 249 | "fn f() { let xs: [u32; 2] = [1, 2]; let view = &xs[..]; set view[..] = 0; }", |
| 250 | "fn f() { let xs: [u32; 2] = [1, 2]; let view = &xs[..]; let other = &mut view[..]; }", |
| 251 | ] { |
| 252 | let mut arena = testArena(); |
| 253 | let storage: 'test = &mut arena in { |
| 254 | let mut res = testResolver(storage); |
| 255 | let result = try resolveProgramStr(&mut res, program); |
| 256 | let err = try expectError(&result); |
| 257 | assert err.kind == super::ErrorKind::ImmutableBinding, program; |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | /// Fixed and result layouts cover primitive and pointer-like representations. |
| 263 | @test unsafe fn testFixedTypeLayouts() throws (testing::TestError) { |
| 264 | for ty in [super::Type::Void, super::Type::Never] { |
| 265 | checkFixedTypeLayout(ty, 0, 0); |
| 266 | } |
| 267 | for ty in [super::Type::Bool, super::Type::U8, super::Type::I8] { |
| 268 | checkFixedTypeLayout(ty, 1, 1); |
| 269 | } |
| 270 | for ty in [super::Type::U16, super::Type::I16] { checkFixedTypeLayout(ty, 2, 2); } |
| 271 | for ty in [super::Type::U32, super::Type::I32] { checkFixedTypeLayout(ty, 4, 4); } |
| 272 | for ty in [super::Type::Int, super::Type::U64, super::Type::I64] { checkFixedTypeLayout(ty, 8, 8); } |
| 273 | let mut arena = testArena(); |
| 274 | let itemStorage = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
| 275 | set *itemStorage = super::Type::U32; |
| 276 | let item: *super::Type = itemStorage; |
| 277 | let region = types::Region { id: 1, origin: types::RegionOrigin::Block, name: "r", parent: nil }; |
| 278 | let traitInfo = super::TraitType { name: "T", moduleId: 0, methods: &mut [], supertraits: &mut [] }; |
| 279 | let signature = try! alloc::alloc(&mut arena, @sizeOf(super::FnType), @alignOf(super::FnType)) as *mut super::FnType; |
| 280 | set *signature = super::FnType { |
| 281 | regions: nil, paramTypes: &[], returnType: item, throwList: &[], isUnsafe: false, |
| 282 | }; |
| 283 | checkFixedTypeLayout(super::Type::Fn(signature), 8, 8); |
| 284 | checkFixedTypeLayout(super::Type::Session(®ion), 16, 8); |
| 285 | for class in [types::PointerClass::Owned, types::PointerClass::Ref, |
| 286 | types::PointerClass::Unsafe, types::PointerClass::Region(®ion)] { |
| 287 | checkFixedTypeLayout(super::Type::Cell { class, payload: item }, 8, 8); |
| 288 | for mutable in [false, true] { |
| 289 | checkFixedTypeLayout(super::Type::Pointer { class, target: item, mutable }, 8, 8); |
| 290 | checkFixedTypeLayout(super::Type::Slice { class, item, mutable }, 16, 8); |
| 291 | checkFixedTypeLayout(super::Type::TraitObject { class, traitInfo: &traitInfo, mutable }, 16, 8); |
| 292 | } |
| 293 | } |
| 294 | /// Result layouts retain the tag and the largest success or error payload. |
| 295 | let errors = try! alloc::allocSlice(&mut arena, @sizeOf(*super::Type), @alignOf(*super::Type), 3) |
| 296 | as *mut [*super::Type]; |
| 297 | for ty, i in [super::Type::Void, super::Type::U8, super::Type::U64] { |
| 298 | let slot = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
| 299 | set *slot = ty; |
| 300 | set errors[i] = slot; |
| 301 | } |
| 302 | let throwList: *[*super::Type] = errors; |
| 303 | let payloads = [super::Type::Void, super::Type::U8, |
| 304 | super::Type::Array(super::ArrayType { item: throwList[2], length: 3 })]; |
| 305 | let emptySizes: [u32; 3] = [8, 9, 32]; |
| 306 | let errorSizes: [u32; 3] = [16, 16, 32]; |
| 307 | for payload, i in payloads { |
| 308 | let empty = super::getResultLayout(payload, &[]); |
| 309 | assert empty.size == emptySizes[i] and empty.alignment == 8; |
| 310 | let result = super::getResultLayout(payload, throwList); |
| 311 | assert result.size == errorSizes[i] and result.alignment == 8; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | /// Check a fixed layout through direct, array, and optional type traversal. |
| 316 | unsafe fn checkFixedTypeLayout(ty: super::Type, size: u32, alignment: u32) { |
| 317 | let layout = super::getTypeLayout(ty); |
| 318 | assert layout.size == size and layout.alignment == alignment; |
| 319 | let mut arena = alloc::new(&mut FIXED_LAYOUT_STORAGE[..]); |
| 320 | let itemStorage = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
| 321 | set *itemStorage = ty; |
| 322 | let item: *super::Type = itemStorage; |
| 323 | for length in [0 as u32, 1, 3] { |
| 324 | let array = super::getTypeLayout(super::Type::Array(super::ArrayType { item, length })); |
| 325 | assert array.size == size * length and array.alignment == alignment; |
| 326 | } |
| 327 | let optional = super::getTypeLayout(super::Type::Optional(item)); |
| 328 | let expected = layout if super::isNullableType(ty) else super::getOptionalAggregateLayout(layout); |
| 329 | assert optional.size == expected.size and optional.alignment == expected.alignment; |
| 330 | let optionalStorage = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
| 331 | set *optionalStorage = super::Type::Optional(item); |
| 332 | let optionalItem: *super::Type = optionalStorage; |
| 333 | for length in [0 as u32, 1, 3] { |
| 334 | let array = super::getTypeLayout(super::Type::Array(super::ArrayType { item: optionalItem, length })); |
| 335 | assert array.size == optional.size * length and array.alignment == optional.alignment; |
| 336 | } |
| 337 | let nested = super::getTypeLayout(super::Type::Optional(optionalItem)); |
| 338 | let nestedExpected = super::getOptionalAggregateLayout(optional); |
| 339 | assert nested.size == nestedExpected.size and nested.alignment == nestedExpected.alignment; |
| 340 | } |
| 341 | |
| 342 | /// Storage for owned types used by array and optional layout fixtures. |
| 343 | static FIXED_LAYOUT_STORAGE: [u8; 256] = [0; 256]; |
| 344 | |
| 345 | /// Array layouts retain element alignment for empty and populated arrays. |
| 346 | @test fn testArrayLayoutFromElementLayout() throws (testing::TestError) { |
| 347 | for alignment in [1 as u32, 2, 4, 8, 16] { |
| 348 | let item = super::Layout { size: alignment, alignment }; |
| 349 | for count in [0 as u32, 1, 3] { |
| 350 | let result = super::getArrayLayout(item, count); |
| 351 | assert result.size == alignment * count; |
| 352 | assert result.alignment == alignment; |
| 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | /// Synthetic file path used for resolver tests. |
| 358 | constant MODULE_PATH: *[u8] = "/dev/test.rad"; |
| 359 | |
| 360 | /// Optional aggregate layouts align the tag and payload for safe callers. |
| 361 | @test fn testOptionalAggregateLayouts() throws (testing::TestError) { |
| 362 | for alignment in [1 as u32, 2, 4, 8, 16] { |
| 363 | for length in [0 as u32, 1, 3] { |
| 364 | let payload = super::Layout { size: length * alignment, alignment }; |
| 365 | let layout = super::getOptionalAggregateLayout(payload); |
| 366 | assert layout.size == (length + 1) * alignment; |
| 367 | assert layout.alignment == alignment; |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | /// AST arena storage used by resolver tests. |
| 373 | static AST_ARENA: [u8; 2097152] = [0; 2097152]; |
| 374 | |
| 375 | /// Resolver arena storage used by resolver tests. |
| 376 | static ARENA_STORAGE: [u8; 2097152] = [0; 2097152]; |
| 377 | |
| 378 | /// Node metadata storage used by resolver tests. |
| 379 | unsafe static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined; |
| 380 | |
| 381 | /// Diagnostic storage used by resolver tests. |
| 382 | unsafe static ERROR_STORAGE: [super::Error; 16] = undefined; |
| 383 | |
| 384 | /// Package scope used by resolver tests. |
| 385 | unsafe static PKG_SCOPE: super::Scope = undefined; |
| 386 | |
| 387 | /// Module entries used by resolver tests. |
| 388 | unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; 8] = [nil; 8]; |
| 389 | |
| 390 | /// Module graph used by resolver tests. |
| 391 | unsafe static MODULE_GRAPH: module::ModuleGraph = undefined; |
| 392 | |
| 393 | /// Module AST arena storage used by resolver tests. |
| 394 | static MODULE_ARENA_STORAGE: [u8; 16384] = [0; 16384]; |
| 395 | |
| 396 | /// Module AST arena used by resolver tests. |
| 397 | unsafe static MODULE_ARENA: ast::NodeArena = undefined; |
| 398 | |
| 399 | /// Interned string pool used by resolver tests. |
| 400 | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
| 401 | |
| 402 | /// String literals used in tests. |
| 403 | constant LITERALS: [*[u8]; 15] = [ |
| 404 | "Ok", "Error", "R", "S", |
| 405 | "f", "Status", "Pending", |
| 406 | "Some", "None", "First", |
| 407 | "Second", "Opt", "x", |
| 408 | "value", "idx" |
| 409 | ]; |
| 410 | |
| 411 | /// Resolver result with AST, used by test helpers. |
| 412 | export record TestResult: Copy { |
| 413 | /// Diagnostics from semantic analysis. |
| 414 | diagnostics: super::Diagnostics, |
| 415 | /// Root of the parsed test program. |
| 416 | root: *ast::Node, |
| 417 | } |
| 418 | |
| 419 | /// Create isolated storage for tests to avoid conflicts with global resolver storage. |
| 420 | unsafe fn testStorage() -> super::ResolverStorage { |
| 421 | return super::ResolverStorage { |
| 422 | nodeData: &mut NODE_DATA_STORAGE[..], |
| 423 | pkgScope: &mut PKG_SCOPE, |
| 424 | errors: &mut ERROR_STORAGE[..], |
| 425 | }; |
| 426 | } |
| 427 | |
| 428 | /// Create an arena owner for one resolver test scope. |
| 429 | export unsafe fn testArena() -> alloc::Arena { |
| 430 | return alloc::new(&mut ARENA_STORAGE[..]); |
| 431 | } |
| 432 | |
| 433 | /// Construct a resolver backed by test storage and a synthetic module graph. |
| 434 | export unsafe fn testResolver 'arena (arena: &'arena mut alloc::Arena) -> super::Resolver 'arena { |
| 435 | // TODO: This should be initialized only once. |
| 436 | for i in 0..LITERALS.len { |
| 437 | strings::intern(&mut STRING_POOL, LITERALS[i]); |
| 438 | } |
| 439 | // TODO: Use local static for this. |
| 440 | // Reset the module graph for each test. |
| 441 | set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]); |
| 442 | set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut MODULE_ARENA); |
| 443 | let config = super::Config { buildTest: true }; |
| 444 | let res = super::resolver(arena, testStorage(), config); |
| 445 | |
| 446 | return res; |
| 447 | } |
| 448 | |
| 449 | /// Resolve a block of statements by wrapping them in a synthetic function. |
| 450 | unsafe fn resolveStatements 'arena ( |
| 451 | self: &mut super::Resolver 'arena, block: ast::Block, arena: &mut ast::NodeArena |
| 452 | ) -> TestResult throws (super::ResolveError) { |
| 453 | let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements); |
| 454 | let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch { |
| 455 | return TestResult { diagnostics: super::diagnostics(self), root: module.modBody }; |
| 456 | }; |
| 457 | return TestResult { diagnostics, root: module.fnBody }; |
| 458 | } |
| 459 | |
| 460 | /// Parse and analyze an expression string for testing. |
| 461 | unsafe fn resolveExprStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
| 462 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 463 | let poolRef: 'pool = &mut STRING_POOL, arenaRef = &mut arena in { |
| 464 | let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, arenaRef, poolRef); |
| 465 | parser::advance(&mut p); |
| 466 | |
| 467 | let expr = try parser::parseExpr(&mut p) catch { |
| 468 | panic "resolveExprStr: parsing failed"; |
| 469 | }; |
| 470 | let diagnostics = try super::resolveExpr(self, expr, p.arena) catch { |
| 471 | throw testing::TestError::Failed; |
| 472 | }; |
| 473 | return TestResult { diagnostics, root: expr }; |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | /// Parse and analyze a module string for testing. |
| 478 | /// Use this for code with `fn`, `record`, `union`, etc. at the top level. |
| 479 | export unsafe fn resolveProgramStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
| 480 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 481 | let stmt: *ast::Node = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
| 482 | panic "resolveProgramStr: parsing failed"; |
| 483 | }; |
| 484 | let diagnostics = try super::resolveModuleRoot(self, stmt) catch { |
| 485 | throw testing::TestError::Failed; |
| 486 | }; |
| 487 | return TestResult { diagnostics, root: stmt }; |
| 488 | } |
| 489 | |
| 490 | /// Resolve session diagnostics against the standard allocator declaration contract. |
| 491 | export unsafe fn resolveSessionProgramStr 'arena (self: &mut super::Resolver 'arena, program: *[u8]) -> TestResult |
| 492 | throws (testing::TestError) |
| 493 | { |
| 494 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 495 | let root = try registerModule(&mut MODULE_GRAPH, nil, "std", "export mod lang; mod app;", &mut arena); |
| 496 | let lang = try registerModule(&mut MODULE_GRAPH, root, "lang", "export mod alloc;", &mut arena); |
| 497 | let allocator = try registerModule(&mut MODULE_GRAPH, lang, "alloc", |
| 498 | "export record Arena { data: *mut [u8], offset: u32 } export fn reset(arena: &mut Arena) { set arena.offset = 0; } export union AllocError: Copy { OutOfMemory } export trait Alloc { unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); } instance Alloc for Arena { unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { panic; } unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { panic; } }", |
| 499 | &mut arena); |
| 500 | let app = try registerModule(&mut MODULE_GRAPH, root, "app", program, &mut arena); |
| 501 | return try resolveModuleTree(self, root); |
| 502 | } |
| 503 | |
| 504 | /// Parse and analyze a block of statements (eg. inside a function body) for testing. |
| 505 | /// Use this for code with `let` bindings and expressions, not module-level declarations. |
| 506 | unsafe fn resolveBlockStr 'arena (self: &mut super::Resolver 'arena, stmt: *[u8]) -> TestResult throws (testing::TestError) { |
| 507 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 508 | let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch { |
| 509 | panic "resolveBlockStr: parsing failed"; |
| 510 | }; |
| 511 | let case ast::NodeValue::Block(block) = parsed.value |
| 512 | else panic "resolveBlockStr: expected block root"; |
| 513 | |
| 514 | let analysis = try resolveStatements(self, block, &mut arena) catch { |
| 515 | throw testing::TestError::Failed; |
| 516 | }; |
| 517 | return TestResult { |
| 518 | diagnostics: analysis.diagnostics, |
| 519 | root: analysis.root, |
| 520 | }; |
| 521 | } |
| 522 | |
| 523 | /// Resolve a module with the full resolution process. |
| 524 | unsafe fn resolveModuleTree 'arena ( |
| 525 | res: &mut super::Resolver 'arena, |
| 526 | rootId: u16 |
| 527 | ) -> TestResult throws (testing::TestError) { |
| 528 | let root = module::get(&MODULE_GRAPH, rootId) |
| 529 | else throw testing::TestError::Failed; |
| 530 | let rootAst = module::astFor(root) |
| 531 | else throw testing::TestError::Failed; |
| 532 | let packages = [super::Pkg { |
| 533 | rootEntry: root, |
| 534 | rootAst, |
| 535 | }]; |
| 536 | let diagnostics = try super::resolve(res, &MODULE_GRAPH, &packages[..]) catch { |
| 537 | throw testing::TestError::Failed; |
| 538 | }; |
| 539 | return TestResult { diagnostics, root: rootAst }; |
| 540 | } |
| 541 | |
| 542 | /// Register a module in the graph and attach a parsed AST to it. |
| 543 | /// If parentId is nil, registers as a root module. |
| 544 | unsafe fn registerModule( |
| 545 | graph: &mut module::ModuleGraph, |
| 546 | parentId: ?u16, |
| 547 | name: *[u8], |
| 548 | code: *[u8], |
| 549 | arena: &mut ast::NodeArena |
| 550 | ) -> u16 throws (testing::TestError) { |
| 551 | let filePath = "<test>"; |
| 552 | let mut modId: u16 = undefined; |
| 553 | if let parent = parentId { |
| 554 | set modId = try module::registerChild(graph, &mut STRING_POOL, parent, name, filePath) catch { |
| 555 | throw testing::TestError::Failed; |
| 556 | }; |
| 557 | } else { |
| 558 | set modId = try module::registerRootWithName(graph, &mut STRING_POOL, 0, name, filePath) catch { |
| 559 | throw testing::TestError::Failed; |
| 560 | }; |
| 561 | } |
| 562 | let root = try parser::parse(scanner::SourceLoc::String, code, arena, &mut STRING_POOL) catch { |
| 563 | panic "registerModule: parsing failed"; |
| 564 | }; |
| 565 | try module::setAst(graph, modId, root) catch { |
| 566 | panic "registerModule: module not found"; |
| 567 | }; |
| 568 | return modId; |
| 569 | } |
| 570 | |
| 571 | /// Ensure an expression statement produces the expected type and return the expression node. |
| 572 | fn expectExprStmtType 'arena (self: &super::Resolver 'arena, node: *ast::Node, expected: super::Type) -> *ast::Node |
| 573 | throws (testing::TestError) |
| 574 | { |
| 575 | let case ast::NodeValue::ExprStmt(expr) = node.value |
| 576 | else throw testing::TestError::Failed; |
| 577 | try expectType(self, expr, expected); |
| 578 | |
| 579 | return expr; |
| 580 | } |
| 581 | |
| 582 | /// Assert that the test result contains no diagnostic errors. |
| 583 | export fn expectNoErrors(r: &TestResult) throws (testing::TestError) { |
| 584 | try testing::expect(super::success(&r.diagnostics)); |
| 585 | } |
| 586 | |
| 587 | /// Extract the first error from a test result, failing if none exists. |
| 588 | export fn expectError(result: &TestResult) -> super::Error throws (testing::TestError) { |
| 589 | let err = super::errorAt(&result.diagnostics.errors[..], 0) |
| 590 | else throw testing::TestError::Failed; |
| 591 | return err; |
| 592 | } |
| 593 | |
| 594 | /// Check if two error kinds match. |
| 595 | fn errorKindMatches(actual: &super::ErrorKind, expected: super::ErrorKind) -> bool { |
| 596 | if let case super::ErrorKind::DuplicateBinding(expectedName) = expected { |
| 597 | if let case super::ErrorKind::DuplicateBinding(actualName) = *actual { |
| 598 | return mem::eq(actualName, expectedName); |
| 599 | } |
| 600 | return false; |
| 601 | } |
| 602 | if let case super::ErrorKind::UnresolvedSymbol(expectedName) = expected { |
| 603 | if let case super::ErrorKind::UnresolvedSymbol(actualName) = *actual { |
| 604 | return mem::eq(actualName, expectedName); |
| 605 | } |
| 606 | return false; |
| 607 | } |
| 608 | if let case super::ErrorKind::RecordFieldMissing(expectedName) = expected { |
| 609 | if let case super::ErrorKind::RecordFieldMissing(actualName) = *actual { |
| 610 | return mem::eq(actualName, expectedName); |
| 611 | } |
| 612 | return false; |
| 613 | } |
| 614 | if let case super::ErrorKind::RecordFieldUnknown(expectedName) = expected { |
| 615 | if let case super::ErrorKind::RecordFieldUnknown(actualName) = *actual { |
| 616 | return mem::eq(actualName, expectedName); |
| 617 | } |
| 618 | return false; |
| 619 | } |
| 620 | if let case super::ErrorKind::ArrayFieldUnknown(expectedName) = expected { |
| 621 | if let case super::ErrorKind::ArrayFieldUnknown(actualName) = *actual { |
| 622 | return mem::eq(actualName, expectedName); |
| 623 | } |
| 624 | return false; |
| 625 | } |
| 626 | if let case super::ErrorKind::SliceFieldUnknown(expectedName) = expected { |
| 627 | if let case super::ErrorKind::SliceFieldUnknown(actualName) = *actual { |
| 628 | return mem::eq(actualName, expectedName); |
| 629 | } |
| 630 | return false; |
| 631 | } |
| 632 | if let case super::ErrorKind::UnionVariantPayloadMissing(expectedName) = expected { |
| 633 | if let case super::ErrorKind::UnionVariantPayloadMissing(actualName) = *actual { |
| 634 | return mem::eq(actualName, expectedName); |
| 635 | } |
| 636 | return false; |
| 637 | } |
| 638 | if let case super::ErrorKind::UnionVariantPayloadUnexpected(expectedName) = expected { |
| 639 | if let case super::ErrorKind::UnionVariantPayloadUnexpected(actualName) = *actual { |
| 640 | return mem::eq(actualName, expectedName); |
| 641 | } |
| 642 | return false; |
| 643 | } |
| 644 | if let case super::ErrorKind::UnionMatchNonExhaustive(expectedName) = expected { |
| 645 | if let case super::ErrorKind::UnionMatchNonExhaustive(actualName) = *actual { |
| 646 | return mem::eq(actualName, expectedName); |
| 647 | } |
| 648 | return false; |
| 649 | } |
| 650 | if let case super::ErrorKind::MissingTraitMethod(expectedName) = expected { |
| 651 | if let case super::ErrorKind::MissingTraitMethod(actualName) = *actual { |
| 652 | return mem::eq(actualName, expectedName); |
| 653 | } |
| 654 | return false; |
| 655 | } |
| 656 | if let case super::ErrorKind::MissingSupertraitInstance(expectedName) = expected { |
| 657 | if let case super::ErrorKind::MissingSupertraitInstance(actualName) = *actual { |
| 658 | return mem::eq(actualName, expectedName); |
| 659 | } |
| 660 | return false; |
| 661 | } |
| 662 | if let case super::ErrorKind::AffineUseAfterMove(expectedName) = expected { |
| 663 | if let case super::ErrorKind::AffineUseAfterMove(actualName) = *actual { |
| 664 | return mem::eq(actualName, expectedName); |
| 665 | } |
| 666 | return false; |
| 667 | } |
| 668 | if let case super::ErrorKind::LinearUseAfterConsume(expectedName) = expected { |
| 669 | if let case super::ErrorKind::LinearUseAfterConsume(actualName) = *actual { |
| 670 | return mem::eq(actualName, expectedName); |
| 671 | } |
| 672 | return false; |
| 673 | } |
| 674 | if let case super::ErrorKind::LinearNotConsumed(expectedName) = expected { |
| 675 | if let case super::ErrorKind::LinearNotConsumed(actualName) = *actual { |
| 676 | return mem::eq(actualName, expectedName); |
| 677 | } |
| 678 | return false; |
| 679 | } |
| 680 | if let case super::ErrorKind::LinearBranchMismatch(expectedName) = expected { |
| 681 | if let case super::ErrorKind::LinearBranchMismatch(actualName) = *actual { |
| 682 | return mem::eq(actualName, expectedName); |
| 683 | } |
| 684 | return false; |
| 685 | } |
| 686 | if let case super::ErrorKind::BorrowConflict(expectedName) = expected { |
| 687 | if let case super::ErrorKind::BorrowConflict(actualName) = *actual { |
| 688 | return mem::eq(actualName, expectedName); |
| 689 | } |
| 690 | return false; |
| 691 | } |
| 692 | return *actual == expected; |
| 693 | } |
| 694 | |
| 695 | /// Extract the first error and ensure it has the expected kind. |
| 696 | export fn expectErrorKind(result: &TestResult, kind: super::ErrorKind) -> super::Error |
| 697 | throws (testing::TestError) |
| 698 | { |
| 699 | let err = try expectError(result); |
| 700 | try testing::expect(errorKindMatches(&err.kind, kind)); |
| 701 | return err; |
| 702 | } |
| 703 | |
| 704 | /// Ensure an expression resolves to the expected type annotation. |
| 705 | fn expectType 'arena (self: &super::Resolver 'arena, expr: *ast::Node, expected: super::Type) |
| 706 | throws (testing::TestError) |
| 707 | { |
| 708 | let actual = super::typeFor(self, expr) |
| 709 | else throw testing::TestError::Failed; |
| 710 | |
| 711 | if actual <> expected { |
| 712 | throw testing::TestError::Failed; |
| 713 | } |
| 714 | } |
| 715 | |
| 716 | /// Verify that an error represents a specific type mismatch. |
| 717 | fn expectTypeMismatch(err: super::Error, expected: super::Type, actual: super::Type) |
| 718 | throws (testing::TestError) |
| 719 | { |
| 720 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
| 721 | else throw testing::TestError::Failed; |
| 722 | try testing::expect(mismatch.expected == expected); |
| 723 | try testing::expect(mismatch.actual == actual); |
| 724 | } |
| 725 | |
| 726 | /// Resolve a program and require successful analysis. |
| 727 | unsafe fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) { |
| 728 | let mut testArena1 = testArena(); |
| 729 | let testStorage1: 'test1 = &mut testArena1 in { |
| 730 | let mut a = testResolver(testStorage1); |
| 731 | let result = try resolveProgramStr(&mut a, program); |
| 732 | try expectNoErrors(&result); |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | /// Require an inferred integer type mismatch. |
| 737 | unsafe fn expectIntMismatch(program: *[u8], expected: super::Type) |
| 738 | throws (testing::TestError) |
| 739 | { |
| 740 | let mut testArena2 = testArena(); |
| 741 | let testStorage2: 'test2 = &mut testArena2 in { |
| 742 | let mut a = testResolver(testStorage2); |
| 743 | let result = try resolveProgramStr(&mut a, program); |
| 744 | let err = try expectError(&result); |
| 745 | try expectTypeMismatch(err, expected, super::Type::Int); |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | /// Retrieve the nth statement from a block node. |
| 750 | export fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node |
| 751 | throws (testing::TestError) |
| 752 | { |
| 753 | let case ast::NodeValue::Block(body) = block.value |
| 754 | else throw testing::TestError::Failed; |
| 755 | |
| 756 | if index >= body.statements.len { |
| 757 | throw testing::TestError::Failed; |
| 758 | } |
| 759 | return body.statements[index]; |
| 760 | } |
| 761 | |
| 762 | /// Retrieve a function body block by function name from the program scope. |
| 763 | unsafe fn getFnBody 'arena (a: &super::Resolver 'arena, root: *ast::Node, name: *[u8]) -> ast::Block |
| 764 | throws (testing::TestError) |
| 765 | { |
| 766 | let scope = super::scopeFor(a, root) |
| 767 | else throw testing::TestError::Failed; |
| 768 | let sym = super::findSymbolInScope(scope, name) |
| 769 | else throw testing::TestError::Failed; |
| 770 | // Verify it's a value symbol by pattern matching. |
| 771 | let case super::SymbolData::Value { .. } = sym.data |
| 772 | else throw testing::TestError::Failed; |
| 773 | |
| 774 | let case ast::NodeValue::FnDecl(fnDecl) = sym.node.value |
| 775 | else throw testing::TestError::Failed; |
| 776 | |
| 777 | let body = fnDecl.body |
| 778 | else throw testing::TestError::Failed; |
| 779 | let case ast::NodeValue::Block(blk) = body.value |
| 780 | else throw testing::TestError::Failed; |
| 781 | |
| 782 | return blk; |
| 783 | } |
| 784 | |
| 785 | /// Get the payload type of a union variant, if it has one. |
| 786 | /// For single-field unlabeled variants like `Variant(i32)`, unwraps to return the inner type. |
| 787 | unsafe fn getUnionVariantPayload(nominalTy: *unsafe super::NominalType, variantName: *[u8]) -> super::Type { |
| 788 | let case super::NominalType::Union(unionType) = *nominalTy |
| 789 | else panic "getUnionVariantPayload: not a union"; |
| 790 | for i in 0..unionType.variants.len { |
| 791 | if mem::eq(unionType.variants[i].name, variantName) { |
| 792 | let payloadType = unionType.variants[i].valueType; |
| 793 | // Unwrap single-field unlabeled records to get the inner type. |
| 794 | if let case super::Type::Nominal(super::NominalType::Record(recInfo)) = payloadType { |
| 795 | if not recInfo.labeled and recInfo.fields.len == 1 { |
| 796 | return recInfo.fields[0].fieldType; |
| 797 | } |
| 798 | } |
| 799 | return payloadType; |
| 800 | } |
| 801 | } |
| 802 | panic "getUnionVariantPayload: variant not found"; |
| 803 | } |
| 804 | |
| 805 | /// Get a nominal type by name, in the scope of the given block node. |
| 806 | unsafe fn getTypeInScopeOf 'arena (a: &super::Resolver 'arena, blk: *ast::Node, name: *[u8]) -> *unsafe super::NominalType |
| 807 | throws (testing::TestError) |
| 808 | { |
| 809 | let scope = super::scopeFor(a, blk) |
| 810 | else throw testing::TestError::Failed; |
| 811 | let sym = super::findSymbolInScope(scope, name) |
| 812 | else throw testing::TestError::Failed; |
| 813 | let case super::SymbolData::Type(ty) = sym.data |
| 814 | else throw testing::TestError::Failed; |
| 815 | return ty; |
| 816 | } |
| 817 | |
| 818 | /// Return the resolved type of a syntax node. |
| 819 | fn typeOf 'arena (a: &super::Resolver 'arena, node: *ast::Node) -> super::Type |
| 820 | throws (testing::TestError) |
| 821 | { |
| 822 | let ty = super::typeFor(a, node) |
| 823 | else throw testing::TestError::Failed; |
| 824 | return ty; |
| 825 | } |
| 826 | |
| 827 | /// Require an array type and return its element type. |
| 828 | fn expectArrayType(ty: super::Type, length: u32) -> super::Type |
| 829 | throws (testing::TestError) |
| 830 | { |
| 831 | let case super::Type::Array(info) = ty |
| 832 | else throw testing::TestError::Failed; |
| 833 | try testing::expect(info.length == length); |
| 834 | |
| 835 | return *info.item; |
| 836 | } |
| 837 | |
| 838 | /// Require a slice type and return its element type. |
| 839 | fn expectSliceType(ty: super::Type, mutable: bool) -> super::Type |
| 840 | throws (testing::TestError) |
| 841 | { |
| 842 | let case super::Type::Slice { item, mutable: sliceMut, .. } = ty |
| 843 | else throw testing::TestError::Failed; |
| 844 | try testing::expect(sliceMut == mutable); |
| 845 | |
| 846 | return *item; |
| 847 | } |
| 848 | |
| 849 | /// Require a pointer type and return its target type. |
| 850 | fn expectPointerType(ty: super::Type, mutable: bool) -> super::Type |
| 851 | throws (testing::TestError) |
| 852 | { |
| 853 | let case super::Type::Pointer { target, mutable: ptrMut, .. } = ty |
| 854 | else throw testing::TestError::Failed; |
| 855 | try testing::expect(ptrMut == mutable); |
| 856 | |
| 857 | return *target; |
| 858 | } |
| 859 | |
| 860 | /// Verify that a node has a constant integer value with the expected magnitude. |
| 861 | fn expectConstInt 'arena (a: &super::Resolver 'arena, node: *ast::Node, expected: u32) |
| 862 | throws (testing::TestError) |
| 863 | { |
| 864 | let constVal = super::constValueEntry(a, node) |
| 865 | else throw testing::TestError::Failed; |
| 866 | |
| 867 | let case super::ConstValue::Int(int) = constVal |
| 868 | else throw testing::TestError::Failed; |
| 869 | |
| 870 | try testing::expect(int.magnitude == expected); |
| 871 | } |
| 872 | |
| 873 | /// Resolve an expression that should evaluate to a constant, and verify it equals the expected value. |
| 874 | unsafe fn resolveAndExpectConstExpr(expr: *[u8], expected: u32) |
| 875 | throws (testing::TestError) |
| 876 | { |
| 877 | let mut testArena3 = testArena(); |
| 878 | let testStorage3: 'test3 = &mut testArena3 in { |
| 879 | let mut a = testResolver(testStorage3); |
| 880 | let result = try resolveExprStr(&mut a, expr); |
| 881 | try expectNoErrors(&result); |
| 882 | try expectType(&a, result.root, super::Type::U32); |
| 883 | try expectConstInt(&a, result.root, expected); |
| 884 | } |
| 885 | } |
| 886 | |
| 887 | /// Resolve a statement that should evaluate to a constant, and verify it equals the expected value. |
| 888 | unsafe fn resolveAndExpectConstStmt(expr: *[u8], expected: u32) |
| 889 | throws (testing::TestError) |
| 890 | { |
| 891 | let mut testArena4 = testArena(); |
| 892 | let testStorage4: 'test4 = &mut testArena4 in { |
| 893 | let mut a = testResolver(testStorage4); |
| 894 | let result = try resolveProgramStr(&mut a, expr); |
| 895 | try expectNoErrors(&result); |
| 896 | let stmt = try getBlockStmt(result.root, 1); |
| 897 | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
| 898 | try expectConstInt(&a, expr, expected); |
| 899 | } |
| 900 | } |
| 901 | |
| 902 | // Tests /////////////////////////////////////////////////////////////////////// |
| 903 | |
| 904 | @test unsafe fn testResolveLit() throws (testing::TestError) { |
| 905 | let mut testArena5 = testArena(); |
| 906 | let testStorage5: 'test5 = &mut testArena5 in { |
| 907 | let mut a = testResolver(testStorage5); |
| 908 | let result = try resolveExprStr(&mut a, "true"); |
| 909 | |
| 910 | try expectNoErrors(&result); |
| 911 | try expectType(&a, result.root, super::Type::Bool); |
| 912 | } |
| 913 | } |
| 914 | |
| 915 | @test unsafe fn testResolveStringLiteralType() throws (testing::TestError) { |
| 916 | let mut testArena6 = testArena(); |
| 917 | let testStorage6: 'test6 = &mut testArena6 in { |
| 918 | let mut a = testResolver(testStorage6); |
| 919 | let result = try resolveExprStr(&mut a, "\"hello\""); |
| 920 | |
| 921 | try expectNoErrors(&result); |
| 922 | let ty = try typeOf(&a, result.root); |
| 923 | let elemTy = try expectSliceType(ty, false); |
| 924 | try testing::expect(elemTy == super::Type::U8); |
| 925 | } |
| 926 | } |
| 927 | |
| 928 | @test unsafe fn testResolveAsNumeric() throws (testing::TestError) { |
| 929 | { |
| 930 | let mut testArena7 = testArena(); |
| 931 | let testStorage7: 'test7 = &mut testArena7 in { |
| 932 | let mut a = testResolver(testStorage7); |
| 933 | let result = try resolveExprStr(&mut a, "1 as u32"); |
| 934 | try expectNoErrors(&result); |
| 935 | try expectType(&a, result.root, super::Type::U32); |
| 936 | } |
| 937 | } { |
| 938 | let mut testArena8 = testArena(); |
| 939 | let testStorage8: 'test8 = &mut testArena8 in { |
| 940 | let mut a = testResolver(testStorage8); |
| 941 | let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;"); |
| 942 | try expectNoErrors(&result); |
| 943 | |
| 944 | let x = try getBlockStmt(result.root, 1); |
| 945 | try expectExprStmtType(&a, x, super::Type::U8); |
| 946 | } |
| 947 | } |
| 948 | } |
| 949 | |
| 950 | @test unsafe fn testResolveAsInvalid() throws (testing::TestError) { |
| 951 | let mut testArena9 = testArena(); |
| 952 | let testStorage9: 'test9 = &mut testArena9 in { |
| 953 | let mut a = testResolver(testStorage9); |
| 954 | let result = try resolveProgramStr(&mut a, "true as u32"); |
| 955 | |
| 956 | try expectErrorKind( |
| 957 | &result, |
| 958 | super::ErrorKind::InvalidAsCast(super::InvalidAsCast { |
| 959 | from: super::Type::Bool, |
| 960 | to: super::Type::U32, |
| 961 | }) |
| 962 | ); |
| 963 | } |
| 964 | } |
| 965 | |
| 966 | @test unsafe fn testResolveAsUnionToInt() throws (testing::TestError) { |
| 967 | let mut testArena10 = testArena(); |
| 968 | let testStorage10: 'test10 = &mut testArena10 in { |
| 969 | let mut a = testResolver(testStorage10); |
| 970 | let program = "union Color { Red } Color::Red as u32;"; |
| 971 | let result = try resolveProgramStr(&mut a, program); |
| 972 | try expectNoErrors(&result); |
| 973 | |
| 974 | let red = try getBlockStmt(result.root, 1); |
| 975 | try expectExprStmtType(&a, red, super::Type::U32); |
| 976 | } |
| 977 | } |
| 978 | |
| 979 | @test unsafe fn testResolveBinding() throws (testing::TestError) { |
| 980 | let mut testArena11 = testArena(); |
| 981 | let testStorage11: 'test11 = &mut testArena11 in { |
| 982 | let mut a = testResolver(testStorage11); |
| 983 | let result = try resolveBlockStr(&mut a, "let x: bool = true; x;"); |
| 984 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 985 | |
| 986 | try expectNoErrors(&result); |
| 987 | try expectType(&a, stmt, super::Type::Void); |
| 988 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 989 | |
| 990 | let case ast::NodeValue::ExprStmt(x) = stmt.value |
| 991 | else throw testing::TestError::Failed; |
| 992 | |
| 993 | let sym = super::symbolFor(&a, x) |
| 994 | else throw testing::TestError::Failed; |
| 995 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
| 996 | else throw testing::TestError::Failed; |
| 997 | try testing::expect(valType == super::Type::Bool); |
| 998 | } |
| 999 | } |
| 1000 | |
| 1001 | @test unsafe fn testResolveBindingInvalid() throws (testing::TestError) { |
| 1002 | let mut testArena12 = testArena(); |
| 1003 | let testStorage12: 'test12 = &mut testArena12 in { |
| 1004 | let mut a = testResolver(testStorage12); |
| 1005 | let result = try resolveBlockStr(&mut a, "let x: i32 = true;"); |
| 1006 | let err = try expectError(&result); |
| 1007 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 1008 | } |
| 1009 | } |
| 1010 | |
| 1011 | @test unsafe fn testResolveDuplicateBinding() throws (testing::TestError) { |
| 1012 | let mut testArena13 = testArena(); |
| 1013 | let testStorage13: 'test13 = &mut testArena13 in { |
| 1014 | let mut a = testResolver(testStorage13); |
| 1015 | let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;"); |
| 1016 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 1017 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x")); |
| 1018 | } |
| 1019 | } |
| 1020 | |
| 1021 | @test unsafe fn testResolveConstLiteralValue() throws (testing::TestError) { |
| 1022 | let mut testArena14 = testArena(); |
| 1023 | let testStorage14: 'test14 = &mut testArena14 in { |
| 1024 | let mut a = testResolver(testStorage14); |
| 1025 | let program = "constant ANSWER: i32 = 42;"; |
| 1026 | let result = try resolveProgramStr(&mut a, program); |
| 1027 | try expectNoErrors(&result); |
| 1028 | |
| 1029 | let constNode = try getBlockStmt(result.root, 0); |
| 1030 | let sym = super::symbolFor(&a, constNode) |
| 1031 | else throw testing::TestError::Failed; |
| 1032 | let case super::SymbolData::Constant { type: constType, .. } = sym.data |
| 1033 | else throw testing::TestError::Failed; |
| 1034 | try testing::expect(constType == super::Type::I32); |
| 1035 | } |
| 1036 | } |
| 1037 | |
| 1038 | @test unsafe fn testResolveConstRequiresConstantExpr() throws (testing::TestError) { |
| 1039 | let mut testArena15 = testArena(); |
| 1040 | let testStorage15: 'test15 = &mut testArena15 in { |
| 1041 | let mut a = testResolver(testStorage15); |
| 1042 | let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }"; |
| 1043 | let result = try resolveProgramStr(&mut a, program); |
| 1044 | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
| 1045 | |
| 1046 | let errNode = err.node |
| 1047 | else throw testing::TestError::Failed; |
| 1048 | let case ast::NodeValue::Call(_) = errNode.value |
| 1049 | else throw testing::TestError::Failed; |
| 1050 | } |
| 1051 | } |
| 1052 | |
| 1053 | @test unsafe fn testResolveStaticLiteralValue() throws (testing::TestError) { |
| 1054 | let mut testArena16 = testArena(); |
| 1055 | let testStorage16: 'test16 = &mut testArena16 in { |
| 1056 | let mut a = testResolver(testStorage16); |
| 1057 | let program = "static COUNTER: i32 = 0;"; |
| 1058 | let result = try resolveProgramStr(&mut a, program); |
| 1059 | try expectNoErrors(&result); |
| 1060 | |
| 1061 | let staticNode = try getBlockStmt(result.root, 0); |
| 1062 | let sym = super::symbolFor(&a, staticNode) |
| 1063 | else throw testing::TestError::Failed; |
| 1064 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
| 1065 | else throw testing::TestError::Failed; |
| 1066 | try testing::expect(valType == super::Type::I32); |
| 1067 | } |
| 1068 | } |
| 1069 | |
| 1070 | @test unsafe fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) { |
| 1071 | let mut testArena17 = testArena(); |
| 1072 | let testStorage17: 'test17 = &mut testArena17 in { |
| 1073 | let mut a = testResolver(testStorage17); |
| 1074 | let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();"; |
| 1075 | let result = try resolveProgramStr(&mut a, program); |
| 1076 | let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
| 1077 | |
| 1078 | let errNode = err.node |
| 1079 | else throw testing::TestError::Failed; |
| 1080 | let case ast::NodeValue::Call(_) = errNode.value |
| 1081 | else throw testing::TestError::Failed; |
| 1082 | } |
| 1083 | } |
| 1084 | |
| 1085 | @test unsafe fn testSymbolStoresFnAttributes() throws (testing::TestError) { |
| 1086 | let mut testArena18 = testArena(); |
| 1087 | let testStorage18: 'test18 = &mut testArena18 in { |
| 1088 | let mut a = testResolver(testStorage18); |
| 1089 | let program = "@default export fn f() { return; }"; |
| 1090 | let result = try resolveProgramStr(&mut a, program); |
| 1091 | try expectNoErrors(&result); |
| 1092 | |
| 1093 | let scope = super::scopeFor(&a, result.root) |
| 1094 | else throw testing::TestError::Failed; |
| 1095 | let sym = super::findSymbolInScope(scope, "f") |
| 1096 | else throw testing::TestError::Failed; |
| 1097 | |
| 1098 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
| 1099 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
| 1100 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
| 1101 | } |
| 1102 | } |
| 1103 | |
| 1104 | @test unsafe fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
| 1105 | let mut testArena19 = testArena(); |
| 1106 | let testStorage19: 'test19 = &mut testArena19 in { |
| 1107 | let mut a = testResolver(testStorage19); |
| 1108 | let program = "export record S { value: i32 }"; |
| 1109 | let result = try resolveProgramStr(&mut a, program); |
| 1110 | try expectNoErrors(&result); |
| 1111 | |
| 1112 | let scope = super::scopeFor(&a, result.root) |
| 1113 | else throw testing::TestError::Failed; |
| 1114 | let sym = super::findSymbolInScope(scope, "S") |
| 1115 | else throw testing::TestError::Failed; |
| 1116 | |
| 1117 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
| 1118 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
| 1119 | } |
| 1120 | } |
| 1121 | |
| 1122 | @test unsafe fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) { |
| 1123 | let mut testArena20 = testArena(); |
| 1124 | let testStorage20: 'test20 = &mut testArena20 in { |
| 1125 | let mut a = testResolver(testStorage20); |
| 1126 | let program = "@default record T { value: i32 }"; |
| 1127 | let result = try resolveProgramStr(&mut a, program); |
| 1128 | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
| 1129 | } |
| 1130 | } |
| 1131 | |
| 1132 | @test unsafe fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) { |
| 1133 | let mut testArena21 = testArena(); |
| 1134 | let testStorage21: 'test21 = &mut testArena21 in { |
| 1135 | let mut a = testResolver(testStorage21); |
| 1136 | let program = "@default union Result { Ok, Err }"; |
| 1137 | let result = try resolveProgramStr(&mut a, program); |
| 1138 | try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn); |
| 1139 | } |
| 1140 | } |
| 1141 | |
| 1142 | @test unsafe fn testResolveArrayLiteralTyped() throws (testing::TestError) { |
| 1143 | let mut testArena22 = testArena(); |
| 1144 | let testStorage22: 'test22 = &mut testArena22 in { |
| 1145 | let mut a = testResolver(testStorage22); |
| 1146 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];"); |
| 1147 | try expectNoErrors(&result); |
| 1148 | |
| 1149 | let stmt = try getBlockStmt(result.root, 0); |
| 1150 | let case ast::NodeValue::Let(decl) = stmt.value |
| 1151 | else throw testing::TestError::Failed; |
| 1152 | let arrayTy = try typeOf(&a, decl.value); |
| 1153 | let elemTy = try expectArrayType(arrayTy, 2); |
| 1154 | try testing::expect(elemTy == super::Type::I32); |
| 1155 | } |
| 1156 | } |
| 1157 | |
| 1158 | @test unsafe fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) { |
| 1159 | let mut testArena23 = testArena(); |
| 1160 | let testStorage23: 'test23 = &mut testArena23 in { |
| 1161 | let mut a = testResolver(testStorage23); |
| 1162 | let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];"); |
| 1163 | let err = try expectError(&result); |
| 1164 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 1165 | } |
| 1166 | } |
| 1167 | |
| 1168 | @test unsafe fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) { |
| 1169 | let mut testArena24 = testArena(); |
| 1170 | let testStorage24: 'test24 = &mut testArena24 in { |
| 1171 | let mut a = testResolver(testStorage24); |
| 1172 | let result = try resolveProgramStr(&mut a, "let xs = [1, 2];"); |
| 1173 | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
| 1174 | } |
| 1175 | } |
| 1176 | |
| 1177 | @test unsafe fn testResolveArrayLiteralOverflow() throws (testing::TestError) { |
| 1178 | let mut testArena25 = testArena(); |
| 1179 | let testStorage25: 'test25 = &mut testArena25 in { |
| 1180 | let mut a = testResolver(testStorage25); |
| 1181 | let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];"); |
| 1182 | let err = try expectError(&result); |
| 1183 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1184 | else throw testing::TestError::Failed; |
| 1185 | } |
| 1186 | } |
| 1187 | |
| 1188 | @test unsafe fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) { |
| 1189 | let mut testArena26 = testArena(); |
| 1190 | let testStorage26: 'test26 = &mut testArena26 in { |
| 1191 | let mut a = testResolver(testStorage26); |
| 1192 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];"); |
| 1193 | let err = try expectError(&result); |
| 1194 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1195 | else throw testing::TestError::Failed; |
| 1196 | } |
| 1197 | } |
| 1198 | |
| 1199 | @test unsafe fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) { |
| 1200 | let mut testArena27 = testArena(); |
| 1201 | let testStorage27: 'test27 = &mut testArena27 in { |
| 1202 | let mut a = testResolver(testStorage27); |
| 1203 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];"); |
| 1204 | let err = try expectError(&result); |
| 1205 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1206 | else throw testing::TestError::Failed; |
| 1207 | } |
| 1208 | } |
| 1209 | |
| 1210 | @test unsafe fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) { |
| 1211 | let mut testArena28 = testArena(); |
| 1212 | let testStorage28: 'test28 = &mut testArena28 in { |
| 1213 | let mut a = testResolver(testStorage28); |
| 1214 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];"); |
| 1215 | try expectNoErrors(&result); |
| 1216 | } |
| 1217 | } |
| 1218 | |
| 1219 | @test unsafe fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) { |
| 1220 | let mut testArena29 = testArena(); |
| 1221 | let testStorage29: 'test29 = &mut testArena29 in { |
| 1222 | let mut a = testResolver(testStorage29); |
| 1223 | let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];"); |
| 1224 | try expectNoErrors(&result); |
| 1225 | |
| 1226 | let stmt = try getBlockStmt(result.root, 0); |
| 1227 | let case ast::NodeValue::Let(decl) = stmt.value |
| 1228 | else throw testing::TestError::Failed; |
| 1229 | let gridTy = try typeOf(&a, decl.value); |
| 1230 | let rowTy = try expectArrayType(gridTy, 2); |
| 1231 | let elemTy = try expectArrayType(rowTy, 2); |
| 1232 | try testing::expect(elemTy == super::Type::I32); |
| 1233 | } |
| 1234 | } |
| 1235 | |
| 1236 | @test unsafe fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) { |
| 1237 | let mut testArena30 = testArena(); |
| 1238 | let testStorage30: 'test30 = &mut testArena30 in { |
| 1239 | let mut a = testResolver(testStorage30); |
| 1240 | let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];"); |
| 1241 | try expectNoErrors(&result); |
| 1242 | |
| 1243 | let stmt = try getBlockStmt(result.root, 0); |
| 1244 | let case ast::NodeValue::Let(decl) = stmt.value |
| 1245 | else throw testing::TestError::Failed; |
| 1246 | let arrayTy = try typeOf(&a, decl.value); |
| 1247 | let elemTy = try expectArrayType(arrayTy, 2); |
| 1248 | let case super::Type::Optional(inner) = elemTy |
| 1249 | else throw testing::TestError::Failed; |
| 1250 | try testing::expect(*inner == super::Type::I32); |
| 1251 | } |
| 1252 | } |
| 1253 | |
| 1254 | @test unsafe fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) { |
| 1255 | let mut testArena31 = testArena(); |
| 1256 | let testStorage31: 'test31 = &mut testArena31 in { |
| 1257 | let mut a = testResolver(testStorage31); |
| 1258 | let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];"); |
| 1259 | let err = try expectError(&result); |
| 1260 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1261 | else throw testing::TestError::Failed; |
| 1262 | } |
| 1263 | } |
| 1264 | |
| 1265 | @test unsafe fn testResolveArrayRepeatBasic() throws (testing::TestError) { |
| 1266 | let mut testArena32 = testArena(); |
| 1267 | let testStorage32: 'test32 = &mut testArena32 in { |
| 1268 | let mut a = testResolver(testStorage32); |
| 1269 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];"); |
| 1270 | try expectNoErrors(&result); |
| 1271 | |
| 1272 | let stmt = try getBlockStmt(result.root, 0); |
| 1273 | let case ast::NodeValue::Let(decl) = stmt.value |
| 1274 | else throw testing::TestError::Failed; |
| 1275 | let arrayTy = try typeOf(&a, decl.value); |
| 1276 | let elemTy = try expectArrayType(arrayTy, 3); |
| 1277 | try testing::expect(elemTy == super::Type::I32); |
| 1278 | } |
| 1279 | } |
| 1280 | |
| 1281 | @test unsafe fn testResolveArrayRepeatWithExpression() throws (testing::TestError) { |
| 1282 | let mut testArena33 = testArena(); |
| 1283 | let testStorage33: 'test33 = &mut testArena33 in { |
| 1284 | let mut a = testResolver(testStorage33); |
| 1285 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];"); |
| 1286 | try expectNoErrors(&result); |
| 1287 | |
| 1288 | let stmt = try getBlockStmt(result.root, 0); |
| 1289 | let case ast::NodeValue::Let(decl) = stmt.value |
| 1290 | else throw testing::TestError::Failed; |
| 1291 | let arrayTy = try typeOf(&a, decl.value); |
| 1292 | let elemTy = try expectArrayType(arrayTy, 5); |
| 1293 | try testing::expect(elemTy == super::Type::I32); |
| 1294 | } |
| 1295 | } |
| 1296 | |
| 1297 | @test unsafe fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) { |
| 1298 | let mut testArena34 = testArena(); |
| 1299 | let testStorage34: 'test34 = &mut testArena34 in { |
| 1300 | let mut a = testResolver(testStorage34); |
| 1301 | // `3 * 1` folds to a compile-time constant, so the repeat count is valid. |
| 1302 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];"); |
| 1303 | try expectNoErrors(&result); |
| 1304 | } |
| 1305 | } |
| 1306 | |
| 1307 | @test unsafe fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) { |
| 1308 | let mut testArena35 = testArena(); |
| 1309 | let testStorage35: 'test35 = &mut testArena35 in { |
| 1310 | let mut a = testResolver(testStorage35); |
| 1311 | // A function call is not a constant expression. |
| 1312 | let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];"); |
| 1313 | try expectErrorKind(&result, super::ErrorKind::ConstExprRequired); |
| 1314 | } |
| 1315 | } |
| 1316 | |
| 1317 | @test unsafe fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) { |
| 1318 | let mut testArena36 = testArena(); |
| 1319 | let testStorage36: 'test36 = &mut testArena36 in { |
| 1320 | let mut a = testResolver(testStorage36); |
| 1321 | let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];"); |
| 1322 | let err = try expectError(&result); |
| 1323 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1324 | else throw testing::TestError::Failed; |
| 1325 | } |
| 1326 | } |
| 1327 | |
| 1328 | @test unsafe fn testResolveArrayIndex() throws (testing::TestError) { |
| 1329 | let mut testArena37 = testArena(); |
| 1330 | let testStorage37: 'test37 = &mut testArena37 in { |
| 1331 | let mut a = testResolver(testStorage37); |
| 1332 | let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];"; |
| 1333 | let result = try resolveProgramStr(&mut a, program); |
| 1334 | try expectNoErrors(&result); |
| 1335 | |
| 1336 | let stmt = try getBlockStmt(result.root, 1); |
| 1337 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 1338 | } |
| 1339 | } |
| 1340 | |
| 1341 | @test unsafe fn testResolveSliceIndex() throws (testing::TestError) { |
| 1342 | let mut testArena38 = testArena(); |
| 1343 | let testStorage38: 'test38 = &mut testArena38 in { |
| 1344 | let mut a = testResolver(testStorage38); |
| 1345 | let program = "static xs: [i32; 4] = [1, 2, 3, 4]; let slice = &xs[1..]; slice[1];"; |
| 1346 | let result = try resolveProgramStr(&mut a, program); |
| 1347 | try expectNoErrors(&result); |
| 1348 | |
| 1349 | let sliceStmt = try getBlockStmt(result.root, 1); |
| 1350 | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
| 1351 | else throw testing::TestError::Failed; |
| 1352 | let sliceTy = try typeOf(&a, sliceDecl.value); |
| 1353 | let elemTy = try expectSliceType(sliceTy, false); |
| 1354 | try testing::expect(elemTy == super::Type::I32); |
| 1355 | |
| 1356 | let indexStmt = try getBlockStmt(result.root, 2); |
| 1357 | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
| 1358 | } |
| 1359 | } |
| 1360 | |
| 1361 | @test unsafe fn testResolveSliceFields() throws (testing::TestError) { |
| 1362 | let mut testArena39 = testArena(); |
| 1363 | let testStorage39: 'test39 = &mut testArena39 in { |
| 1364 | let mut a = testResolver(testStorage39); |
| 1365 | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[1..]; slice.len; unsafe { slice.ptr; }"; |
| 1366 | let result = try resolveProgramStr(&mut a, program); |
| 1367 | try expectNoErrors(&result); |
| 1368 | |
| 1369 | let lenStmt = try getBlockStmt(result.root, 2); |
| 1370 | let case ast::NodeValue::ExprStmt(lenExpr) = lenStmt.value |
| 1371 | else throw testing::TestError::Failed; |
| 1372 | let lenTy = try typeOf(&a, lenExpr); |
| 1373 | try testing::expect(lenTy == super::Type::U32); |
| 1374 | |
| 1375 | let ptrStmt = try getBlockStmt(result.root, 3); |
| 1376 | let case ast::NodeValue::Block(ptrBlock) = ptrStmt.value |
| 1377 | else throw testing::TestError::Failed; |
| 1378 | let case ast::NodeValue::ExprStmt(ptrExpr) = ptrBlock.statements[0].value |
| 1379 | else throw testing::TestError::Failed; |
| 1380 | let ptrTy = try typeOf(&a, ptrExpr); |
| 1381 | let targetTy = try expectPointerType(ptrTy, false); |
| 1382 | try testing::expect(targetTy == super::Type::I32); |
| 1383 | } |
| 1384 | } |
| 1385 | |
| 1386 | @test unsafe fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
| 1387 | let mut testArena40 = testArena(); |
| 1388 | let testStorage40: 'test40 = &mut testArena40 in { |
| 1389 | let mut a = testResolver(testStorage40); |
| 1390 | let program = "let slice: *[i32] = &[1, 2, 3];"; |
| 1391 | let result = try resolveProgramStr(&mut a, program); |
| 1392 | try expectNoErrors(&result); |
| 1393 | } |
| 1394 | } |
| 1395 | |
| 1396 | /// Empty array literal infers element type from slice annotation. |
| 1397 | @test unsafe fn testResolveSliceLiteralEmpty() throws (testing::TestError) { |
| 1398 | let mut testArena41 = testArena(); |
| 1399 | let testStorage41: 'test41 = &mut testArena41 in { |
| 1400 | let mut a = testResolver(testStorage41); |
| 1401 | let program = "let slice: *[i32] = &[];"; |
| 1402 | let result = try resolveProgramStr(&mut a, program); |
| 1403 | try expectNoErrors(&result); |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | /// Nested array literal should infer inner element type from slice annotation. |
| 1408 | @test unsafe fn testResolveSliceLiteralNestedArray() throws (testing::TestError) { |
| 1409 | let mut testArena42 = testArena(); |
| 1410 | let testStorage42: 'test42 = &mut testArena42 in { |
| 1411 | let mut a = testResolver(testStorage42); |
| 1412 | let program = "let slice: *[[i32; 2]] = &[[1, 2], [3, 4]];"; |
| 1413 | let result = try resolveProgramStr(&mut a, program); |
| 1414 | try expectNoErrors(&result); |
| 1415 | } |
| 1416 | } |
| 1417 | |
| 1418 | @test unsafe fn testResolveSliceFromArray() throws (testing::TestError) { |
| 1419 | { |
| 1420 | let mut testArena43 = testArena(); |
| 1421 | let testStorage43: 'test43 = &mut testArena43 in { |
| 1422 | let mut a = testResolver(testStorage43); |
| 1423 | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..];"; |
| 1424 | let result = try resolveProgramStr(&mut a, program); |
| 1425 | try expectNoErrors(&result); |
| 1426 | } |
| 1427 | } { |
| 1428 | let mut testArena44 = testArena(); |
| 1429 | let testStorage44: 'test44 = &mut testArena44 in { |
| 1430 | let mut a = testResolver(testStorage44); |
| 1431 | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3];"; |
| 1432 | let result = try resolveProgramStr(&mut a, program); |
| 1433 | try expectNoErrors(&result); |
| 1434 | } |
| 1435 | } { |
| 1436 | let mut testArena45 = testArena(); |
| 1437 | let testStorage45: 'test45 = &mut testArena45 in { |
| 1438 | let mut a = testResolver(testStorage45); |
| 1439 | let program = "static xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3];"; |
| 1440 | let result = try resolveProgramStr(&mut a, program); |
| 1441 | try expectNoErrors(&result); |
| 1442 | } |
| 1443 | } { |
| 1444 | let mut testArena46 = testArena(); |
| 1445 | let testStorage46: 'test46 = &mut testArena46 in { |
| 1446 | let mut a = testResolver(testStorage46); |
| 1447 | let program = "static xs: [u8; 2] = [1, 2]; let slice = &xs[1..1];"; |
| 1448 | let result = try resolveProgramStr(&mut a, program); |
| 1449 | try expectNoErrors(&result); |
| 1450 | } |
| 1451 | } |
| 1452 | } |
| 1453 | |
| 1454 | @test unsafe fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) { |
| 1455 | let mut testArena47 = testArena(); |
| 1456 | let testStorage47: 'test47 = &mut testArena47 in { |
| 1457 | let mut a = testResolver(testStorage47); |
| 1458 | let program = "let slice: *mut [i32] = &[1, 2, 3];"; |
| 1459 | let result = try resolveProgramStr(&mut a, program); |
| 1460 | let err = try expectError(&result); |
| 1461 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1462 | else throw testing::TestError::Failed; |
| 1463 | } |
| 1464 | } |
| 1465 | |
| 1466 | @test unsafe fn testResolveSliceLiteralMutable() throws (testing::TestError) { |
| 1467 | let mut testArena48 = testArena(); |
| 1468 | let testStorage48: 'test48 = &mut testArena48 in { |
| 1469 | let mut a = testResolver(testStorage48); |
| 1470 | let program = "let slice: *mut [i32] = &mut [1, 2, 3];"; |
| 1471 | let result = try resolveProgramStr(&mut a, program); |
| 1472 | try expectNoErrors(&result); |
| 1473 | } |
| 1474 | } |
| 1475 | |
| 1476 | @test unsafe fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) { |
| 1477 | let mut testArena49 = testArena(); |
| 1478 | let testStorage49: 'test49 = &mut testArena49 in { |
| 1479 | let mut a = testResolver(testStorage49); |
| 1480 | let program = "let x: i32 = 0; let ptr: *mut i32 = &x;"; |
| 1481 | let result = try resolveProgramStr(&mut a, program); |
| 1482 | let err = try expectError(&result); |
| 1483 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1484 | else throw testing::TestError::Failed; |
| 1485 | } |
| 1486 | } |
| 1487 | |
| 1488 | @test unsafe fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) { |
| 1489 | let mut testArena50 = testArena(); |
| 1490 | let testStorage50: 'test50 = &mut testArena50 in { |
| 1491 | let mut a = testResolver(testStorage50); |
| 1492 | let program = "static x: i32 = 0; let mptr: *mut i32 = &mut x; let ptr: *i32 = mptr;"; |
| 1493 | let result = try resolveProgramStr(&mut a, program); |
| 1494 | try expectNoErrors(&result); |
| 1495 | } |
| 1496 | } |
| 1497 | |
| 1498 | @test unsafe fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) { |
| 1499 | { |
| 1500 | let mut testArena51 = testArena(); |
| 1501 | let testStorage51: 'test51 = &mut testArena51 in { |
| 1502 | let mut a = testResolver(testStorage51); |
| 1503 | let program = "let x: i32 = 0; let ptr = &mut x;"; |
| 1504 | let result = try resolveProgramStr(&mut a, program); |
| 1505 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 1506 | } |
| 1507 | } { |
| 1508 | let mut testArena52 = testArena(); |
| 1509 | let testStorage52: 'test52 = &mut testArena52 in { |
| 1510 | let mut a = testResolver(testStorage52); |
| 1511 | let program = "let mut x: i32 = 0; &mut x;"; |
| 1512 | let result = try resolveProgramStr(&mut a, program); |
| 1513 | try expectNoErrors(&result); |
| 1514 | } |
| 1515 | } |
| 1516 | } |
| 1517 | |
| 1518 | @test unsafe fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) { |
| 1519 | { |
| 1520 | let mut testArena53 = testArena(); |
| 1521 | let testStorage53: 'test53 = &mut testArena53 in { |
| 1522 | let mut a = testResolver(testStorage53); |
| 1523 | let program = "let xs: [i32; 3] = [1, 2, 3]; let slice = &mut xs[..];"; |
| 1524 | let result = try resolveProgramStr(&mut a, program); |
| 1525 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 1526 | } |
| 1527 | } { |
| 1528 | let mut testArena54 = testArena(); |
| 1529 | let testStorage54: 'test54 = &mut testArena54 in { |
| 1530 | let mut a = testResolver(testStorage54); |
| 1531 | let program = "let mut xs: [i32; 3] = [1, 2, 3]; &mut xs[..];"; |
| 1532 | let result = try resolveProgramStr(&mut a, program); |
| 1533 | try expectNoErrors(&result); |
| 1534 | } |
| 1535 | } |
| 1536 | } |
| 1537 | |
| 1538 | @test unsafe fn testResolveSliceCannotAssignToArray() throws (testing::TestError) { |
| 1539 | let mut testArena55 = testArena(); |
| 1540 | let testStorage55: 'test55 = &mut testArena55 in { |
| 1541 | let mut a = testResolver(testStorage55); |
| 1542 | let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;"; |
| 1543 | let result = try resolveProgramStr(&mut a, program); |
| 1544 | let err = try expectError(&result); |
| 1545 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 1546 | else throw testing::TestError::Failed; |
| 1547 | } |
| 1548 | } |
| 1549 | |
| 1550 | @test unsafe fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) { |
| 1551 | let mut testArena56 = testArena(); |
| 1552 | let testStorage56: 'test56 = &mut testArena56 in { |
| 1553 | let mut a = testResolver(testStorage56); |
| 1554 | let program = "let xs: [u8; 2] = [1, 2]; xs[..];"; |
| 1555 | let result = try resolveProgramStr(&mut a, program); |
| 1556 | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
| 1557 | } |
| 1558 | } |
| 1559 | |
| 1560 | @test unsafe fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) { |
| 1561 | let mut testArena57 = testArena(); |
| 1562 | let testStorage57: 'test57 = &mut testArena57 in { |
| 1563 | let mut a = testResolver(testStorage57); |
| 1564 | let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }"; |
| 1565 | let result = try resolveProgramStr(&mut a, program); |
| 1566 | try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress); |
| 1567 | } |
| 1568 | } |
| 1569 | |
| 1570 | @test unsafe fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) { |
| 1571 | { |
| 1572 | let mut testArena58 = testArena(); |
| 1573 | let testStorage58: 'test58 = &mut testArena58 in { |
| 1574 | let mut a = testResolver(testStorage58); |
| 1575 | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];"; |
| 1576 | let result = try resolveProgramStr(&mut a, program); |
| 1577 | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
| 1578 | } |
| 1579 | } { |
| 1580 | let mut testArena59 = testArena(); |
| 1581 | let testStorage59: 'test59 = &mut testArena59 in { |
| 1582 | let mut a = testResolver(testStorage59); |
| 1583 | let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];"; |
| 1584 | let result = try resolveProgramStr(&mut a, program); |
| 1585 | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
| 1586 | } |
| 1587 | } { |
| 1588 | let mut testArena60 = testArena(); |
| 1589 | let testStorage60: 'test60 = &mut testArena60 in { |
| 1590 | let mut a = testResolver(testStorage60); |
| 1591 | let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];"; |
| 1592 | let result = try resolveProgramStr(&mut a, program); |
| 1593 | try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds); |
| 1594 | } |
| 1595 | } |
| 1596 | } |
| 1597 | |
| 1598 | @test unsafe fn testResolveArrayLenConstValue() throws (testing::TestError) { |
| 1599 | let mut testArena61 = testArena(); |
| 1600 | let testStorage61: 'test61 = &mut testArena61 in { |
| 1601 | let mut a = testResolver(testStorage61); |
| 1602 | let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;"; |
| 1603 | let result = try resolveBlockStr(&mut a, program); |
| 1604 | try expectNoErrors(&result); |
| 1605 | |
| 1606 | let constStmt = try getBlockStmt(result.root, 1); |
| 1607 | let case ast::NodeValue::ConstDecl(decl) = constStmt.value |
| 1608 | else throw testing::TestError::Failed; |
| 1609 | let valueConst = super::constValueEntry(&a, decl.value) |
| 1610 | else throw testing::TestError::Failed; |
| 1611 | let case super::ConstValue::Int(lenVal) = valueConst |
| 1612 | else throw testing::TestError::Failed; |
| 1613 | try testing::expect(lenVal.magnitude == 3); |
| 1614 | try testing::expect(not lenVal.negative); |
| 1615 | } |
| 1616 | } |
| 1617 | |
| 1618 | @test unsafe fn testResolveIndexNonIndexable() throws (testing::TestError) { |
| 1619 | let mut testArena62 = testArena(); |
| 1620 | let testStorage62: 'test62 = &mut testArena62 in { |
| 1621 | let mut a = testResolver(testStorage62); |
| 1622 | let program = "let flag: bool = true; flag[0];"; |
| 1623 | let result = try resolveProgramStr(&mut a, program); |
| 1624 | try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable); |
| 1625 | } |
| 1626 | } |
| 1627 | |
| 1628 | @test unsafe fn testResolveSliceFieldUnknown() throws (testing::TestError) { |
| 1629 | let mut testArena63 = testArena(); |
| 1630 | let testStorage63: 'test63 = &mut testArena63 in { |
| 1631 | let mut a = testResolver(testStorage63); |
| 1632 | let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;"; |
| 1633 | let result = try resolveProgramStr(&mut a, program); |
| 1634 | try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown")); |
| 1635 | } |
| 1636 | } |
| 1637 | |
| 1638 | @test unsafe fn testResolveArrayFieldUnknown() throws (testing::TestError) { |
| 1639 | let mut testArena64 = testArena(); |
| 1640 | let testStorage64: 'test64 = &mut testArena64 in { |
| 1641 | let mut a = testResolver(testStorage64); |
| 1642 | let program = "let xs: [i32; 2] = [1, 2]; xs.field;"; |
| 1643 | let result = try resolveProgramStr(&mut a, program); |
| 1644 | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
| 1645 | } |
| 1646 | } |
| 1647 | |
| 1648 | @test unsafe fn testResolveIfConditionRequiresBool() throws (testing::TestError) { |
| 1649 | { |
| 1650 | let mut testArena65 = testArena(); |
| 1651 | let testStorage65: 'test65 = &mut testArena65 in { |
| 1652 | let mut a = testResolver(testStorage65); |
| 1653 | let result = try resolveProgramStr(&mut a, "if 42 {}"); |
| 1654 | let err = try expectError(&result); |
| 1655 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 1656 | } |
| 1657 | } { |
| 1658 | let mut testArena66 = testArena(); |
| 1659 | let testStorage66: 'test66 = &mut testArena66 in { |
| 1660 | let mut a = testResolver(testStorage66); |
| 1661 | let result = try resolveProgramStr(&mut a, "if true {}"); |
| 1662 | try expectNoErrors(&result); |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | |
| 1667 | @test unsafe fn testResolveIfLetScopeBinding() throws (testing::TestError) { |
| 1668 | let mut testArena67 = testArena(); |
| 1669 | let testStorage67: 'test67 = &mut testArena67 in { |
| 1670 | let mut a = testResolver(testStorage67); |
| 1671 | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }"); |
| 1672 | try expectNoErrors(&result); |
| 1673 | |
| 1674 | // Get the if-let statement and verify `x` has type `i32`. |
| 1675 | let ifLetStmt = try parser::tests::getBlockLastStmt(result.root); |
| 1676 | let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value |
| 1677 | else throw testing::TestError::Failed; |
| 1678 | |
| 1679 | let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch); |
| 1680 | let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value |
| 1681 | else throw testing::TestError::Failed; |
| 1682 | |
| 1683 | try expectType(&a, xExpr, super::Type::I32); |
| 1684 | |
| 1685 | let scope = super::scopeFor(&a, ifLetStmt) |
| 1686 | else throw testing::TestError::Failed; |
| 1687 | let xSym = super::findSymbolInScope(scope, "x") |
| 1688 | else throw testing::TestError::Failed; |
| 1689 | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
| 1690 | else throw testing::TestError::Failed; |
| 1691 | |
| 1692 | try testing::expect(valType == super::Type::I32); |
| 1693 | } |
| 1694 | } |
| 1695 | |
| 1696 | @test unsafe fn testResolveIfLetScopeBindingError() throws (testing::TestError) { |
| 1697 | let mut testArena68 = testArena(); |
| 1698 | let testStorage68: 'test68 = &mut testArena68 in { |
| 1699 | let mut a = testResolver(testStorage68); |
| 1700 | let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }"); |
| 1701 | let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
| 1702 | |
| 1703 | // Verify the error comes from the else branch (offset 48). |
| 1704 | let errNode = err.node |
| 1705 | else throw testing::TestError::Failed; |
| 1706 | try testing::expect(errNode.span.offset == 48); |
| 1707 | } |
| 1708 | } |
| 1709 | |
| 1710 | /// Tests that `if let` with a condition expression binds the variable in scope. |
| 1711 | @test unsafe fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) { |
| 1712 | let mut testArena69 = testArena(); |
| 1713 | let testStorage69: 'test69 = &mut testArena69 in { |
| 1714 | let mut a = testResolver(testStorage69); |
| 1715 | let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }"; |
| 1716 | let result = try resolveProgramStr(&mut a, program); |
| 1717 | try expectNoErrors(&result); |
| 1718 | } |
| 1719 | } |
| 1720 | |
| 1721 | @test unsafe fn testResolveWhileConditionRequiresBool() throws (testing::TestError) { |
| 1722 | { |
| 1723 | let mut testArena70 = testArena(); |
| 1724 | let testStorage70: 'test70 = &mut testArena70 in { |
| 1725 | let mut a = testResolver(testStorage70); |
| 1726 | let result = try resolveProgramStr(&mut a, "while 1 {}"); |
| 1727 | let err = try expectError(&result); |
| 1728 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 1729 | } |
| 1730 | } { |
| 1731 | let mut testArena71 = testArena(); |
| 1732 | let testStorage71: 'test71 = &mut testArena71 in { |
| 1733 | let mut a = testResolver(testStorage71); |
| 1734 | let result = try resolveProgramStr(&mut a, "while true {}"); |
| 1735 | try expectNoErrors(&result); |
| 1736 | } |
| 1737 | } |
| 1738 | } |
| 1739 | |
| 1740 | @test unsafe fn testResolveWhileLetBindingScope() throws (testing::TestError) { |
| 1741 | { |
| 1742 | let mut testArena72 = testArena(); |
| 1743 | let testStorage72: 'test72 = &mut testArena72 in { |
| 1744 | let mut a = testResolver(testStorage72); |
| 1745 | let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }"; |
| 1746 | let result = try resolveProgramStr(&mut a, program); |
| 1747 | try expectNoErrors(&result); |
| 1748 | |
| 1749 | let whileStmt = try parser::tests::getBlockLastStmt(result.root); |
| 1750 | let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value |
| 1751 | else throw testing::TestError::Failed; |
| 1752 | |
| 1753 | let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body); |
| 1754 | try expectExprStmtType(&a, bodyStmt, super::Type::I32); |
| 1755 | |
| 1756 | let scope = super::scopeFor(&a, whileStmt) |
| 1757 | else throw testing::TestError::Failed; |
| 1758 | let xSym = super::findSymbolInScope(scope, "x") |
| 1759 | else throw testing::TestError::Failed; |
| 1760 | let case super::SymbolData::Value { type: valType, .. } = xSym.data |
| 1761 | else throw testing::TestError::Failed; |
| 1762 | try testing::expect(valType == super::Type::I32); |
| 1763 | } |
| 1764 | } { |
| 1765 | let mut testArena73 = testArena(); |
| 1766 | let testStorage73: 'test73 = &mut testArena73 in { |
| 1767 | let mut a = testResolver(testStorage73); |
| 1768 | let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }"; |
| 1769 | let result = try resolveProgramStr(&mut a, program); |
| 1770 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
| 1771 | } |
| 1772 | } |
| 1773 | } |
| 1774 | |
| 1775 | @test unsafe fn testResolveForArrayBindsElementType() throws (testing::TestError) { |
| 1776 | let mut testArena74 = testArena(); |
| 1777 | let testStorage74: 'test74 = &mut testArena74 in { |
| 1778 | let mut a = testResolver(testStorage74); |
| 1779 | let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }"; |
| 1780 | let result = try resolveProgramStr(&mut a, program); |
| 1781 | try expectNoErrors(&result); |
| 1782 | |
| 1783 | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
| 1784 | let case ast::NodeValue::For(loopNode) = forStmt.value |
| 1785 | else throw testing::TestError::Failed; |
| 1786 | |
| 1787 | let scope = super::scopeFor(&a, forStmt) |
| 1788 | else throw testing::TestError::Failed; |
| 1789 | let sym = super::findSymbolInScope(scope, "x") |
| 1790 | else throw testing::TestError::Failed; |
| 1791 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
| 1792 | else throw testing::TestError::Failed; |
| 1793 | try testing::expect(valType == super::Type::I32); |
| 1794 | |
| 1795 | let bindingTy = super::typeFor(&a, loopNode.binding) |
| 1796 | else throw testing::TestError::Failed; |
| 1797 | try testing::expect(bindingTy == super::Type::I32); |
| 1798 | } |
| 1799 | } |
| 1800 | |
| 1801 | @test unsafe fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) { |
| 1802 | let mut testArena75 = testArena(); |
| 1803 | let testStorage75: 'test75 = &mut testArena75 in { |
| 1804 | let mut a = testResolver(testStorage75); |
| 1805 | let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }"; |
| 1806 | let result = try resolveProgramStr(&mut a, program); |
| 1807 | try expectNoErrors(&result); |
| 1808 | |
| 1809 | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
| 1810 | let case ast::NodeValue::For(loopNode) = forStmt.value |
| 1811 | else throw testing::TestError::Failed; |
| 1812 | |
| 1813 | let scope = super::scopeFor(&a, forStmt) |
| 1814 | else throw testing::TestError::Failed; |
| 1815 | let valueSym = super::findSymbolInScope(scope, "value") |
| 1816 | else throw testing::TestError::Failed; |
| 1817 | let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data |
| 1818 | else throw testing::TestError::Failed; |
| 1819 | try testing::expect(valueValType == super::Type::Bool); |
| 1820 | let indexSym = super::findSymbolInScope(scope, "idx") |
| 1821 | else throw testing::TestError::Failed; |
| 1822 | let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data |
| 1823 | else throw testing::TestError::Failed; |
| 1824 | try testing::expect(indexValType == super::Type::U32); |
| 1825 | |
| 1826 | let indexNode = loopNode.index |
| 1827 | else throw testing::TestError::Failed; |
| 1828 | let indexTy = super::typeFor(&a, indexNode) |
| 1829 | else throw testing::TestError::Failed; |
| 1830 | try testing::expect(indexTy == super::Type::U32); |
| 1831 | } |
| 1832 | } |
| 1833 | |
| 1834 | @test unsafe fn testResolveForSliceIterable() throws (testing::TestError) { |
| 1835 | let mut testArena76 = testArena(); |
| 1836 | let testStorage76: 'test76 = &mut testArena76 in { |
| 1837 | let mut a = testResolver(testStorage76); |
| 1838 | let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }"; |
| 1839 | let result = try resolveProgramStr(&mut a, program); |
| 1840 | try expectNoErrors(&result); |
| 1841 | |
| 1842 | let forStmt = try parser::tests::getBlockLastStmt(result.root); |
| 1843 | let case ast::NodeValue::For(loopNode) = forStmt.value |
| 1844 | else throw testing::TestError::Failed; |
| 1845 | |
| 1846 | let bindingTy = super::typeFor(&a, loopNode.binding) |
| 1847 | else throw testing::TestError::Failed; |
| 1848 | try testing::expect(bindingTy == super::Type::I32); |
| 1849 | } |
| 1850 | } |
| 1851 | |
| 1852 | @test unsafe fn testResolveForRequiresIterable() throws (testing::TestError) { |
| 1853 | let mut testArena77 = testArena(); |
| 1854 | let testStorage77: 'test77 = &mut testArena77 in { |
| 1855 | let mut a = testResolver(testStorage77); |
| 1856 | let result = try resolveProgramStr(&mut a, "for x in true { x; }"); |
| 1857 | try expectErrorKind(&result, super::ErrorKind::ExpectedIterable); |
| 1858 | } |
| 1859 | } |
| 1860 | |
| 1861 | @test unsafe fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) { |
| 1862 | let mut testArena78 = testArena(); |
| 1863 | let testStorage78: 'test78 = &mut testArena78 in { |
| 1864 | let mut a = testResolver(testStorage78); |
| 1865 | let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }"); |
| 1866 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 1867 | } |
| 1868 | } |
| 1869 | |
| 1870 | @test unsafe fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) { |
| 1871 | let mut testArena79 = testArena(); |
| 1872 | let testStorage79: 'test79 = &mut testArena79 in { |
| 1873 | let mut a = testResolver(testStorage79); |
| 1874 | let program = "let val: i32 = 0; match val { case true => {} }"; |
| 1875 | let result = try resolveProgramStr(&mut a, program); |
| 1876 | let err = try expectError(&result); |
| 1877 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 1878 | } |
| 1879 | } |
| 1880 | |
| 1881 | @test unsafe fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) { |
| 1882 | let mut testArena80 = testArena(); |
| 1883 | let testStorage80: 'test80 = &mut testArena80 in { |
| 1884 | let mut a = testResolver(testStorage80); |
| 1885 | let program = "union First { A } union Second { B } fn run(val: First) { match val { case Second::B => {} } }"; |
| 1886 | let result = try resolveProgramStr(&mut a, program); |
| 1887 | let err = try expectError(&result); |
| 1888 | |
| 1889 | let firstTy = try getTypeInScopeOf(&a, result.root, "First"); |
| 1890 | let secondTy = try getTypeInScopeOf(&a, result.root, "Second"); |
| 1891 | try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy)); |
| 1892 | } |
| 1893 | } |
| 1894 | |
| 1895 | @test unsafe fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) { |
| 1896 | let mut testArena81 = testArena(); |
| 1897 | let testStorage81: 'test81 = &mut testArena81 in { |
| 1898 | let mut a = testResolver(testStorage81); |
| 1899 | let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
| 1900 | let result = try resolveProgramStr(&mut a, program); |
| 1901 | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some")); |
| 1902 | } |
| 1903 | } |
| 1904 | |
| 1905 | @test unsafe fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) { |
| 1906 | let mut testArena82 = testArena(); |
| 1907 | let testStorage82: 'test82 = &mut testArena82 in { |
| 1908 | let mut a = testResolver(testStorage82); |
| 1909 | let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }"; |
| 1910 | let result = try resolveProgramStr(&mut a, program); |
| 1911 | try expectNoErrors(&result); |
| 1912 | } |
| 1913 | } |
| 1914 | |
| 1915 | @test unsafe fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) { |
| 1916 | let mut testArena83 = testArena(); |
| 1917 | let testStorage83: 'test83 = &mut testArena83 in { |
| 1918 | let mut a = testResolver(testStorage83); |
| 1919 | let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }"; |
| 1920 | let result = try resolveProgramStr(&mut a, program); |
| 1921 | try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None")); |
| 1922 | } |
| 1923 | } |
| 1924 | |
| 1925 | @test unsafe fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) { |
| 1926 | let mut testArena84 = testArena(); |
| 1927 | let testStorage84: 'test84 = &mut testArena84 in { |
| 1928 | let mut a = testResolver(testStorage84); |
| 1929 | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }"; |
| 1930 | let result = try resolveProgramStr(&mut a, program); |
| 1931 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
| 1932 | } |
| 1933 | } |
| 1934 | |
| 1935 | @test unsafe fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) { |
| 1936 | { |
| 1937 | let mut testArena85 = testArena(); |
| 1938 | let testStorage85: 'test85 = &mut testArena85 in { |
| 1939 | let mut a = testResolver(testStorage85); |
| 1940 | let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }"; |
| 1941 | let result = try resolveProgramStr(&mut a, program); |
| 1942 | try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None")); |
| 1943 | } |
| 1944 | } { |
| 1945 | let mut testArena86 = testArena(); |
| 1946 | let testStorage86: 'test86 = &mut testArena86 in { |
| 1947 | let mut a = testResolver(testStorage86); |
| 1948 | let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }"; |
| 1949 | let result = try resolveProgramStr(&mut a, program); |
| 1950 | try expectNoErrors(&result); |
| 1951 | } |
| 1952 | } |
| 1953 | } |
| 1954 | |
| 1955 | @test unsafe fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) { |
| 1956 | let mut testArena87 = testArena(); |
| 1957 | let testStorage87: 'test87 = &mut testArena87 in { |
| 1958 | let mut a = testResolver(testStorage87); |
| 1959 | let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }"; |
| 1960 | let result = try resolveProgramStr(&mut a, program); |
| 1961 | try expectNoErrors(&result); |
| 1962 | } |
| 1963 | } |
| 1964 | |
| 1965 | @test unsafe fn testResolveMatchUnionBindingScope() throws (testing::TestError) { |
| 1966 | let mut testArena88 = testArena(); |
| 1967 | let testStorage88: 'test88 = &mut testArena88 in { |
| 1968 | let mut a = testResolver(testStorage88); |
| 1969 | let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }"; |
| 1970 | let result = try resolveProgramStr(&mut a, program); |
| 1971 | try expectNoErrors(&result); |
| 1972 | |
| 1973 | let fnBlock = try getFnBody(&a, result.root, "f"); |
| 1974 | try testing::expect(fnBlock.statements.len > 0); |
| 1975 | |
| 1976 | let matchNode = fnBlock.statements[0]; |
| 1977 | let case ast::NodeValue::Match(sw) = matchNode.value |
| 1978 | else throw testing::TestError::Failed; |
| 1979 | let caseNode = sw.prongs[0]; |
| 1980 | |
| 1981 | let scope = super::scopeFor(&a, caseNode) |
| 1982 | else throw testing::TestError::Failed; |
| 1983 | let payloadSym = super::findSymbolInScope(scope, "x") |
| 1984 | else throw testing::TestError::Failed; |
| 1985 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
| 1986 | else throw testing::TestError::Failed; |
| 1987 | try testing::expect(payloadValType == super::Type::I32); |
| 1988 | } |
| 1989 | } |
| 1990 | |
| 1991 | @test unsafe fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) { |
| 1992 | let mut testArena89 = testArena(); |
| 1993 | let testStorage89: 'test89 = &mut testArena89 in { |
| 1994 | let mut a = testResolver(testStorage89); |
| 1995 | let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }"; |
| 1996 | let result = try resolveProgramStr(&mut a, program); |
| 1997 | let err = try expectError(&result); |
| 1998 | let optionTy = try getTypeInScopeOf(&a, result.root, "Opt"); |
| 1999 | try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool); |
| 2000 | } |
| 2001 | } |
| 2002 | |
| 2003 | @test unsafe fn testResolveMatchGuardForms() throws (testing::TestError) { |
| 2004 | let mut testArena90 = testArena(); |
| 2005 | let testStorage90: 'test90 = &mut testArena90 in { |
| 2006 | let mut a = testResolver(testStorage90); |
| 2007 | let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }"; |
| 2008 | let result = try resolveProgramStr(&mut a, program); |
| 2009 | try expectNoErrors(&result); |
| 2010 | } |
| 2011 | } |
| 2012 | |
| 2013 | /// Expected lowering metadata for a resolved match. |
| 2014 | record MatchMetadataCase: Copy { |
| 2015 | /// Complete source program with a function named f. |
| 2016 | source: *[u8], |
| 2017 | /// Bit set when lowering can omit a prong's pattern test. |
| 2018 | catchAllMask: u32, |
| 2019 | /// Whether scalar cases can use switch lowering. |
| 2020 | isConst: bool, |
| 2021 | } |
| 2022 | |
| 2023 | /// Guards and subject types determine unconditional-prong metadata. |
| 2024 | @test unsafe fn testMatchCatchAllMetadata() throws (testing::TestError) { |
| 2025 | let cases = [ |
| 2026 | MatchMetadataCase { source: "fn f(x: u32) { match x { case 1 => {}, else => {} } }", catchAllMask: 2, isConst: true }, |
| 2027 | MatchMetadataCase { source: "fn f(x: u32) { match x { n if n > 0 => {}, case _ => {} } }", catchAllMask: 2, isConst: false }, |
| 2028 | MatchMetadataCase { source: "fn f(x: u32) { match x { case _ if x > 0 => {}, n => {} } }", catchAllMask: 2, isConst: false }, |
| 2029 | MatchMetadataCase { source: "fn f(x: u32) { match x { n => {} } }", catchAllMask: 1, isConst: false }, |
| 2030 | MatchMetadataCase { source: "fn f(x: [[u32; 2]; 2]) { match x { case [[1, a], [b, c]] => {}, case [[a, b], [c, d]] => {} } }", catchAllMask: 2, isConst: false }, |
| 2031 | MatchMetadataCase { source: "union U: Copy { A, B } fn f(x: U) { match x { case U::A => {}, else => {} } }", catchAllMask: 2, isConst: false }, |
| 2032 | MatchMetadataCase { source: "fn f(x: ?u32) { match x { n => {}, case nil => {} } }", catchAllMask: 2, isConst: false }, |
| 2033 | MatchMetadataCase { source: "fn f(x: ?u32) { match x { case _ if false => {}, else => {} } }", catchAllMask: 2, isConst: false }, |
| 2034 | MatchMetadataCase { source: "fn f(x: ?u32) { match x { case _ => {} } }", catchAllMask: 1, isConst: false }, |
| 2035 | MatchMetadataCase { source: "fn f(x: bool) { match x { case true => {}, case false => {} } }", catchAllMask: 2, isConst: true }, |
| 2036 | ]; |
| 2037 | for item in cases { |
| 2038 | let mut arena = testArena(); |
| 2039 | let storage: 'test = &mut arena in { |
| 2040 | let mut res = testResolver(storage); |
| 2041 | let result = try resolveProgramStr(&mut res, item.source); |
| 2042 | try expectNoErrors(&result); |
| 2043 | let body = try getFnBody(&res, result.root, "f"); |
| 2044 | let node = body.statements[0]; |
| 2045 | let case ast::NodeValue::Match(sw) = node.value else throw testing::TestError::Failed; |
| 2046 | for prong, i in sw.prongs { |
| 2047 | assert super::isProngCatchAll(&res, prong) == ((item.catchAllMask & (1 << i)) <> 0); |
| 2048 | } |
| 2049 | assert super::isMatchConst(&res, node) == item.isConst; |
| 2050 | } |
| 2051 | } |
| 2052 | } |
| 2053 | |
| 2054 | /// Duplicate unconditional arms are rejected for each match subject category. |
| 2055 | @test unsafe fn testMatchDuplicateCatchAll() throws (testing::TestError) { |
| 2056 | for source in [ |
| 2057 | "fn f(x: u32) { match x { case _ => {}, else => {} } }", |
| 2058 | "fn f(x: u32) { match x { n => {}, case _ => {} } }", |
| 2059 | "fn f(x: [[u32; 2]; 2]) { match x { case [[a, b], [c, d]] => {}, else => {} } }", |
| 2060 | "union U: Copy { A, B } fn f(x: U) { match x { n => {}, else => {} } }", |
| 2061 | "fn f(x: ?u32) { match x { case _ => {}, else => {} } }", |
| 2062 | ] { |
| 2063 | let mut arena = testArena(); |
| 2064 | let storage: 'test = &mut arena in { |
| 2065 | let mut res = testResolver(storage); |
| 2066 | let result = try resolveProgramStr(&mut res, source); |
| 2067 | try expectErrorKind(&result, super::ErrorKind::DuplicateCatchAll); |
| 2068 | } |
| 2069 | } |
| 2070 | } |
| 2071 | |
| 2072 | /// Test that a binding prong binds the subject to the identifier. |
| 2073 | @test unsafe fn testResolveMatchBindingProng() throws (testing::TestError) { |
| 2074 | let mut testArena91 = testArena(); |
| 2075 | let testStorage91: 'test91 = &mut testArena91 in { |
| 2076 | let mut a = testResolver(testStorage91); |
| 2077 | let program = "fn f(value: i32) -> i32 { match value { x => return x } }"; |
| 2078 | let result = try resolveProgramStr(&mut a, program); |
| 2079 | try expectNoErrors(&result); |
| 2080 | } |
| 2081 | } |
| 2082 | |
| 2083 | /// Test that a binding prong with guard can use the bound variable. |
| 2084 | @test unsafe fn testResolveMatchBindingProngGuard() throws (testing::TestError) { |
| 2085 | let mut testArena92 = testArena(); |
| 2086 | let testStorage92: 'test92 = &mut testArena92 in { |
| 2087 | let mut a = testResolver(testStorage92); |
| 2088 | let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }"; |
| 2089 | let result = try resolveProgramStr(&mut a, program); |
| 2090 | try expectNoErrors(&result); |
| 2091 | } |
| 2092 | } |
| 2093 | |
| 2094 | /// Test that a binding prong covers all union variants for exhaustiveness. |
| 2095 | @test unsafe fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) { |
| 2096 | let mut testArena93 = testArena(); |
| 2097 | let testStorage93: 'test93 = &mut testArena93 in { |
| 2098 | let mut a = testResolver(testStorage93); |
| 2099 | let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }"; |
| 2100 | let result = try resolveProgramStr(&mut a, program); |
| 2101 | try expectNoErrors(&result); |
| 2102 | } |
| 2103 | } |
| 2104 | |
| 2105 | /// Test that `case x =>` fails if `x` is not in scope, since bare identifiers |
| 2106 | /// in case patterns are values to compare against, not bindings. |
| 2107 | @test unsafe fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) { |
| 2108 | let mut testArena94 = testArena(); |
| 2109 | let testStorage94: 'test94 = &mut testArena94 in { |
| 2110 | let mut a = testResolver(testStorage94); |
| 2111 | let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }"; |
| 2112 | let result = try resolveProgramStr(&mut a, program); |
| 2113 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
| 2114 | } |
| 2115 | } |
| 2116 | |
| 2117 | /// Test matching on optionals: exhaustiveness and type unwrapping. |
| 2118 | @test unsafe fn testResolveMatchOptional() throws (testing::TestError) { |
| 2119 | { |
| 2120 | // Exhaustive: binding + nil case. |
| 2121 | let mut testArena95 = testArena(); |
| 2122 | let testStorage95: 'test95 = &mut testArena95 in { |
| 2123 | let mut a = testResolver(testStorage95); |
| 2124 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }"; |
| 2125 | let result = try resolveProgramStr(&mut a, program); |
| 2126 | try expectNoErrors(&result); |
| 2127 | } |
| 2128 | } { |
| 2129 | // Missing nil case. |
| 2130 | let mut testArena96 = testArena(); |
| 2131 | let testStorage96: 'test96 = &mut testArena96 in { |
| 2132 | let mut a = testResolver(testStorage96); |
| 2133 | let program = "fn f(opt: ?i32) { match opt { v => {} } }"; |
| 2134 | let result = try resolveProgramStr(&mut a, program); |
| 2135 | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil); |
| 2136 | } |
| 2137 | } { |
| 2138 | // Missing value case. |
| 2139 | let mut testArena97 = testArena(); |
| 2140 | let testStorage97: 'test97 = &mut testArena97 in { |
| 2141 | let mut a = testResolver(testStorage97); |
| 2142 | let program = "fn f(opt: ?i32) { match opt { case nil => {} } }"; |
| 2143 | let result = try resolveProgramStr(&mut a, program); |
| 2144 | try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue); |
| 2145 | } |
| 2146 | } { |
| 2147 | // Else covers both cases. |
| 2148 | let mut testArena98 = testArena(); |
| 2149 | let testStorage98: 'test98 = &mut testArena98 in { |
| 2150 | let mut a = testResolver(testStorage98); |
| 2151 | let program = "fn f(opt: ?i32) { match opt { else => {} } }"; |
| 2152 | let result = try resolveProgramStr(&mut a, program); |
| 2153 | try expectNoErrors(&result); |
| 2154 | } |
| 2155 | } { |
| 2156 | // Binding unwraps the inner type. |
| 2157 | let mut testArena99 = testArena(); |
| 2158 | let testStorage99: 'test99 = &mut testArena99 in { |
| 2159 | let mut a = testResolver(testStorage99); |
| 2160 | let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }"; |
| 2161 | let result = try resolveProgramStr(&mut a, program); |
| 2162 | try expectNoErrors(&result); |
| 2163 | } |
| 2164 | } |
| 2165 | } |
| 2166 | |
| 2167 | /// Test that match on non-union types requires exhaustiveness. |
| 2168 | @test unsafe fn testResolveMatchGenericExhaustive() throws (testing::TestError) { |
| 2169 | { |
| 2170 | // Match on i32 without catch-all should error. |
| 2171 | let mut testArena100 = testArena(); |
| 2172 | let testStorage100: 'test100 = &mut testArena100 in { |
| 2173 | let mut a = testResolver(testStorage100); |
| 2174 | let program = "fn f(x: i32) { match x { case 1 => {} } }"; |
| 2175 | let result = try resolveProgramStr(&mut a, program); |
| 2176 | try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive); |
| 2177 | } |
| 2178 | } { |
| 2179 | // Match on i32 with else is fine. |
| 2180 | let mut testArena101 = testArena(); |
| 2181 | let testStorage101: 'test101 = &mut testArena101 in { |
| 2182 | let mut a = testResolver(testStorage101); |
| 2183 | let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }"; |
| 2184 | let result = try resolveProgramStr(&mut a, program); |
| 2185 | try expectNoErrors(&result); |
| 2186 | } |
| 2187 | } { |
| 2188 | // Match on i32 with binding catch-all is fine. |
| 2189 | let mut testArena102 = testArena(); |
| 2190 | let testStorage102: 'test102 = &mut testArena102 in { |
| 2191 | let mut a = testResolver(testStorage102); |
| 2192 | let program = "fn f(x: i32) { match x { y => {} } }"; |
| 2193 | let result = try resolveProgramStr(&mut a, program); |
| 2194 | try expectNoErrors(&result); |
| 2195 | } |
| 2196 | } { |
| 2197 | // Match on i32 with wildcard catch-all is fine. |
| 2198 | let mut testArena103 = testArena(); |
| 2199 | let testStorage103: 'test103 = &mut testArena103 in { |
| 2200 | let mut a = testResolver(testStorage103); |
| 2201 | let program = "fn f(x: i32) { match x { case _ => {} } }"; |
| 2202 | let result = try resolveProgramStr(&mut a, program); |
| 2203 | try expectNoErrors(&result); |
| 2204 | } |
| 2205 | } |
| 2206 | } |
| 2207 | |
| 2208 | /// Test that match on bool requires both true and false cases. |
| 2209 | @test unsafe fn testResolveMatchBoolExhaustive() throws (testing::TestError) { |
| 2210 | { |
| 2211 | // Match on bool with both cases is fine. |
| 2212 | let mut testArena104 = testArena(); |
| 2213 | let testStorage104: 'test104 = &mut testArena104 in { |
| 2214 | let mut a = testResolver(testStorage104); |
| 2215 | let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }"; |
| 2216 | let result = try resolveProgramStr(&mut a, program); |
| 2217 | try expectNoErrors(&result); |
| 2218 | } |
| 2219 | } { |
| 2220 | // Match on bool missing true should error. |
| 2221 | let mut testArena105 = testArena(); |
| 2222 | let testStorage105: 'test105 = &mut testArena105 in { |
| 2223 | let mut a = testResolver(testStorage105); |
| 2224 | let program = "fn f(x: bool) { match x { case false => {} } }"; |
| 2225 | let result = try resolveProgramStr(&mut a, program); |
| 2226 | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true)); |
| 2227 | } |
| 2228 | } { |
| 2229 | // Match on bool missing false should error. |
| 2230 | let mut testArena106 = testArena(); |
| 2231 | let testStorage106: 'test106 = &mut testArena106 in { |
| 2232 | let mut a = testResolver(testStorage106); |
| 2233 | let program = "fn f(x: bool) { match x { case true => {} } }"; |
| 2234 | let result = try resolveProgramStr(&mut a, program); |
| 2235 | try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false)); |
| 2236 | } |
| 2237 | } { |
| 2238 | // Match on bool with else is fine. |
| 2239 | let mut testArena107 = testArena(); |
| 2240 | let testStorage107: 'test107 = &mut testArena107 in { |
| 2241 | let mut a = testResolver(testStorage107); |
| 2242 | let program = "fn f(x: bool) { match x { else => {} } }"; |
| 2243 | let result = try resolveProgramStr(&mut a, program); |
| 2244 | try expectNoErrors(&result); |
| 2245 | } |
| 2246 | } { |
| 2247 | // Match on bool with binding catch-all is fine. |
| 2248 | let mut testArena108 = testArena(); |
| 2249 | let testStorage108: 'test108 = &mut testArena108 in { |
| 2250 | let mut a = testResolver(testStorage108); |
| 2251 | let program = "fn f(x: bool) { match x { b => {} } }"; |
| 2252 | let result = try resolveProgramStr(&mut a, program); |
| 2253 | try expectNoErrors(&result); |
| 2254 | } |
| 2255 | } |
| 2256 | } |
| 2257 | |
| 2258 | @test unsafe fn testResolveFnTypeVoidNoParams() throws (testing::TestError) { |
| 2259 | let mut testArena113 = testArena(); |
| 2260 | let testStorage113: 'test113 = &mut testArena113 in { |
| 2261 | let mut a = testResolver(testStorage113); |
| 2262 | let result = try resolveProgramStr(&mut a, "fn f() {} f();"); |
| 2263 | try expectNoErrors(&result); |
| 2264 | |
| 2265 | let blockNode = result.root; |
| 2266 | let case ast::NodeValue::Block(block) = blockNode.value |
| 2267 | else throw testing::TestError::Failed; |
| 2268 | let fnNode = try getBlockStmt(blockNode, 0); |
| 2269 | let callStmt = try getBlockStmt(blockNode, 1); |
| 2270 | |
| 2271 | { // Verify the function symbol captures an empty parameter list and void return. |
| 2272 | let sym = super::symbolFor(&a, fnNode) |
| 2273 | else throw testing::TestError::Failed; |
| 2274 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
| 2275 | else throw testing::TestError::Failed; |
| 2276 | try testing::expect(fnTy.paramTypes.len == 0); |
| 2277 | try testing::expect(*fnTy.returnType == super::Type::Void); |
| 2278 | } |
| 2279 | { // Checking that the type of the call matches the function return type. |
| 2280 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
| 2281 | |
| 2282 | let fnSym = super::symbolFor(&a, fnNode) |
| 2283 | else throw testing::TestError::Failed; |
| 2284 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
| 2285 | else throw testing::TestError::Failed; |
| 2286 | try expectType(&a, callExpr, *fnTy.returnType); |
| 2287 | } |
| 2288 | } |
| 2289 | } |
| 2290 | |
| 2291 | @test unsafe fn testResolveFnTypeReturnsValue() throws (testing::TestError) { |
| 2292 | let mut testArena114 = testArena(); |
| 2293 | let testStorage114: 'test114 = &mut testArena114 in { |
| 2294 | let mut a = testResolver(testStorage114); |
| 2295 | let program = "fn f() -> i32 { return 1; } f();"; |
| 2296 | let result = try resolveProgramStr(&mut a, program); |
| 2297 | try expectNoErrors(&result); |
| 2298 | |
| 2299 | let blockNode = result.root; |
| 2300 | let case ast::NodeValue::Block(block) = blockNode.value |
| 2301 | else throw testing::TestError::Failed; |
| 2302 | let fnNode = try getBlockStmt(blockNode, 0); |
| 2303 | let callStmt = try getBlockStmt(blockNode, 1); |
| 2304 | |
| 2305 | { // Function returns i32 with no parameters. |
| 2306 | let sym = super::symbolFor(&a, fnNode) |
| 2307 | else throw testing::TestError::Failed; |
| 2308 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
| 2309 | else throw testing::TestError::Failed; |
| 2310 | try testing::expect(fnTy.paramTypes.len == 0); |
| 2311 | try testing::expect(*fnTy.returnType == super::Type::I32); |
| 2312 | } |
| 2313 | { // Call expression should inherit the function's return type. |
| 2314 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32); |
| 2315 | |
| 2316 | let fnSym = super::symbolFor(&a, fnNode) |
| 2317 | else throw testing::TestError::Failed; |
| 2318 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
| 2319 | else throw testing::TestError::Failed; |
| 2320 | try expectType(&a, callExpr, *fnTy.returnType); |
| 2321 | } |
| 2322 | } |
| 2323 | } |
| 2324 | |
| 2325 | @test unsafe fn testResolveFnTypeSingleParam() throws (testing::TestError) { |
| 2326 | let mut testArena115 = testArena(); |
| 2327 | let testStorage115: 'test115 = &mut testArena115 in { |
| 2328 | let mut a = testResolver(testStorage115); |
| 2329 | let program = "fn f(x: i8) {} let x: i8 = 1; f(x);"; |
| 2330 | let result = try resolveProgramStr(&mut a, program); |
| 2331 | try expectNoErrors(&result); |
| 2332 | |
| 2333 | let blockNode = result.root; |
| 2334 | let case ast::NodeValue::Block(block) = blockNode.value |
| 2335 | else throw testing::TestError::Failed; |
| 2336 | let fnNode = try getBlockStmt(blockNode, 0); |
| 2337 | let callStmt = try getBlockStmt(blockNode, 2); |
| 2338 | |
| 2339 | { // Single parameter propagates nominal type onto the symbol and parameter node. |
| 2340 | let sym = super::symbolFor(&a, fnNode) |
| 2341 | else throw testing::TestError::Failed; |
| 2342 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
| 2343 | else throw testing::TestError::Failed; |
| 2344 | try testing::expect(fnTy.paramTypes.len == 1); |
| 2345 | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
| 2346 | try testing::expect(*fnTy.returnType == super::Type::Void); |
| 2347 | |
| 2348 | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
| 2349 | else throw testing::TestError::Failed; |
| 2350 | try testing::expect(fnDecl.sig.params.len == 1); |
| 2351 | |
| 2352 | let paramNode = fnDecl.sig.params[0]; |
| 2353 | try expectType(&a, paramNode, super::Type::I8); |
| 2354 | } |
| 2355 | { // Call should resolve to void, matching the function's return type. |
| 2356 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
| 2357 | let fnSym = super::symbolFor(&a, fnNode) |
| 2358 | else throw testing::TestError::Failed; |
| 2359 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
| 2360 | else throw testing::TestError::Failed; |
| 2361 | try expectType(&a, callExpr, *fnTy.returnType); |
| 2362 | } |
| 2363 | } |
| 2364 | } |
| 2365 | |
| 2366 | @test unsafe fn testResolveFnTypeMultipleParams() throws (testing::TestError) { |
| 2367 | let mut testArena116 = testArena(); |
| 2368 | let testStorage116: 'test116 = &mut testArena116 in { |
| 2369 | let mut a = testResolver(testStorage116); |
| 2370 | let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);"; |
| 2371 | let result = try resolveProgramStr(&mut a, program); |
| 2372 | try expectNoErrors(&result); |
| 2373 | |
| 2374 | let blockNode = result.root; |
| 2375 | let case ast::NodeValue::Block(block) = blockNode.value |
| 2376 | else throw testing::TestError::Failed; |
| 2377 | let fnNode = try getBlockStmt(blockNode, 0); |
| 2378 | let callStmt = try getBlockStmt(blockNode, 3); |
| 2379 | |
| 2380 | { // Ensure multi-parameter signatures record both argument types. |
| 2381 | let sym = super::symbolFor(&a, fnNode) |
| 2382 | else throw testing::TestError::Failed; |
| 2383 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
| 2384 | else throw testing::TestError::Failed; |
| 2385 | try testing::expect(fnTy.paramTypes.len == 2); |
| 2386 | try testing::expect(*fnTy.paramTypes[0] == super::Type::I8); |
| 2387 | try testing::expect(*fnTy.paramTypes[1] == super::Type::I32); |
| 2388 | try testing::expect(*fnTy.returnType == super::Type::Void); |
| 2389 | |
| 2390 | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value |
| 2391 | else throw testing::TestError::Failed; |
| 2392 | try testing::expect(fnDecl.sig.params.len == 2); |
| 2393 | |
| 2394 | let firstParam = fnDecl.sig.params[0]; |
| 2395 | let secondParam = fnDecl.sig.params[1]; |
| 2396 | try expectType(&a, firstParam, super::Type::I8); |
| 2397 | try expectType(&a, secondParam, super::Type::I32); |
| 2398 | } |
| 2399 | { // Call expression should again mirror the function return type. |
| 2400 | let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void); |
| 2401 | let fnSym = super::symbolFor(&a, fnNode) |
| 2402 | else throw testing::TestError::Failed; |
| 2403 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data |
| 2404 | else throw testing::TestError::Failed; |
| 2405 | try expectType(&a, callExpr, *fnTy.returnType); |
| 2406 | } |
| 2407 | } |
| 2408 | } |
| 2409 | |
| 2410 | @test unsafe fn testResolveFnRecursiveCall() throws (testing::TestError) { |
| 2411 | let mut testArena117 = testArena(); |
| 2412 | let testStorage117: 'test117 = &mut testArena117 in { |
| 2413 | let mut a = testResolver(testStorage117); |
| 2414 | let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }"; |
| 2415 | let result = try resolveProgramStr(&mut a, program); |
| 2416 | try expectNoErrors(&result); |
| 2417 | |
| 2418 | let blockNode = result.root; |
| 2419 | let case ast::NodeValue::Block(block) = blockNode.value |
| 2420 | else throw testing::TestError::Failed; |
| 2421 | let fnNode = try getBlockStmt(blockNode, 0); |
| 2422 | |
| 2423 | { // Function symbol should be visible for recursive calls within its own body. |
| 2424 | let sym = super::symbolFor(&a, fnNode) |
| 2425 | else throw testing::TestError::Failed; |
| 2426 | let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data |
| 2427 | else throw testing::TestError::Failed; |
| 2428 | try testing::expect(fnTy.paramTypes.len == 1); |
| 2429 | try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool); |
| 2430 | try testing::expect(*fnTy.returnType == super::Type::Bool); |
| 2431 | } |
| 2432 | } |
| 2433 | } |
| 2434 | |
| 2435 | @test unsafe fn testResolveFnCallMissingArgument() throws (testing::TestError) { |
| 2436 | let mut testArena118 = testArena(); |
| 2437 | let testStorage118: 'test118 = &mut testArena118 in { |
| 2438 | let mut a = testResolver(testStorage118); |
| 2439 | let program = "fn f(x: i8) {} f();"; |
| 2440 | let result = try resolveProgramStr(&mut a, program); |
| 2441 | // Expect an error when a required parameter is omitted. |
| 2442 | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
| 2443 | expected: 1, |
| 2444 | actual: 0, |
| 2445 | })); |
| 2446 | } |
| 2447 | } |
| 2448 | |
| 2449 | @test unsafe fn testResolveFnCallExtraArgument() throws (testing::TestError) { |
| 2450 | let mut testArena119 = testArena(); |
| 2451 | let testStorage119: 'test119 = &mut testArena119 in { |
| 2452 | let mut a = testResolver(testStorage119); |
| 2453 | let program = "fn f() {} f(1);"; |
| 2454 | let result = try resolveProgramStr(&mut a, program); |
| 2455 | // Passing more arguments than declared should fail. |
| 2456 | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
| 2457 | expected: 0, |
| 2458 | actual: 1, |
| 2459 | })); |
| 2460 | } |
| 2461 | } |
| 2462 | |
| 2463 | /// Invalid calls in function bodies retain their resolution diagnostics. |
| 2464 | @test unsafe fn testInvalidBodyCalls() throws (testing::TestError) { |
| 2465 | for program in &[ |
| 2466 | "fn f(x: u32) {} fn g() { f(1, 2); }", |
| 2467 | "fn f(x: u32) {} fn g() { if true { f(1, 2); } }", |
| 2468 | "fn f(x: u32) {} unsafe fn g() { f(1, 2); }", |
| 2469 | ] { |
| 2470 | let mut arena = testArena(); |
| 2471 | let storage: 'test = &mut arena in { |
| 2472 | let mut res = testResolver(storage); |
| 2473 | let result = try resolveProgramStr(&mut res, program); |
| 2474 | try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch { |
| 2475 | expected: 1, actual: 2, |
| 2476 | })); |
| 2477 | } |
| 2478 | } |
| 2479 | } |
| 2480 | |
| 2481 | /// Unsafe calls with excess arguments report the unsafe-call diagnostic. |
| 2482 | @test unsafe fn testInvalidUnsafeBodyCall() throws (testing::TestError) { |
| 2483 | let mut arena = testArena(); |
| 2484 | let storage: 'test = &mut arena in { |
| 2485 | let mut res = testResolver(storage); |
| 2486 | let result = try resolveProgramStr(&mut res, |
| 2487 | "unsafe fn f(x: u32) {} fn g() { f(1, 2); }"); |
| 2488 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 2489 | } |
| 2490 | } |
| 2491 | |
| 2492 | @test unsafe fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) { |
| 2493 | let mut testArena120 = testArena(); |
| 2494 | let testStorage120: 'test120 = &mut testArena120 in { |
| 2495 | let mut a = testResolver(testStorage120); |
| 2496 | let program = "fn f(x: i8) {} f(true);"; |
| 2497 | let result = try resolveProgramStr(&mut a, program); |
| 2498 | let err = try expectError(&result); |
| 2499 | // The argument type (bool) should not match the parameter type (i8). |
| 2500 | try expectTypeMismatch(err, super::Type::I8, super::Type::Bool); |
| 2501 | } |
| 2502 | } |
| 2503 | |
| 2504 | @test unsafe fn testResolveFnReturnTypeMismatch() throws (testing::TestError) { |
| 2505 | let mut testArena121 = testArena(); |
| 2506 | let testStorage121: 'test121 = &mut testArena121 in { |
| 2507 | let mut a = testResolver(testStorage121); |
| 2508 | let program = "fn f() -> i32 { return true; }"; |
| 2509 | let result = try resolveProgramStr(&mut a, program); |
| 2510 | let err = try expectError(&result); |
| 2511 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 2512 | } |
| 2513 | } |
| 2514 | |
| 2515 | @test unsafe fn testResolveFnReturnVoid() throws (testing::TestError) { |
| 2516 | { |
| 2517 | let mut testArena122 = testArena(); |
| 2518 | let testStorage122: 'test122 = &mut testArena122 in { |
| 2519 | let mut a = testResolver(testStorage122); |
| 2520 | let result = try resolveProgramStr(&mut a, "fn f() { return; }"); |
| 2521 | try expectNoErrors(&result); |
| 2522 | } |
| 2523 | } { |
| 2524 | let mut testArena123 = testArena(); |
| 2525 | let testStorage123: 'test123 = &mut testArena123 in { |
| 2526 | let mut a = testResolver(testStorage123); |
| 2527 | let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }"); |
| 2528 | let err = try expectError(&result); |
| 2529 | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
| 2530 | } |
| 2531 | } |
| 2532 | } |
| 2533 | |
| 2534 | @test unsafe fn testResolveFnMissingReturn() throws (testing::TestError) { |
| 2535 | { |
| 2536 | let mut testArena124 = testArena(); |
| 2537 | let testStorage124: 'test124 = &mut testArena124 in { |
| 2538 | let mut a = testResolver(testStorage124); |
| 2539 | let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}"); |
| 2540 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
| 2541 | } |
| 2542 | } { |
| 2543 | let mut testArena125 = testArena(); |
| 2544 | let testStorage125: 'test125 = &mut testArena125 in { |
| 2545 | let mut a = testResolver(testStorage125); |
| 2546 | let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }"; |
| 2547 | let result = try resolveProgramStr(&mut a, program); |
| 2548 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
| 2549 | } |
| 2550 | } |
| 2551 | } |
| 2552 | |
| 2553 | /// Never-returning functions require divergence on every path. |
| 2554 | @test unsafe fn testResolveNeverReturn() throws (testing::TestError) { |
| 2555 | for program in &[ |
| 2556 | "fn stop() -> ! { panic; } fn f() -> i32 { stop(); }", |
| 2557 | "fn stop() -> ! { panic; } fn f() -> ! { return stop(); }", |
| 2558 | "fn f(callback: fn() -> !) -> ! { callback(); }", |
| 2559 | "fn stop() -> ! { panic; } fn f() -> ! { let _ = stop(); }", |
| 2560 | "fn stop() -> ! { panic; } fn f() -> i32 { let value: i32 = stop(); }", |
| 2561 | "union Token: Once { Held(u32) } fn stop() -> ! { panic; } fn f(token: Token) -> ! { stop(); }", |
| 2562 | "union Token: Once { Held(u32) } fn stop() -> ! { panic; } fn f(token: Token) -> ! { return stop(); }", |
| 2563 | "union Token: Once { Held(u32) } fn stop() -> ! throws (i32) { throw 1; } fn f(token: Token) -> ! { try! stop(); }", |
| 2564 | "fn f() -> ! { while true {} }", |
| 2565 | "fn f(flag: bool) -> ! { if flag { panic; } else { while true {} } }", |
| 2566 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! throws (i32) { try f(); }", |
| 2567 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> i32 { try f() catch { return 2; }; }", |
| 2568 | ] { |
| 2569 | let mut testArena126 = testArena(); |
| 2570 | let testStorage126: 'test126 = &mut testArena126 in { |
| 2571 | let mut a = testResolver(testStorage126); |
| 2572 | let result = try resolveProgramStr(&mut a, program); |
| 2573 | try expectNoErrors(&result); |
| 2574 | } |
| 2575 | } |
| 2576 | } |
| 2577 | |
| 2578 | /// A never-returning signature rejects any normal completion path. |
| 2579 | @test unsafe fn testResolveNeverFallthrough() throws (testing::TestError) { |
| 2580 | for program in &[ |
| 2581 | "fn f() -> ! {}", |
| 2582 | "fn f(flag: bool) -> ! { if flag { panic; } }", |
| 2583 | "fn f() -> ! { while true { break; } }", |
| 2584 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch {}; }", |
| 2585 | "fn f() -> ! throws (i32) { throw 1; } fn g() -> ! { try f() catch e as i32 {}; }", |
| 2586 | ] { |
| 2587 | let mut testArena127 = testArena(); |
| 2588 | let testStorage127: 'test127 = &mut testArena127 in { |
| 2589 | let mut a = testResolver(testStorage127); |
| 2590 | let result = try resolveProgramStr(&mut a, program); |
| 2591 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
| 2592 | } |
| 2593 | } |
| 2594 | } |
| 2595 | |
| 2596 | /// No value, including undefined, can construct a never return value. |
| 2597 | @test unsafe fn testResolveNeverValue() throws (testing::TestError) { |
| 2598 | for program in &[ |
| 2599 | "fn f() -> ! { return; }", |
| 2600 | "fn f() -> ! { return 1; }", |
| 2601 | "unsafe fn f() -> ! { return undefined; }", |
| 2602 | "fn ordinary() {} fn f() { let callback: fn() -> ! = ordinary; }", |
| 2603 | ] { |
| 2604 | let mut testArena128 = testArena(); |
| 2605 | let testStorage128: 'test128 = &mut testArena128 in { |
| 2606 | let mut a = testResolver(testStorage128); |
| 2607 | let result = try resolveProgramStr(&mut a, program); |
| 2608 | let error = try expectError(&result); |
| 2609 | } |
| 2610 | } |
| 2611 | } |
| 2612 | |
| 2613 | /// Caught errors preserve the caller's exact-use ownership obligations. |
| 2614 | @test unsafe fn testResolveNeverCaughtOwnership() throws (testing::TestError) { |
| 2615 | for program in &[ |
| 2616 | "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { try fail() catch {}; }", |
| 2617 | "union Token: Once { Held(u32) } fn fail() -> ! throws (i32) { throw 1; } fn f(token: Token) { let absent = try? fail(); }", |
| 2618 | ] { |
| 2619 | let mut testArena129 = testArena(); |
| 2620 | let testStorage129: 'test129 = &mut testArena129 in { |
| 2621 | let mut a = testResolver(testStorage129); |
| 2622 | let result = try resolveProgramStr(&mut a, program); |
| 2623 | let error = try expectError(&result); |
| 2624 | let case super::ErrorKind::LinearNotConsumed(_) = error.kind |
| 2625 | else throw testing::TestError::Failed; |
| 2626 | } |
| 2627 | } |
| 2628 | } |
| 2629 | |
| 2630 | @test unsafe fn testResolveFnAllPathsReturn() throws (testing::TestError) { |
| 2631 | let mut testArena130 = testArena(); |
| 2632 | let testStorage130: 'test130 = &mut testArena130 in { |
| 2633 | let mut a = testResolver(testStorage130); |
| 2634 | let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }"; |
| 2635 | let result = try resolveProgramStr(&mut a, program); |
| 2636 | try expectNoErrors(&result); |
| 2637 | } |
| 2638 | } |
| 2639 | |
| 2640 | /// Test that match statements with returns in all branches don't require a |
| 2641 | /// return at the end of the function. |
| 2642 | @test unsafe fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) { |
| 2643 | { |
| 2644 | // Union match with all variants returning. |
| 2645 | let mut testArena131 = testArena(); |
| 2646 | let testStorage131: 'test131 = &mut testArena131 in { |
| 2647 | let mut a = testResolver(testStorage131); |
| 2648 | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }"; |
| 2649 | let result = try resolveProgramStr(&mut a, program); |
| 2650 | try expectNoErrors(&result); |
| 2651 | } |
| 2652 | } { |
| 2653 | // Match with default case where all branches return. |
| 2654 | let mut testArena132 = testArena(); |
| 2655 | let testStorage132: 'test132 = &mut testArena132 in { |
| 2656 | let mut a = testResolver(testStorage132); |
| 2657 | let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }"; |
| 2658 | let result = try resolveProgramStr(&mut a, program); |
| 2659 | try expectNoErrors(&result); |
| 2660 | } |
| 2661 | } { |
| 2662 | // Match where not all branches return should error. |
| 2663 | let mut testArena133 = testArena(); |
| 2664 | let testStorage133: 'test133 = &mut testArena133 in { |
| 2665 | let mut a = testResolver(testStorage133); |
| 2666 | let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }"; |
| 2667 | let result = try resolveProgramStr(&mut a, program); |
| 2668 | try expectErrorKind(&result, super::ErrorKind::FnMissingReturn); |
| 2669 | } |
| 2670 | } |
| 2671 | } |
| 2672 | |
| 2673 | @test unsafe fn testResolveAssign() throws (testing::TestError) { |
| 2674 | { |
| 2675 | let mut testArena134 = testArena(); |
| 2676 | let testStorage134: 'test134 = &mut testArena134 in { |
| 2677 | let mut a = testResolver(testStorage134); |
| 2678 | let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;"); |
| 2679 | try expectNoErrors(&result); |
| 2680 | } |
| 2681 | } { |
| 2682 | let mut testArena135 = testArena(); |
| 2683 | let testStorage135: 'test135 = &mut testArena135 in { |
| 2684 | let mut a = testResolver(testStorage135); |
| 2685 | let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;"); |
| 2686 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 2687 | } |
| 2688 | } { |
| 2689 | let mut testArena136 = testArena(); |
| 2690 | let testStorage136: 'test136 = &mut testArena136 in { |
| 2691 | let mut a = testResolver(testStorage136); |
| 2692 | let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;"); |
| 2693 | let err = try expectError(&result); |
| 2694 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 2695 | } |
| 2696 | } { |
| 2697 | let mut testArena137 = testArena(); |
| 2698 | let testStorage137: 'test137 = &mut testArena137 in { |
| 2699 | let mut a = testResolver(testStorage137); |
| 2700 | let result = try resolveProgramStr(&mut a, "set x = 1;"); |
| 2701 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
| 2702 | } |
| 2703 | } { |
| 2704 | let mut testArena138 = testArena(); |
| 2705 | let testStorage138: 'test138 = &mut testArena138 in { |
| 2706 | let mut a = testResolver(testStorage138); |
| 2707 | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;"); |
| 2708 | try expectNoErrors(&result); |
| 2709 | } |
| 2710 | } { |
| 2711 | let mut testArena139 = testArena(); |
| 2712 | let testStorage139: 'test139 = &mut testArena139 in { |
| 2713 | let mut a = testResolver(testStorage139); |
| 2714 | let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;"); |
| 2715 | try expectNoErrors(&result); |
| 2716 | } |
| 2717 | } |
| 2718 | } |
| 2719 | |
| 2720 | @test unsafe fn testResolveAssignSubscript() throws (testing::TestError) { |
| 2721 | { |
| 2722 | let mut testArena140 = testArena(); |
| 2723 | let testStorage140: 'test140 = &mut testArena140 in { |
| 2724 | let mut a = testResolver(testStorage140); |
| 2725 | let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
| 2726 | let result = try resolveProgramStr(&mut a, program); |
| 2727 | try expectNoErrors(&result); |
| 2728 | } |
| 2729 | } |
| 2730 | { |
| 2731 | let mut testArena141 = testArena(); |
| 2732 | let testStorage141: 'test141 = &mut testArena141 in { |
| 2733 | let mut a = testResolver(testStorage141); |
| 2734 | let program = "static xs: [u8; 2] = [0, 1]; let slice: *mut [u8] = &mut xs[..]; set slice[0] = 1;"; |
| 2735 | let result = try resolveProgramStr(&mut a, program); |
| 2736 | try expectNoErrors(&result); |
| 2737 | } |
| 2738 | } |
| 2739 | { |
| 2740 | let mut testArena142 = testArena(); |
| 2741 | let testStorage142: 'test142 = &mut testArena142 in { |
| 2742 | let mut a = testResolver(testStorage142); |
| 2743 | let program = "static xs: [u8; 2] = [0, 1]; let mut slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
| 2744 | let result = try resolveProgramStr(&mut a, program); |
| 2745 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 2746 | } |
| 2747 | } |
| 2748 | { |
| 2749 | let mut testArena143 = testArena(); |
| 2750 | let testStorage143: 'test143 = &mut testArena143 in { |
| 2751 | let mut a = testResolver(testStorage143); |
| 2752 | let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;"; |
| 2753 | let result = try resolveProgramStr(&mut a, program); |
| 2754 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 2755 | } |
| 2756 | } |
| 2757 | { |
| 2758 | let mut testArena144 = testArena(); |
| 2759 | let testStorage144: 'test144 = &mut testArena144 in { |
| 2760 | let mut a = testResolver(testStorage144); |
| 2761 | let program = "static xs: [u8; 2] = [0, 1]; let slice: *[u8] = &xs[..]; set slice[0] = 1;"; |
| 2762 | let result = try resolveProgramStr(&mut a, program); |
| 2763 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 2764 | } |
| 2765 | } |
| 2766 | } |
| 2767 | |
| 2768 | @test unsafe fn testResolveAssignIntegerLits() throws (testing::TestError) { |
| 2769 | try expectAnalyzeOk("let x: i8 = 127;"); |
| 2770 | try expectAnalyzeOk("let x: i8 = 0x7F;"); |
| 2771 | try expectAnalyzeOk("let x: i8 = -128;"); |
| 2772 | try expectAnalyzeOk("let x: u8 = 255;"); |
| 2773 | try expectAnalyzeOk("let x: u8 = 0b11111111;"); |
| 2774 | try expectAnalyzeOk("let x: i16 = 0x7FFF;"); |
| 2775 | try expectAnalyzeOk("let x: i16 = -32768;"); |
| 2776 | try expectAnalyzeOk("let x: u16 = 0xFFFF;"); |
| 2777 | try expectAnalyzeOk("let x: i32 = 2147483647;"); |
| 2778 | try expectAnalyzeOk("let x: i32 = -2147483648;"); |
| 2779 | try expectAnalyzeOk("let x: u32 = 0xFFFFFFFF;"); |
| 2780 | try expectAnalyzeOk("let x: i64 = 9223372036854775807;"); |
| 2781 | try expectAnalyzeOk("let x: i64 = -9223372036854775808;"); |
| 2782 | |
| 2783 | try expectAnalyzeOk("constant LIMIT: u8 = 0xFF;"); |
| 2784 | |
| 2785 | try expectIntMismatch("let x: i8 = 128;", super::Type::I8); |
| 2786 | try expectIntMismatch("let x: i8 = -129;", super::Type::I8); |
| 2787 | try expectIntMismatch("let x: i8 = 0x80;", super::Type::I8); |
| 2788 | try expectIntMismatch("let x: i8 = 0b10000000;", super::Type::I8); |
| 2789 | try expectIntMismatch("let x: u8 = 256;", super::Type::U8); |
| 2790 | try expectIntMismatch("let x: u8 = -1;", super::Type::U8); |
| 2791 | try expectIntMismatch("let x: u8 = 0b100000000;", super::Type::U8); |
| 2792 | try expectIntMismatch("let x: i16 = 32768;", super::Type::I16); |
| 2793 | try expectIntMismatch("let x: i16 = -32769;", super::Type::I16); |
| 2794 | try expectIntMismatch("let x: u16 = 65536;", super::Type::U16); |
| 2795 | try expectIntMismatch("let x: u16 = -1;", super::Type::U16); |
| 2796 | try expectIntMismatch("let x: i32 = 2147483648;", super::Type::I32); |
| 2797 | try expectIntMismatch("let x: i32 = -2147483649;", super::Type::I32); |
| 2798 | try expectIntMismatch("let x: i32 = 0xFFFFFFFF;", super::Type::I32); |
| 2799 | try expectIntMismatch("let x: u32 = -1;", super::Type::U32); |
| 2800 | try expectIntMismatch("let x: u32 = 0x100000000;", super::Type::U32); |
| 2801 | try expectIntMismatch("let x: i64 = 9223372036854775808;", super::Type::I64); |
| 2802 | try expectIntMismatch("let x: i64 = -9223372036854775809;", super::Type::I64); |
| 2803 | try expectIntMismatch("constant LIMIT: u8 = 512;", super::Type::U8); |
| 2804 | try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8); |
| 2805 | } |
| 2806 | |
| 2807 | @test unsafe fn testNilCoercions() throws (testing::TestError) { |
| 2808 | { |
| 2809 | let mut testArena145 = testArena(); |
| 2810 | let testStorage145: 'test145 = &mut testArena145 in { |
| 2811 | let mut a = testResolver(testStorage145); |
| 2812 | let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;"); |
| 2813 | try expectNoErrors(&result); |
| 2814 | } |
| 2815 | } { |
| 2816 | let mut testArena146 = testArena(); |
| 2817 | let testStorage146: 'test146 = &mut testArena146 in { |
| 2818 | let mut a = testResolver(testStorage146); |
| 2819 | let program = "fn g(opt: ?i32) {} fn f() { g(nil); }"; |
| 2820 | let result = try resolveProgramStr(&mut a, program); |
| 2821 | try expectNoErrors(&result); |
| 2822 | } |
| 2823 | } { |
| 2824 | let mut testArena147 = testArena(); |
| 2825 | let testStorage147: 'test147 = &mut testArena147 in { |
| 2826 | let mut a = testResolver(testStorage147); |
| 2827 | let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }"; |
| 2828 | let result = try resolveProgramStr(&mut a, program); |
| 2829 | try expectNoErrors(&result); |
| 2830 | } |
| 2831 | } |
| 2832 | } |
| 2833 | |
| 2834 | @test unsafe fn testOptionalComparedWithNil() throws (testing::TestError) { |
| 2835 | let mut testArena148 = testArena(); |
| 2836 | let testStorage148: 'test148 = &mut testArena148 in { |
| 2837 | let mut a = testResolver(testStorage148); |
| 2838 | let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;"; |
| 2839 | let result = try resolveBlockStr(&mut a, program); |
| 2840 | try expectNoErrors(&result); |
| 2841 | |
| 2842 | for i in 1..7 { |
| 2843 | let stmt = try getBlockStmt(result.root, i); |
| 2844 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 2845 | } |
| 2846 | } |
| 2847 | } |
| 2848 | |
| 2849 | @test unsafe fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) { |
| 2850 | let mut testArena149 = testArena(); |
| 2851 | let testStorage149: 'test149 = &mut testArena149 in { |
| 2852 | let mut a = testResolver(testStorage149); |
| 2853 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };"; |
| 2854 | let result = try resolveProgramStr(&mut a, program); |
| 2855 | try expectNoErrors(&result); |
| 2856 | } |
| 2857 | } |
| 2858 | |
| 2859 | @test unsafe fn testResolveRecordLiteralMissingField() throws (testing::TestError) { |
| 2860 | let mut testArena150 = testArena(); |
| 2861 | let testStorage150: 'test150 = &mut testArena150 in { |
| 2862 | let mut a = testResolver(testStorage150); |
| 2863 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };"; |
| 2864 | let result = try resolveProgramStr(&mut a, program); |
| 2865 | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
| 2866 | } |
| 2867 | } |
| 2868 | |
| 2869 | @test unsafe fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) { |
| 2870 | let mut testArena151 = testArena(); |
| 2871 | let testStorage151: 'test151 = &mut testArena151 in { |
| 2872 | let mut a = testResolver(testStorage151); |
| 2873 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };"; |
| 2874 | let result = try resolveProgramStr(&mut a, program); |
| 2875 | let err = try expectError(&result); |
| 2876 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 2877 | |
| 2878 | let errNode = err.node |
| 2879 | else throw testing::TestError::Failed; |
| 2880 | let case ast::NodeValue::Bool(_) = errNode.value |
| 2881 | else throw testing::TestError::Failed; |
| 2882 | } |
| 2883 | } |
| 2884 | |
| 2885 | @test unsafe fn testResolveRecordLiteralExtraField() throws (testing::TestError) { |
| 2886 | let mut testArena152 = testArena(); |
| 2887 | let testStorage152: 'test152 = &mut testArena152 in { |
| 2888 | let mut a = testResolver(testStorage152); |
| 2889 | let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };"; |
| 2890 | let result = try resolveProgramStr(&mut a, program); |
| 2891 | let err = try expectError(&result); |
| 2892 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 2893 | else throw testing::TestError::Failed; |
| 2894 | } |
| 2895 | } |
| 2896 | |
| 2897 | /// Test that anonymous record literals with labels can be passed to functions expecting named records. |
| 2898 | @test unsafe fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) { |
| 2899 | let mut testArena153 = testArena(); |
| 2900 | let testStorage153: 'test153 = &mut testArena153 in { |
| 2901 | let mut a = testResolver(testStorage153); |
| 2902 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });"; |
| 2903 | let result = try resolveProgramStr(&mut a, program); |
| 2904 | try expectNoErrors(&result); |
| 2905 | } |
| 2906 | } |
| 2907 | |
| 2908 | /// Test that anonymous record with wrong field name causes out of order error. |
| 2909 | @test unsafe fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) { |
| 2910 | let mut testArena154 = testArena(); |
| 2911 | let testStorage154: 'test154 = &mut testArena154 in { |
| 2912 | let mut a = testResolver(testStorage154); |
| 2913 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });"; |
| 2914 | let result = try resolveProgramStr(&mut a, program); |
| 2915 | let err = try expectError(&result); |
| 2916 | let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind |
| 2917 | else throw testing::TestError::Failed; |
| 2918 | } |
| 2919 | } |
| 2920 | |
| 2921 | /// Test that anonymous record with wrong field type causes type mismatch. |
| 2922 | @test unsafe fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) { |
| 2923 | let mut testArena155 = testArena(); |
| 2924 | let testStorage155: 'test155 = &mut testArena155 in { |
| 2925 | let mut a = testResolver(testStorage155); |
| 2926 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });"; |
| 2927 | let result = try resolveProgramStr(&mut a, program); |
| 2928 | let err = try expectError(&result); |
| 2929 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 2930 | else throw testing::TestError::Failed; |
| 2931 | } |
| 2932 | } |
| 2933 | |
| 2934 | /// Test that anonymous record with missing field causes a missing field error. |
| 2935 | @test unsafe fn testResolveAnonRecordMissingField() throws (testing::TestError) { |
| 2936 | let mut testArena156 = testArena(); |
| 2937 | let testStorage156: 'test156 = &mut testArena156 in { |
| 2938 | let mut a = testResolver(testStorage156); |
| 2939 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });"; |
| 2940 | let result = try resolveProgramStr(&mut a, program); |
| 2941 | try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y")); |
| 2942 | } |
| 2943 | } |
| 2944 | |
| 2945 | /// Test that anonymous record with extra field causes a count mismatch error. |
| 2946 | @test unsafe fn testResolveAnonRecordExtraField() throws (testing::TestError) { |
| 2947 | let mut testArena157 = testArena(); |
| 2948 | let testStorage157: 'test157 = &mut testArena157 in { |
| 2949 | let mut a = testResolver(testStorage157); |
| 2950 | let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });"; |
| 2951 | let result = try resolveProgramStr(&mut a, program); |
| 2952 | let err = try expectError(&result); |
| 2953 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 2954 | else throw testing::TestError::Failed; |
| 2955 | } |
| 2956 | } |
| 2957 | |
| 2958 | /// Test that anonymous record fields can be coerced (e.g., i32 to optional). |
| 2959 | @test unsafe fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) { |
| 2960 | let mut testArena158 = testArena(); |
| 2961 | let testStorage158: 'test158 = &mut testArena158 in { |
| 2962 | let mut a = testResolver(testStorage158); |
| 2963 | let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });"; |
| 2964 | let result = try resolveProgramStr(&mut a, program); |
| 2965 | try expectNoErrors(&result); |
| 2966 | } |
| 2967 | } |
| 2968 | |
| 2969 | /// Test that arrays of anonymous records with labeled fields are allowed. |
| 2970 | @test unsafe fn testResolveAnonRecordArray() throws (testing::TestError) { |
| 2971 | let mut testArena159 = testArena(); |
| 2972 | let testStorage159: 'test159 = &mut testArena159 in { |
| 2973 | let mut a = testResolver(testStorage159); |
| 2974 | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];"; |
| 2975 | let result = try resolveProgramStr(&mut a, program); |
| 2976 | try expectNoErrors(&result); |
| 2977 | } |
| 2978 | } |
| 2979 | |
| 2980 | /// Test that arrays of anonymous records with extra fields cause count mismatch. |
| 2981 | @test unsafe fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) { |
| 2982 | let mut testArena160 = testArena(); |
| 2983 | let testStorage160: 'test160 = &mut testArena160 in { |
| 2984 | let mut a = testResolver(testStorage160); |
| 2985 | let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];"; |
| 2986 | let result = try resolveProgramStr(&mut a, program); |
| 2987 | let err = try expectError(&result); |
| 2988 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 2989 | else throw testing::TestError::Failed; |
| 2990 | } |
| 2991 | } |
| 2992 | |
| 2993 | /// Test that unlabeled record declarations are analyzed correctly. |
| 2994 | @test unsafe fn testResolveUnlabeledRecordDecl() throws (testing::TestError) { |
| 2995 | let mut testArena161 = testArena(); |
| 2996 | let testStorage161: 'test161 = &mut testArena161 in { |
| 2997 | let mut a = testResolver(testStorage161); |
| 2998 | let program = "record R(i32, bool);"; |
| 2999 | let result = try resolveProgramStr(&mut a, program); |
| 3000 | try expectNoErrors(&result); |
| 3001 | |
| 3002 | // Verify the type symbol was created with labeled=false. |
| 3003 | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
| 3004 | let case super::NominalType::Record(recordType) = *nominalTy |
| 3005 | else throw testing::TestError::Failed; |
| 3006 | try testing::expect(not recordType.labeled); |
| 3007 | try testing::expect(recordType.fields.len == 2); |
| 3008 | try testing::expect(recordType.fields[0].name == nil); |
| 3009 | try testing::expect(recordType.fields[1].name == nil); |
| 3010 | } |
| 3011 | } |
| 3012 | |
| 3013 | @test unsafe fn testResolveLabeledRecordDecl() throws (testing::TestError) { |
| 3014 | let mut testArena162 = testArena(); |
| 3015 | let testStorage162: 'test162 = &mut testArena162 in { |
| 3016 | let mut a = testResolver(testStorage162); |
| 3017 | let program = "record R { x: i32, y: i32 }"; |
| 3018 | let result = try resolveProgramStr(&mut a, program); |
| 3019 | try expectNoErrors(&result); |
| 3020 | |
| 3021 | let nominalTy = try getTypeInScopeOf(&a, result.root, "R"); |
| 3022 | let case super::NominalType::Record(recordType) = *nominalTy |
| 3023 | else throw testing::TestError::Failed; |
| 3024 | try testing::expect(recordType.labeled); |
| 3025 | try testing::expect(recordType.fields.len == 2); |
| 3026 | try testing::expect(recordType.fields[0].name <> nil); |
| 3027 | try testing::expect(recordType.fields[1].name <> nil); |
| 3028 | } |
| 3029 | } |
| 3030 | |
| 3031 | @test unsafe fn testResolveRecordFieldAccessValid() throws (testing::TestError) { |
| 3032 | let mut testArena163 = testArena(); |
| 3033 | let testStorage163: 'test163 = &mut testArena163 in { |
| 3034 | let mut a = testResolver(testStorage163); |
| 3035 | let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;"; |
| 3036 | let result = try resolveProgramStr(&mut a, program); |
| 3037 | try expectNoErrors(&result); |
| 3038 | |
| 3039 | let fieldStmt = try getBlockStmt(result.root, 2); |
| 3040 | try expectExprStmtType(&a, fieldStmt, super::Type::U8); |
| 3041 | } |
| 3042 | } |
| 3043 | |
| 3044 | @test unsafe fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) { |
| 3045 | let mut testArena164 = testArena(); |
| 3046 | let testStorage164: 'test164 = &mut testArena164 in { |
| 3047 | let mut a = testResolver(testStorage164); |
| 3048 | let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;"; |
| 3049 | let result = try resolveProgramStr(&mut a, program); |
| 3050 | try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y")); |
| 3051 | } |
| 3052 | } |
| 3053 | |
| 3054 | @test unsafe fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) { |
| 3055 | let mut testArena165 = testArena(); |
| 3056 | let testStorage165: 'test165 = &mut testArena165 in { |
| 3057 | let mut a = testResolver(testStorage165); |
| 3058 | let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;"; |
| 3059 | let result = try resolveProgramStr(&mut a, program); |
| 3060 | try expectNoErrors(&result); |
| 3061 | |
| 3062 | let stmt = try getBlockStmt(result.root, 2); |
| 3063 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3064 | } |
| 3065 | } |
| 3066 | |
| 3067 | @test unsafe fn testResolveRecordFieldAccessChained() throws (testing::TestError) { |
| 3068 | let mut testArena166 = testArena(); |
| 3069 | let testStorage166: 'test166 = &mut testArena166 in { |
| 3070 | let mut a = testResolver(testStorage166); |
| 3071 | let program = "record C { value: i32 } record B { c: C } record A { b: B } let a = A { b: B { c: C { value: 100 } } }; a.b.c.value;"; |
| 3072 | let result = try resolveProgramStr(&mut a, program); |
| 3073 | try expectNoErrors(&result); |
| 3074 | |
| 3075 | let stmt = try getBlockStmt(result.root, 4); |
| 3076 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3077 | } |
| 3078 | } |
| 3079 | |
| 3080 | @test unsafe fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) { |
| 3081 | let mut testArena167 = testArena(); |
| 3082 | let testStorage167: 'test167 = &mut testArena167 in { |
| 3083 | let mut a = testResolver(testStorage167); |
| 3084 | let program = "let x: i32 = 42; x.field;"; |
| 3085 | let result = try resolveBlockStr(&mut a, program); |
| 3086 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
| 3087 | } |
| 3088 | } |
| 3089 | |
| 3090 | @test unsafe fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) { |
| 3091 | let mut testArena168 = testArena(); |
| 3092 | let testStorage168: 'test168 = &mut testArena168 in { |
| 3093 | let mut a = testResolver(testStorage168); |
| 3094 | let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;"; |
| 3095 | let result = try resolveProgramStr(&mut a, program); |
| 3096 | try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field")); |
| 3097 | } |
| 3098 | } |
| 3099 | |
| 3100 | @test unsafe fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) { |
| 3101 | let mut testArena169 = testArena(); |
| 3102 | let testStorage169: 'test169 = &mut testArena169 in { |
| 3103 | let mut a = testResolver(testStorage169); |
| 3104 | let program = "let b: bool = true; b.field;"; |
| 3105 | let result = try resolveProgramStr(&mut a, program); |
| 3106 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
| 3107 | } |
| 3108 | } |
| 3109 | |
| 3110 | @test unsafe fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) { |
| 3111 | let mut testArena170 = testArena(); |
| 3112 | let testStorage170: 'test170 = &mut testArena170 in { |
| 3113 | let mut a = testResolver(testStorage170); |
| 3114 | let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;"; |
| 3115 | let result = try resolveProgramStr(&mut a, program); |
| 3116 | try expectErrorKind(&result, super::ErrorKind::ExpectedRecord); |
| 3117 | } |
| 3118 | } |
| 3119 | |
| 3120 | /// Records may reference themselves through pointers without causing resolution errors. |
| 3121 | @test unsafe fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) { |
| 3122 | let mut testArena171 = testArena(); |
| 3123 | let testStorage171: 'test171 = &mut testArena171 in { |
| 3124 | let mut a = testResolver(testStorage171); |
| 3125 | let program = "record A { next: *A }"; |
| 3126 | let result = try resolveProgramStr(&mut a, program); |
| 3127 | try expectNoErrors(&result); |
| 3128 | } |
| 3129 | } |
| 3130 | |
| 3131 | /// Mutually recursive records should resolve without infinite loops. |
| 3132 | @test unsafe fn testResolveRecordMutuallyRecursive() throws (testing::TestError) { |
| 3133 | let mut testArena172 = testArena(); |
| 3134 | let testStorage172: 'test172 = &mut testArena172 in { |
| 3135 | let mut a = testResolver(testStorage172); |
| 3136 | let program = "record A { b: *B } record B { a: *A }"; |
| 3137 | let result = try resolveProgramStr(&mut a, program); |
| 3138 | try expectNoErrors(&result); |
| 3139 | } |
| 3140 | } |
| 3141 | |
| 3142 | /// Unions may reference themselves through pointers without causing resolution errors. |
| 3143 | @test unsafe fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) { |
| 3144 | let mut testArena173 = testArena(); |
| 3145 | let testStorage173: 'test173 = &mut testArena173 in { |
| 3146 | let mut a = testResolver(testStorage173); |
| 3147 | let program = "union List { Cons(*List), Nil }"; |
| 3148 | let result = try resolveProgramStr(&mut a, program); |
| 3149 | try expectNoErrors(&result); |
| 3150 | } |
| 3151 | } |
| 3152 | |
| 3153 | /// Mutually recursive unions should resolve without infinite loops. |
| 3154 | @test unsafe fn testResolveUnionMutuallyRecursive() throws (testing::TestError) { |
| 3155 | let mut testArena174 = testArena(); |
| 3156 | let testStorage174: 'test174 = &mut testArena174 in { |
| 3157 | let mut a = testResolver(testStorage174); |
| 3158 | let program = "union A { HasB(*B), None } union B { HasA(*A), None }"; |
| 3159 | let result = try resolveProgramStr(&mut a, program); |
| 3160 | try expectNoErrors(&result); |
| 3161 | } |
| 3162 | } |
| 3163 | |
| 3164 | /// Unions with record payloads containing slice references to self should resolve. |
| 3165 | /// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`. |
| 3166 | @test unsafe fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) { |
| 3167 | let mut testArena175 = testArena(); |
| 3168 | let testStorage175: 'test175 = &mut testArena175 in { |
| 3169 | let mut a = testResolver(testStorage175); |
| 3170 | let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }"; |
| 3171 | let result = try resolveProgramStr(&mut a, program); |
| 3172 | try expectNoErrors(&result); |
| 3173 | } |
| 3174 | } |
| 3175 | |
| 3176 | @test unsafe fn testUndefinedCoercions() throws (testing::TestError) { |
| 3177 | { |
| 3178 | let mut testArena176 = testArena(); |
| 3179 | let testStorage176: 'test176 = &mut testArena176 in { |
| 3180 | let mut a = testResolver(testStorage176); |
| 3181 | let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }"); |
| 3182 | try expectNoErrors(&result); |
| 3183 | } |
| 3184 | } { |
| 3185 | let mut testArena177 = testArena(); |
| 3186 | let testStorage177: 'test177 = &mut testArena177 in { |
| 3187 | let mut a = testResolver(testStorage177); |
| 3188 | let program = "unsafe { let mut value: i32 = 0; set value = undefined; }"; |
| 3189 | let result = try resolveProgramStr(&mut a, program); |
| 3190 | try expectNoErrors(&result); |
| 3191 | } |
| 3192 | } { |
| 3193 | let mut testArena178 = testArena(); |
| 3194 | let testStorage178: 'test178 = &mut testArena178 in { |
| 3195 | let mut a = testResolver(testStorage178); |
| 3196 | let program = "fn f(x: i32) {} unsafe fn g() { f(undefined); }"; |
| 3197 | let result = try resolveProgramStr(&mut a, program); |
| 3198 | try expectNoErrors(&result); |
| 3199 | } |
| 3200 | } { |
| 3201 | let mut testArena179 = testArena(); |
| 3202 | let testStorage179: 'test179 = &mut testArena179 in { |
| 3203 | let mut a = testResolver(testStorage179); |
| 3204 | let program = "unsafe fn fetch() -> i32 { return undefined; }"; |
| 3205 | let result = try resolveProgramStr(&mut a, program); |
| 3206 | try expectNoErrors(&result); |
| 3207 | } |
| 3208 | } |
| 3209 | } |
| 3210 | |
| 3211 | @test unsafe fn testResolveBlockVoid() throws (testing::TestError) { |
| 3212 | let mut testArena180 = testArena(); |
| 3213 | let testStorage180: 'test180 = &mut testArena180 in { |
| 3214 | let mut a = testResolver(testStorage180); |
| 3215 | let result = try resolveProgramStr(&mut a, "{ 42; }"); |
| 3216 | try expectNoErrors(&result); |
| 3217 | |
| 3218 | let block = try getBlockStmt(result.root, 0); |
| 3219 | try expectType(&a, block, super::Type::Void); |
| 3220 | } |
| 3221 | } |
| 3222 | |
| 3223 | @test unsafe fn testResolveBlockNever() throws (testing::TestError) { |
| 3224 | let mut testArena181 = testArena(); |
| 3225 | let testStorage181: 'test181 = &mut testArena181 in { |
| 3226 | let mut a = testResolver(testStorage181); |
| 3227 | let result = try resolveProgramStr(&mut a, "{ panic; }"); |
| 3228 | try expectNoErrors(&result); |
| 3229 | |
| 3230 | let block = try getBlockStmt(result.root, 0); |
| 3231 | try expectType(&a, block, super::Type::Never); |
| 3232 | } |
| 3233 | } |
| 3234 | |
| 3235 | @test unsafe fn testResolveIfAllBranchesNever() throws (testing::TestError) { |
| 3236 | let mut testArena182 = testArena(); |
| 3237 | let testStorage182: 'test182 = &mut testArena182 in { |
| 3238 | let mut a = testResolver(testStorage182); |
| 3239 | let program = "if true { panic; } else { panic; }"; |
| 3240 | let result = try resolveProgramStr(&mut a, program); |
| 3241 | try expectNoErrors(&result); |
| 3242 | |
| 3243 | let stmt = try getBlockStmt(result.root, 0); |
| 3244 | try expectType(&a, stmt, super::Type::Never); |
| 3245 | } |
| 3246 | } |
| 3247 | |
| 3248 | @test unsafe fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) { |
| 3249 | let mut testArena183 = testArena(); |
| 3250 | let testStorage183: 'test183 = &mut testArena183 in { |
| 3251 | let mut a = testResolver(testStorage183); |
| 3252 | let program = "if true { panic; } else {}"; |
| 3253 | let result = try resolveProgramStr(&mut a, program); |
| 3254 | try expectNoErrors(&result); |
| 3255 | |
| 3256 | let stmt = try getBlockStmt(result.root, 0); |
| 3257 | try expectType(&a, stmt, super::Type::Void); |
| 3258 | } |
| 3259 | } |
| 3260 | |
| 3261 | @test unsafe fn testResolveLetElse() throws (testing::TestError) { |
| 3262 | let mut testArena184 = testArena(); |
| 3263 | let testStorage184: 'test184 = &mut testArena184 in { |
| 3264 | let mut a = testResolver(testStorage184); |
| 3265 | let program = "let opt: ?i32 = 42; let value = opt else panic; value;"; |
| 3266 | let result = try resolveProgramStr(&mut a, program); |
| 3267 | try expectNoErrors(&result); |
| 3268 | |
| 3269 | let blockNode = result.root; |
| 3270 | let case ast::NodeValue::Block(block) = blockNode.value |
| 3271 | else throw testing::TestError::Failed; |
| 3272 | let letElseNode = try getBlockStmt(blockNode, 1); |
| 3273 | let valueStmt = try getBlockStmt(blockNode, 2); |
| 3274 | |
| 3275 | { // Ensure the bound identifier receives the inner optional type. |
| 3276 | let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32); |
| 3277 | |
| 3278 | let sym = super::symbolFor(&a, valueExpr) |
| 3279 | else throw testing::TestError::Failed; |
| 3280 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
| 3281 | else throw testing::TestError::Failed; |
| 3282 | try testing::expect(valType == super::Type::I32); |
| 3283 | } |
| 3284 | // The let-else statement itself should be typed as void. |
| 3285 | try expectType(&a, letElseNode, super::Type::Void); |
| 3286 | } |
| 3287 | } |
| 3288 | |
| 3289 | @test unsafe fn testResolveLetElseDefaultValue() throws (testing::TestError) { |
| 3290 | let mut testArena185 = testArena(); |
| 3291 | let testStorage185: 'test185 = &mut testArena185 in { |
| 3292 | let mut a = testResolver(testStorage185); |
| 3293 | let program = "let opt: ?i32 = nil; let value = opt else 42; value;"; |
| 3294 | let result = try resolveProgramStr(&mut a, program); |
| 3295 | try expectNoErrors(&result); |
| 3296 | } |
| 3297 | } |
| 3298 | |
| 3299 | @test unsafe fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) { |
| 3300 | let mut testArena186 = testArena(); |
| 3301 | let testStorage186: 'test186 = &mut testArena186 in { |
| 3302 | let mut a = testResolver(testStorage186); |
| 3303 | let program = "let opt: ?i32 = nil; let value = opt else {}; value;"; |
| 3304 | let result = try resolveProgramStr(&mut a, program); |
| 3305 | let err = try expectError(&result); |
| 3306 | try expectTypeMismatch(err, super::Type::I32, super::Type::Void); |
| 3307 | } |
| 3308 | } |
| 3309 | |
| 3310 | @test unsafe fn testResolveLetElseRequiresOptional() throws (testing::TestError) { |
| 3311 | let mut testArena187 = testArena(); |
| 3312 | let testStorage187: 'test187 = &mut testArena187 in { |
| 3313 | let mut a = testResolver(testStorage187); |
| 3314 | let program = "let x: i32 = 42; let value = x else panic;"; |
| 3315 | let result = try resolveProgramStr(&mut a, program); |
| 3316 | try expectErrorKind(&result, super::ErrorKind::ExpectedOptional); |
| 3317 | } |
| 3318 | } |
| 3319 | |
| 3320 | /// Test that `if let mut` produces a mutable binding. |
| 3321 | @test unsafe fn testResolveIfLetMut() throws (testing::TestError) { |
| 3322 | let mut testArena188 = testArena(); |
| 3323 | let testStorage188: 'test188 = &mut testArena188 in { |
| 3324 | let mut a = testResolver(testStorage188); |
| 3325 | let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }"; |
| 3326 | let result = try resolveProgramStr(&mut a, program); |
| 3327 | try expectNoErrors(&result); |
| 3328 | } |
| 3329 | } |
| 3330 | |
| 3331 | /// Test that `if let` (without mut) rejects assignment. |
| 3332 | @test unsafe fn testResolveIfLetImmutable() throws (testing::TestError) { |
| 3333 | let mut testArena189 = testArena(); |
| 3334 | let testStorage189: 'test189 = &mut testArena189 in { |
| 3335 | let mut a = testResolver(testStorage189); |
| 3336 | let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }"; |
| 3337 | let result = try resolveProgramStr(&mut a, program); |
| 3338 | let err = try expectError(&result); |
| 3339 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 3340 | } |
| 3341 | } |
| 3342 | |
| 3343 | /// Test that `let mut ... else` produces a mutable binding. |
| 3344 | @test unsafe fn testResolveLetMutElse() throws (testing::TestError) { |
| 3345 | let mut testArena190 = testArena(); |
| 3346 | let testStorage190: 'test190 = &mut testArena190 in { |
| 3347 | let mut a = testResolver(testStorage190); |
| 3348 | let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;"; |
| 3349 | let result = try resolveProgramStr(&mut a, program); |
| 3350 | try expectNoErrors(&result); |
| 3351 | } |
| 3352 | } |
| 3353 | |
| 3354 | /// Test that `let ... else` (without mut) rejects assignment. |
| 3355 | @test unsafe fn testResolveLetElseImmutable() throws (testing::TestError) { |
| 3356 | let mut testArena191 = testArena(); |
| 3357 | let testStorage191: 'test191 = &mut testArena191 in { |
| 3358 | let mut a = testResolver(testStorage191); |
| 3359 | let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;"; |
| 3360 | let result = try resolveProgramStr(&mut a, program); |
| 3361 | let err = try expectError(&result); |
| 3362 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 3363 | } |
| 3364 | } |
| 3365 | |
| 3366 | @test unsafe fn testResolveLetCaseElse() throws (testing::TestError) { |
| 3367 | { |
| 3368 | let mut testArena192 = testArena(); |
| 3369 | let testStorage192: 'test192 = &mut testArena192 in { |
| 3370 | let mut a = testResolver(testStorage192); |
| 3371 | let program = "let case _ = 1 else panic;"; |
| 3372 | let result = try resolveProgramStr(&mut a, program); |
| 3373 | try expectNoErrors(&result); |
| 3374 | } |
| 3375 | } { |
| 3376 | let mut testArena193 = testArena(); |
| 3377 | let testStorage193: 'test193 = &mut testArena193 in { |
| 3378 | let mut a = testResolver(testStorage193); |
| 3379 | let program = "let case _ = true else false;"; |
| 3380 | let result = try resolveProgramStr(&mut a, program); |
| 3381 | try expectNoErrors(&result); |
| 3382 | } |
| 3383 | } |
| 3384 | } |
| 3385 | |
| 3386 | @test unsafe fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) { |
| 3387 | let mut testArena194 = testArena(); |
| 3388 | let testStorage194: 'test194 = &mut testArena194 in { |
| 3389 | let mut a = testResolver(testStorage194); |
| 3390 | let program = "let case _ = 1 else {};"; |
| 3391 | let result = try resolveProgramStr(&mut a, program); |
| 3392 | let err = try expectError(&result); |
| 3393 | try expectTypeMismatch(err, super::Type::Int, super::Type::Void); |
| 3394 | } |
| 3395 | } |
| 3396 | |
| 3397 | @test unsafe fn testResolveTryValidPropagation() throws (testing::TestError) { |
| 3398 | let mut testArena195 = testArena(); |
| 3399 | let testStorage195: 'test195 = &mut testArena195 in { |
| 3400 | let mut a = testResolver(testStorage195); |
| 3401 | let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }"; |
| 3402 | let result = try resolveProgramStr(&mut a, program); |
| 3403 | try expectNoErrors(&result); |
| 3404 | } |
| 3405 | } |
| 3406 | |
| 3407 | @test unsafe fn testResolveTryRequiresThrowsClause() throws (testing::TestError) { |
| 3408 | let mut testArena196 = testArena(); |
| 3409 | let testStorage196: 'test196 = &mut testArena196 in { |
| 3410 | let mut a = testResolver(testStorage196); |
| 3411 | let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }"; |
| 3412 | let result = try resolveProgramStr(&mut a, program); |
| 3413 | try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows); |
| 3414 | } |
| 3415 | } |
| 3416 | |
| 3417 | @test unsafe fn testResolveTryIncompatibleError() throws (testing::TestError) { |
| 3418 | let mut testArena197 = testArena(); |
| 3419 | let testStorage197: 'test197 = &mut testArena197 in { |
| 3420 | let mut a = testResolver(testStorage197); |
| 3421 | let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }"; |
| 3422 | let result = try resolveProgramStr(&mut a, program); |
| 3423 | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
| 3424 | } |
| 3425 | } |
| 3426 | |
| 3427 | @test unsafe fn testResolveTryNonThrowing() throws (testing::TestError) { |
| 3428 | let mut testArena198 = testArena(); |
| 3429 | let testStorage198: 'test198 = &mut testArena198 in { |
| 3430 | let mut a = testResolver(testStorage198); |
| 3431 | let program = "fn safe() {} fn caller() throws (i32) { try safe() }"; |
| 3432 | let result = try resolveProgramStr(&mut a, program); |
| 3433 | try expectErrorKind(&result, super::ErrorKind::TryNonThrowing); |
| 3434 | } |
| 3435 | } |
| 3436 | |
| 3437 | @test unsafe fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) { |
| 3438 | let mut testArena199 = testArena(); |
| 3439 | let testStorage199: 'test199 = &mut testArena199 in { |
| 3440 | let mut a = testResolver(testStorage199); |
| 3441 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }"; |
| 3442 | let result = try resolveProgramStr(&mut a, program); |
| 3443 | try expectNoErrors(&result); |
| 3444 | } |
| 3445 | } |
| 3446 | |
| 3447 | @test unsafe fn testResolveTryCatchBlockDiverges() throws (testing::TestError) { |
| 3448 | let mut testArena200 = testArena(); |
| 3449 | let testStorage200: 'test200 = &mut testArena200 in { |
| 3450 | let mut a = testResolver(testStorage200); |
| 3451 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }"; |
| 3452 | let result = try resolveProgramStr(&mut a, program); |
| 3453 | try expectNoErrors(&result); |
| 3454 | } |
| 3455 | } |
| 3456 | |
| 3457 | @test unsafe fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) { |
| 3458 | let mut testArena201 = testArena(); |
| 3459 | let testStorage201: 'test201 = &mut testArena201 in { |
| 3460 | let mut a = testResolver(testStorage201); |
| 3461 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }"; |
| 3462 | let result = try resolveProgramStr(&mut a, program); |
| 3463 | let err = try expectError(&result); |
| 3464 | try expectTypeMismatch(err, super::Type::U32, super::Type::Void); |
| 3465 | } |
| 3466 | } |
| 3467 | |
| 3468 | @test unsafe fn testResolveCallMissingTry() throws (testing::TestError) { |
| 3469 | let mut testArena202 = testArena(); |
| 3470 | let testStorage202: 'test202 = &mut testArena202 in { |
| 3471 | let mut a = testResolver(testStorage202); |
| 3472 | let program = "fn fallible() throws (i32) {} fn caller() { fallible() }"; |
| 3473 | let result = try resolveProgramStr(&mut a, program); |
| 3474 | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
| 3475 | } |
| 3476 | } |
| 3477 | |
| 3478 | /// Test that `try?` converts errors to optionals without requiring caller to throw. |
| 3479 | @test unsafe fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) { |
| 3480 | // `try?` should wrap the return type in optional and not require caller to throw. |
| 3481 | { |
| 3482 | let mut testArena203 = testArena(); |
| 3483 | let testStorage203: 'test203 = &mut testArena203 in { |
| 3484 | let mut a = testResolver(testStorage203); |
| 3485 | let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }"; |
| 3486 | let result = try resolveProgramStr(&mut a, program); |
| 3487 | try expectNoErrors(&result); |
| 3488 | } |
| 3489 | } |
| 3490 | // `try?` works in non-throwing function. |
| 3491 | { |
| 3492 | let mut testArena204 = testArena(); |
| 3493 | let testStorage204: 'test204 = &mut testArena204 in { |
| 3494 | let mut a = testResolver(testStorage204); |
| 3495 | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }"; |
| 3496 | let result = try resolveProgramStr(&mut a, program); |
| 3497 | try expectNoErrors(&result); |
| 3498 | } |
| 3499 | } |
| 3500 | // `try?` can be used in if-let patterns. |
| 3501 | { |
| 3502 | let mut testArena205 = testArena(); |
| 3503 | let testStorage205: 'test205 = &mut testArena205 in { |
| 3504 | let mut a = testResolver(testStorage205); |
| 3505 | let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }"; |
| 3506 | let result = try resolveProgramStr(&mut a, program); |
| 3507 | try expectNoErrors(&result); |
| 3508 | } |
| 3509 | } |
| 3510 | } |
| 3511 | |
| 3512 | @test unsafe fn testResolveThrowValid() throws (testing::TestError) { |
| 3513 | let mut testArena206 = testArena(); |
| 3514 | let testStorage206: 'test206 = &mut testArena206 in { |
| 3515 | let mut a = testResolver(testStorage206); |
| 3516 | let program = "fn fail() throws (i32) { throw 1; }"; |
| 3517 | let result = try resolveProgramStr(&mut a, program); |
| 3518 | try expectNoErrors(&result); |
| 3519 | } |
| 3520 | } |
| 3521 | |
| 3522 | @test unsafe fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) { |
| 3523 | let mut testArena207 = testArena(); |
| 3524 | let testStorage207: 'test207 = &mut testArena207 in { |
| 3525 | let mut a = testResolver(testStorage207); |
| 3526 | let program = "fn fail() { throw 1; }"; |
| 3527 | let result = try resolveProgramStr(&mut a, program); |
| 3528 | try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows); |
| 3529 | } |
| 3530 | } |
| 3531 | |
| 3532 | @test unsafe fn testResolveThrowIncompatibleError() throws (testing::TestError) { |
| 3533 | let mut testArena208 = testArena(); |
| 3534 | let testStorage208: 'test208 = &mut testArena208 in { |
| 3535 | let mut a = testResolver(testStorage208); |
| 3536 | let program = "fn fail() throws (i32) { throw true; }"; |
| 3537 | let result = try resolveProgramStr(&mut a, program); |
| 3538 | try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError); |
| 3539 | } |
| 3540 | } |
| 3541 | |
| 3542 | // Binary operation tests ////////////////////////////////////////////////////// |
| 3543 | |
| 3544 | @test unsafe fn testResolveBinaryOpArithmetic() throws (testing::TestError) { |
| 3545 | { |
| 3546 | let mut testArena209 = testArena(); |
| 3547 | let testStorage209: 'test209 = &mut testArena209 in { |
| 3548 | let mut a = testResolver(testStorage209); |
| 3549 | let result = try resolveExprStr(&mut a, "4 + 4"); |
| 3550 | try expectNoErrors(&result); |
| 3551 | try expectType(&a, result.root, super::Type::Int); |
| 3552 | } |
| 3553 | } { |
| 3554 | let mut testArena210 = testArena(); |
| 3555 | let testStorage210: 'test210 = &mut testArena210 in { |
| 3556 | let mut a = testResolver(testStorage210); |
| 3557 | let result = try resolveExprStr(&mut a, "10 - 3"); |
| 3558 | try expectNoErrors(&result); |
| 3559 | try expectType(&a, result.root, super::Type::Int); |
| 3560 | } |
| 3561 | } { |
| 3562 | let mut testArena211 = testArena(); |
| 3563 | let testStorage211: 'test211 = &mut testArena211 in { |
| 3564 | let mut a = testResolver(testStorage211); |
| 3565 | let result = try resolveExprStr(&mut a, "5 * 6"); |
| 3566 | try expectNoErrors(&result); |
| 3567 | try expectType(&a, result.root, super::Type::Int); |
| 3568 | } |
| 3569 | } { |
| 3570 | let mut testArena212 = testArena(); |
| 3571 | let testStorage212: 'test212 = &mut testArena212 in { |
| 3572 | let mut a = testResolver(testStorage212); |
| 3573 | let result = try resolveExprStr(&mut a, "20 / 4"); |
| 3574 | try expectNoErrors(&result); |
| 3575 | try expectType(&a, result.root, super::Type::Int); |
| 3576 | } |
| 3577 | } { |
| 3578 | let mut testArena213 = testArena(); |
| 3579 | let testStorage213: 'test213 = &mut testArena213 in { |
| 3580 | let mut a = testResolver(testStorage213); |
| 3581 | let result = try resolveExprStr(&mut a, "17 % 5"); |
| 3582 | try expectNoErrors(&result); |
| 3583 | try expectType(&a, result.root, super::Type::Int); |
| 3584 | } |
| 3585 | } { |
| 3586 | let mut testArena214 = testArena(); |
| 3587 | let testStorage214: 'test214 = &mut testArena214 in { |
| 3588 | let mut a = testResolver(testStorage214); |
| 3589 | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;"); |
| 3590 | try expectNoErrors(&result); |
| 3591 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3592 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3593 | } |
| 3594 | } { |
| 3595 | let mut testArena215 = testArena(); |
| 3596 | let testStorage215: 'test215 = &mut testArena215 in { |
| 3597 | let mut a = testResolver(testStorage215); |
| 3598 | let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4"); |
| 3599 | try expectNoErrors(&result); |
| 3600 | try expectType(&a, result.root, super::Type::Int); |
| 3601 | } |
| 3602 | } { |
| 3603 | let mut testArena216 = testArena(); |
| 3604 | let testStorage216: 'test216 = &mut testArena216 in { |
| 3605 | let mut a = testResolver(testStorage216); |
| 3606 | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;"); |
| 3607 | try expectNoErrors(&result); |
| 3608 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3609 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3610 | } |
| 3611 | } { |
| 3612 | let mut testArena217 = testArena(); |
| 3613 | let testStorage217: 'test217 = &mut testArena217 in { |
| 3614 | let mut a = testResolver(testStorage217); |
| 3615 | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;"); |
| 3616 | try expectNoErrors(&result); |
| 3617 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3618 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3619 | } |
| 3620 | } { |
| 3621 | let mut testArena218 = testArena(); |
| 3622 | let testStorage218: 'test218 = &mut testArena218 in { |
| 3623 | let mut a = testResolver(testStorage218); |
| 3624 | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;"); |
| 3625 | try expectNoErrors(&result); |
| 3626 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3627 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3628 | } |
| 3629 | } |
| 3630 | } |
| 3631 | |
| 3632 | @test unsafe fn testResolveBinaryOpComparison() throws (testing::TestError) { |
| 3633 | { |
| 3634 | let mut testArena219 = testArena(); |
| 3635 | let testStorage219: 'test219 = &mut testArena219 in { |
| 3636 | let mut a = testResolver(testStorage219); |
| 3637 | let result = try resolveExprStr(&mut a, "5 == 5"); |
| 3638 | try expectNoErrors(&result); |
| 3639 | try expectType(&a, result.root, super::Type::Bool); |
| 3640 | } |
| 3641 | } { |
| 3642 | let mut testArena220 = testArena(); |
| 3643 | let testStorage220: 'test220 = &mut testArena220 in { |
| 3644 | let mut a = testResolver(testStorage220); |
| 3645 | let result = try resolveExprStr(&mut a, "5 <> 10"); |
| 3646 | try expectNoErrors(&result); |
| 3647 | try expectType(&a, result.root, super::Type::Bool); |
| 3648 | } |
| 3649 | } { |
| 3650 | let mut testArena221 = testArena(); |
| 3651 | let testStorage221: 'test221 = &mut testArena221 in { |
| 3652 | let mut a = testResolver(testStorage221); |
| 3653 | let result = try resolveExprStr(&mut a, "5 < 10"); |
| 3654 | try expectNoErrors(&result); |
| 3655 | try expectType(&a, result.root, super::Type::Bool); |
| 3656 | } |
| 3657 | } { |
| 3658 | let mut testArena222 = testArena(); |
| 3659 | let testStorage222: 'test222 = &mut testArena222 in { |
| 3660 | let mut a = testResolver(testStorage222); |
| 3661 | let result = try resolveExprStr(&mut a, "10 > 5"); |
| 3662 | try expectNoErrors(&result); |
| 3663 | try expectType(&a, result.root, super::Type::Bool); |
| 3664 | } |
| 3665 | } { |
| 3666 | let mut testArena223 = testArena(); |
| 3667 | let testStorage223: 'test223 = &mut testArena223 in { |
| 3668 | let mut a = testResolver(testStorage223); |
| 3669 | let result = try resolveExprStr(&mut a, "5 <= 5"); |
| 3670 | try expectNoErrors(&result); |
| 3671 | try expectType(&a, result.root, super::Type::Bool); |
| 3672 | } |
| 3673 | } { |
| 3674 | let mut testArena224 = testArena(); |
| 3675 | let testStorage224: 'test224 = &mut testArena224 in { |
| 3676 | let mut a = testResolver(testStorage224); |
| 3677 | let result = try resolveExprStr(&mut a, "10 >= 5"); |
| 3678 | try expectNoErrors(&result); |
| 3679 | try expectType(&a, result.root, super::Type::Bool); |
| 3680 | } |
| 3681 | } { |
| 3682 | let mut testArena225 = testArena(); |
| 3683 | let testStorage225: 'test225 = &mut testArena225 in { |
| 3684 | let mut a = testResolver(testStorage225); |
| 3685 | let result = try resolveExprStr(&mut a, "true == false"); |
| 3686 | try expectNoErrors(&result); |
| 3687 | try expectType(&a, result.root, super::Type::Bool); |
| 3688 | } |
| 3689 | } { |
| 3690 | let mut testArena226 = testArena(); |
| 3691 | let testStorage226: 'test226 = &mut testArena226 in { |
| 3692 | let mut a = testResolver(testStorage226); |
| 3693 | let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4"); |
| 3694 | try expectNoErrors(&result); |
| 3695 | try expectType(&a, result.root, super::Type::Bool); |
| 3696 | } |
| 3697 | } { |
| 3698 | let mut testArena227 = testArena(); |
| 3699 | let testStorage227: 'test227 = &mut testArena227 in { |
| 3700 | let mut a = testResolver(testStorage227); |
| 3701 | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;"); |
| 3702 | try expectNoErrors(&result); |
| 3703 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3704 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 3705 | } |
| 3706 | } { |
| 3707 | let mut testArena228 = testArena(); |
| 3708 | let testStorage228: 'test228 = &mut testArena228 in { |
| 3709 | let mut a = testResolver(testStorage228); |
| 3710 | let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;"); |
| 3711 | try expectNoErrors(&result); |
| 3712 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3713 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 3714 | } |
| 3715 | } |
| 3716 | } |
| 3717 | |
| 3718 | @test unsafe fn testResolveBinaryOpLogical() throws (testing::TestError) { |
| 3719 | { |
| 3720 | let mut testArena229 = testArena(); |
| 3721 | let testStorage229: 'test229 = &mut testArena229 in { |
| 3722 | let mut a = testResolver(testStorage229); |
| 3723 | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;"); |
| 3724 | try expectNoErrors(&result); |
| 3725 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3726 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 3727 | } |
| 3728 | } { |
| 3729 | let mut testArena230 = testArena(); |
| 3730 | let testStorage230: 'test230 = &mut testArena230 in { |
| 3731 | let mut a = testResolver(testStorage230); |
| 3732 | let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;"); |
| 3733 | try expectNoErrors(&result); |
| 3734 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3735 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 3736 | } |
| 3737 | } { |
| 3738 | let mut testArena231 = testArena(); |
| 3739 | let testStorage231: 'test231 = &mut testArena231 in { |
| 3740 | let mut a = testResolver(testStorage231); |
| 3741 | let result = try resolveExprStr(&mut a, "true and false"); |
| 3742 | try expectNoErrors(&result); |
| 3743 | try expectType(&a, result.root, super::Type::Bool); |
| 3744 | } |
| 3745 | } |
| 3746 | } |
| 3747 | |
| 3748 | @test unsafe fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) { |
| 3749 | { |
| 3750 | let mut testArena232 = testArena(); |
| 3751 | let testStorage232: 'test232 = &mut testArena232 in { |
| 3752 | let mut a = testResolver(testStorage232); |
| 3753 | let result = try resolveProgramStr(&mut a, "4 + true"); |
| 3754 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3755 | } |
| 3756 | } { |
| 3757 | let mut testArena233 = testArena(); |
| 3758 | let testStorage233: 'test233 = &mut testArena233 in { |
| 3759 | let mut a = testResolver(testStorage233); |
| 3760 | let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;"); |
| 3761 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3762 | } |
| 3763 | } { |
| 3764 | let mut testArena234 = testArena(); |
| 3765 | let testStorage234: 'test234 = &mut testArena234 in { |
| 3766 | let mut a = testResolver(testStorage234); |
| 3767 | let result = try resolveProgramStr(&mut a, "10 - false"); |
| 3768 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3769 | } |
| 3770 | } { |
| 3771 | let mut testArena235 = testArena(); |
| 3772 | let testStorage235: 'test235 = &mut testArena235 in { |
| 3773 | let mut a = testResolver(testStorage235); |
| 3774 | let result = try resolveProgramStr(&mut a, "5 * true"); |
| 3775 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3776 | } |
| 3777 | } { |
| 3778 | let mut testArena236 = testArena(); |
| 3779 | let testStorage236: 'test236 = &mut testArena236 in { |
| 3780 | let mut a = testResolver(testStorage236); |
| 3781 | let result = try resolveProgramStr(&mut a, "20 / false"); |
| 3782 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3783 | } |
| 3784 | } { |
| 3785 | let mut testArena237 = testArena(); |
| 3786 | let testStorage237: 'test237 = &mut testArena237 in { |
| 3787 | let mut a = testResolver(testStorage237); |
| 3788 | let result = try resolveProgramStr(&mut a, "17 % true"); |
| 3789 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3790 | } |
| 3791 | } { |
| 3792 | let mut testArena238 = testArena(); |
| 3793 | let testStorage238: 'test238 = &mut testArena238 in { |
| 3794 | let mut a = testResolver(testStorage238); |
| 3795 | let result = try resolveProgramStr(&mut a, "1 + (true * 3)"); |
| 3796 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3797 | } |
| 3798 | } |
| 3799 | } |
| 3800 | |
| 3801 | @test unsafe fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) { |
| 3802 | { |
| 3803 | let mut testArena239 = testArena(); |
| 3804 | let testStorage239: 'test239 = &mut testArena239 in { |
| 3805 | let mut a = testResolver(testStorage239); |
| 3806 | let result = try resolveProgramStr(&mut a, "42 and true"); |
| 3807 | let err = try expectError(&result); |
| 3808 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 3809 | } |
| 3810 | } { |
| 3811 | let mut testArena240 = testArena(); |
| 3812 | let testStorage240: 'test240 = &mut testArena240 in { |
| 3813 | let mut a = testResolver(testStorage240); |
| 3814 | let result = try resolveProgramStr(&mut a, "true or 5"); |
| 3815 | let err = try expectError(&result); |
| 3816 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 3817 | } |
| 3818 | } { |
| 3819 | let mut testArena241 = testArena(); |
| 3820 | let testStorage241: 'test241 = &mut testArena241 in { |
| 3821 | let mut a = testResolver(testStorage241); |
| 3822 | let result = try resolveProgramStr(&mut a, "1 and 2"); |
| 3823 | let err = try expectError(&result); |
| 3824 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 3825 | } |
| 3826 | } |
| 3827 | } |
| 3828 | |
| 3829 | @test unsafe fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) { |
| 3830 | let mut testArena242 = testArena(); |
| 3831 | let testStorage242: 'test242 = &mut testArena242 in { |
| 3832 | let mut a = testResolver(testStorage242); |
| 3833 | let result = try resolveProgramStr(&mut a, "true < false"); |
| 3834 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3835 | } |
| 3836 | } |
| 3837 | |
| 3838 | // Unary operation tests /////////////////////////////////////////////////////// |
| 3839 | |
| 3840 | @test unsafe fn testResolveUnaryOpNot() throws (testing::TestError) { |
| 3841 | { |
| 3842 | let mut testArena243 = testArena(); |
| 3843 | let testStorage243: 'test243 = &mut testArena243 in { |
| 3844 | let mut a = testResolver(testStorage243); |
| 3845 | let result = try resolveExprStr(&mut a, "not true"); |
| 3846 | try expectNoErrors(&result); |
| 3847 | try expectType(&a, result.root, super::Type::Bool); |
| 3848 | } |
| 3849 | } { |
| 3850 | let mut testArena244 = testArena(); |
| 3851 | let testStorage244: 'test244 = &mut testArena244 in { |
| 3852 | let mut a = testResolver(testStorage244); |
| 3853 | let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;"); |
| 3854 | try expectNoErrors(&result); |
| 3855 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3856 | try expectExprStmtType(&a, stmt, super::Type::Bool); |
| 3857 | } |
| 3858 | } { |
| 3859 | let mut testArena245 = testArena(); |
| 3860 | let testStorage245: 'test245 = &mut testArena245 in { |
| 3861 | let mut a = testResolver(testStorage245); |
| 3862 | let result = try resolveExprStr(&mut a, "not (true and false)"); |
| 3863 | try expectNoErrors(&result); |
| 3864 | try expectType(&a, result.root, super::Type::Bool); |
| 3865 | } |
| 3866 | } { |
| 3867 | let mut testArena246 = testArena(); |
| 3868 | let testStorage246: 'test246 = &mut testArena246 in { |
| 3869 | let mut a = testResolver(testStorage246); |
| 3870 | let result = try resolveProgramStr(&mut a, "not 42"); |
| 3871 | let err = try expectError(&result); |
| 3872 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Int); |
| 3873 | } |
| 3874 | } { |
| 3875 | let mut testArena247 = testArena(); |
| 3876 | let testStorage247: 'test247 = &mut testArena247 in { |
| 3877 | let mut a = testResolver(testStorage247); |
| 3878 | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;"); |
| 3879 | let err = try expectError(&result); |
| 3880 | try expectTypeMismatch(err, super::Type::Bool, super::Type::I32); |
| 3881 | } |
| 3882 | } |
| 3883 | } |
| 3884 | |
| 3885 | @test unsafe fn testResolveUnaryOpNeg() throws (testing::TestError) { |
| 3886 | { |
| 3887 | let mut testArena248 = testArena(); |
| 3888 | let testStorage248: 'test248 = &mut testArena248 in { |
| 3889 | let mut a = testResolver(testStorage248); |
| 3890 | let result = try resolveExprStr(&mut a, "-42"); |
| 3891 | try expectNoErrors(&result); |
| 3892 | try expectType(&a, result.root, super::Type::Int); |
| 3893 | } |
| 3894 | } { |
| 3895 | let mut testArena249 = testArena(); |
| 3896 | let testStorage249: 'test249 = &mut testArena249 in { |
| 3897 | let mut a = testResolver(testStorage249); |
| 3898 | let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;"); |
| 3899 | try expectNoErrors(&result); |
| 3900 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3901 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 3902 | } |
| 3903 | } { |
| 3904 | let mut testArena250 = testArena(); |
| 3905 | let testStorage250: 'test250 = &mut testArena250 in { |
| 3906 | let mut a = testResolver(testStorage250); |
| 3907 | let result = try resolveExprStr(&mut a, "-(5 + 3)"); |
| 3908 | try expectNoErrors(&result); |
| 3909 | try expectType(&a, result.root, super::Type::Int); |
| 3910 | } |
| 3911 | } { |
| 3912 | let mut testArena251 = testArena(); |
| 3913 | let testStorage251: 'test251 = &mut testArena251 in { |
| 3914 | let mut a = testResolver(testStorage251); |
| 3915 | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;"); |
| 3916 | try expectNoErrors(&result); |
| 3917 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3918 | try expectExprStmtType(&a, stmt, super::Type::I8); |
| 3919 | } |
| 3920 | } { |
| 3921 | let mut testArena252 = testArena(); |
| 3922 | let testStorage252: 'test252 = &mut testArena252 in { |
| 3923 | let mut a = testResolver(testStorage252); |
| 3924 | let result = try resolveProgramStr(&mut a, "-true"); |
| 3925 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3926 | } |
| 3927 | } { |
| 3928 | let mut testArena253 = testArena(); |
| 3929 | let testStorage253: 'test253 = &mut testArena253 in { |
| 3930 | let mut a = testResolver(testStorage253); |
| 3931 | let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;"); |
| 3932 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3933 | } |
| 3934 | } |
| 3935 | } |
| 3936 | |
| 3937 | @test unsafe fn testResolveUnaryOpBitNot() throws (testing::TestError) { |
| 3938 | { |
| 3939 | let mut testArena254 = testArena(); |
| 3940 | let testStorage254: 'test254 = &mut testArena254 in { |
| 3941 | let mut a = testResolver(testStorage254); |
| 3942 | let result = try resolveExprStr(&mut a, "~42"); |
| 3943 | try expectNoErrors(&result); |
| 3944 | try expectType(&a, result.root, super::Type::Int); |
| 3945 | } |
| 3946 | } { |
| 3947 | let mut testArena255 = testArena(); |
| 3948 | let testStorage255: 'test255 = &mut testArena255 in { |
| 3949 | let mut a = testResolver(testStorage255); |
| 3950 | let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;"); |
| 3951 | try expectNoErrors(&result); |
| 3952 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3953 | try expectExprStmtType(&a, stmt, super::Type::U32); |
| 3954 | } |
| 3955 | } { |
| 3956 | let mut testArena256 = testArena(); |
| 3957 | let testStorage256: 'test256 = &mut testArena256 in { |
| 3958 | let mut a = testResolver(testStorage256); |
| 3959 | let result = try resolveExprStr(&mut a, "~(0xFF)"); |
| 3960 | try expectNoErrors(&result); |
| 3961 | try expectType(&a, result.root, super::Type::Int); |
| 3962 | } |
| 3963 | } { |
| 3964 | let mut testArena257 = testArena(); |
| 3965 | let testStorage257: 'test257 = &mut testArena257 in { |
| 3966 | let mut a = testResolver(testStorage257); |
| 3967 | let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;"); |
| 3968 | try expectNoErrors(&result); |
| 3969 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 3970 | try expectExprStmtType(&a, stmt, super::Type::I8); |
| 3971 | } |
| 3972 | } { |
| 3973 | let mut testArena258 = testArena(); |
| 3974 | let testStorage258: 'test258 = &mut testArena258 in { |
| 3975 | let mut a = testResolver(testStorage258); |
| 3976 | let result = try resolveProgramStr(&mut a, "~true"); |
| 3977 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3978 | } |
| 3979 | } { |
| 3980 | let mut testArena259 = testArena(); |
| 3981 | let testStorage259: 'test259 = &mut testArena259 in { |
| 3982 | let mut a = testResolver(testStorage259); |
| 3983 | let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;"); |
| 3984 | try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric); |
| 3985 | } |
| 3986 | } |
| 3987 | } |
| 3988 | |
| 3989 | @test unsafe fn testResolveUnaryOpNested() throws (testing::TestError) { |
| 3990 | { |
| 3991 | let mut testArena260 = testArena(); |
| 3992 | let testStorage260: 'test260 = &mut testArena260 in { |
| 3993 | let mut a = testResolver(testStorage260); |
| 3994 | let result = try resolveExprStr(&mut a, "not not true"); |
| 3995 | try expectNoErrors(&result); |
| 3996 | try expectType(&a, result.root, super::Type::Bool); |
| 3997 | } |
| 3998 | } { |
| 3999 | let mut testArena261 = testArena(); |
| 4000 | let testStorage261: 'test261 = &mut testArena261 in { |
| 4001 | let mut a = testResolver(testStorage261); |
| 4002 | let result = try resolveExprStr(&mut a, "--42"); |
| 4003 | try expectNoErrors(&result); |
| 4004 | try expectType(&a, result.root, super::Type::Int); |
| 4005 | } |
| 4006 | } { |
| 4007 | let mut testArena262 = testArena(); |
| 4008 | let testStorage262: 'test262 = &mut testArena262 in { |
| 4009 | let mut a = testResolver(testStorage262); |
| 4010 | let result = try resolveExprStr(&mut a, "~~0xFF"); |
| 4011 | try expectNoErrors(&result); |
| 4012 | try expectType(&a, result.root, super::Type::Int); |
| 4013 | } |
| 4014 | } { |
| 4015 | let mut testArena263 = testArena(); |
| 4016 | let testStorage263: 'test263 = &mut testArena263 in { |
| 4017 | let mut a = testResolver(testStorage263); |
| 4018 | let result = try resolveExprStr(&mut a, "-(~42)"); |
| 4019 | try expectNoErrors(&result); |
| 4020 | try expectType(&a, result.root, super::Type::Int); |
| 4021 | } |
| 4022 | } |
| 4023 | } |
| 4024 | |
| 4025 | // test fn testNormalPointerArithmetic() throws (testing::TestError) { |
| 4026 | // mut a = testResolver(); |
| 4027 | // let result = try resolveProgramStr(&mut a, "fn test() { let ptr: *i32 = undefined; let x = ptr + 1; }"); |
| 4028 | // try expectNoErrors(&result); |
| 4029 | // } |
| 4030 | |
| 4031 | // Dereference tests ////////////////////////////////////////////////////////// |
| 4032 | |
| 4033 | @test unsafe fn testResolveDeref() throws (testing::TestError) { |
| 4034 | { |
| 4035 | let mut testArena264 = testArena(); |
| 4036 | let testStorage264: 'test264 = &mut testArena264 in { |
| 4037 | let mut a = testResolver(testStorage264); |
| 4038 | let result = try resolveBlockStr(&mut a, "static x: i32 = 42; let ptr: *i32 = &x; *ptr;"); |
| 4039 | try expectNoErrors(&result); |
| 4040 | let stmt = try parser::tests::getBlockLastStmt(result.root); |
| 4041 | try expectExprStmtType(&a, stmt, super::Type::I32); |
| 4042 | } |
| 4043 | } { |
| 4044 | let mut testArena265 = testArena(); |
| 4045 | let testStorage265: 'test265 = &mut testArena265 in { |
| 4046 | let mut a = testResolver(testStorage265); |
| 4047 | let result = try resolveExprStr(&mut a, "*42"); |
| 4048 | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
| 4049 | } |
| 4050 | } { |
| 4051 | let mut testArena266 = testArena(); |
| 4052 | let testStorage266: 'test266 = &mut testArena266 in { |
| 4053 | let mut a = testResolver(testStorage266); |
| 4054 | let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;"); |
| 4055 | try expectErrorKind(&result, super::ErrorKind::ExpectedPointer); |
| 4056 | } |
| 4057 | } |
| 4058 | } |
| 4059 | |
| 4060 | @test unsafe fn testResolveAssignDeref() throws (testing::TestError) { |
| 4061 | { |
| 4062 | let mut testArena267 = testArena(); |
| 4063 | let testStorage267: 'test267 = &mut testArena267 in { |
| 4064 | let mut a = testResolver(testStorage267); |
| 4065 | let program = "static x: i32 = 0; let ptr: *mut i32 = &mut x; set *ptr = 42;"; |
| 4066 | let result = try resolveProgramStr(&mut a, program); |
| 4067 | try expectNoErrors(&result); |
| 4068 | } |
| 4069 | } { |
| 4070 | let mut testArena268 = testArena(); |
| 4071 | let testStorage268: 'test268 = &mut testArena268 in { |
| 4072 | let mut a = testResolver(testStorage268); |
| 4073 | let program = "static x: i32 = 0; let ptr: *i32 = &x; set *ptr = 42;"; |
| 4074 | let result = try resolveProgramStr(&mut a, program); |
| 4075 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4076 | } |
| 4077 | } { |
| 4078 | let mut testArena269 = testArena(); |
| 4079 | let testStorage269: 'test269 = &mut testArena269 in { |
| 4080 | let mut a = testResolver(testStorage269); |
| 4081 | let program = "static x: i32 = 0; let mut ptr: *i32 = &x; set *ptr = 42;"; |
| 4082 | let result = try resolveProgramStr(&mut a, program); |
| 4083 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4084 | } |
| 4085 | } { |
| 4086 | let mut testArena270 = testArena(); |
| 4087 | let testStorage270: 'test270 = &mut testArena270 in { |
| 4088 | let mut a = testResolver(testStorage270); |
| 4089 | let program = "static x: u8 = 0; let mut ptr: *mut u8 = &mut x; set *ptr = 255;"; |
| 4090 | let result = try resolveProgramStr(&mut a, program); |
| 4091 | try expectNoErrors(&result); |
| 4092 | } |
| 4093 | } |
| 4094 | } |
| 4095 | |
| 4096 | // Type inference tests /////////////////////////////////////////////////////// |
| 4097 | |
| 4098 | @test unsafe fn testResolveBasicTypeInference() throws (testing::TestError) { |
| 4099 | { |
| 4100 | // Boolean literals are unambiguous. |
| 4101 | let mut testArena271 = testArena(); |
| 4102 | let testStorage271: 'test271 = &mut testArena271 in { |
| 4103 | let mut a = testResolver(testStorage271); |
| 4104 | let result = try resolveProgramStr(&mut a, "let x = true; x;"); |
| 4105 | try expectNoErrors(&result); |
| 4106 | |
| 4107 | let xStmt = try parser::tests::getBlockLastStmt(result.root); |
| 4108 | try expectExprStmtType(&a, xStmt, super::Type::Bool); |
| 4109 | } |
| 4110 | } { |
| 4111 | // Integer literals are ambiguous. |
| 4112 | let mut testArena272 = testArena(); |
| 4113 | let testStorage272: 'test272 = &mut testArena272 in { |
| 4114 | let mut a = testResolver(testStorage272); |
| 4115 | let result = try resolveProgramStr(&mut a, "let x = 34;"); |
| 4116 | try expectErrorKind(&result, super::ErrorKind::CannotInferType); |
| 4117 | } |
| 4118 | } |
| 4119 | } |
| 4120 | |
| 4121 | // Union tests ///////////////////////////////////////////////////////////////// |
| 4122 | |
| 4123 | @test unsafe fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) { |
| 4124 | let mut testArena273 = testArena(); |
| 4125 | let testStorage273: 'test273 = &mut testArena273 in { |
| 4126 | let mut a = testResolver(testStorage273); |
| 4127 | let program = "union Status { Ok, Error } Status::Ok;"; |
| 4128 | let result = try resolveProgramStr(&mut a, program); |
| 4129 | |
| 4130 | let ty = try getTypeInScopeOf(&a, result.root, "Status"); |
| 4131 | let case super::NominalType::Union(unionType) = *ty |
| 4132 | else throw testing::TestError::Failed; |
| 4133 | try testing::expect(unionType.variants.len == 2); |
| 4134 | try testing::expect(mem::eq(unionType.variants[0].name, "Ok")); |
| 4135 | try testing::expect(mem::eq(unionType.variants[1].name, "Error")); |
| 4136 | if getUnionVariantPayload(ty, "Ok") <> super::Type::Void { |
| 4137 | throw testing::TestError::Failed; |
| 4138 | } |
| 4139 | let stmt = try getBlockStmt(result.root, 1); |
| 4140 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
| 4141 | try expectNoErrors(&result); |
| 4142 | } |
| 4143 | } |
| 4144 | |
| 4145 | @test unsafe fn testResolveUnionVariantWithPayload() throws (testing::TestError) { |
| 4146 | let mut testArena274 = testArena(); |
| 4147 | let testStorage274: 'test274 = &mut testArena274 in { |
| 4148 | let mut a = testResolver(testStorage274); |
| 4149 | let program = "union R { Ok(i32), Err(bool) } R::Ok(42);"; |
| 4150 | let result = try resolveProgramStr(&mut a, program); |
| 4151 | try expectNoErrors(&result); |
| 4152 | |
| 4153 | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
| 4154 | |
| 4155 | let okPayload = getUnionVariantPayload(ty, "Ok"); |
| 4156 | try testing::expect(okPayload == super::Type::I32); |
| 4157 | |
| 4158 | let errPayload = getUnionVariantPayload(ty, "Err"); |
| 4159 | try testing::expect(errPayload == super::Type::Bool); |
| 4160 | |
| 4161 | let stmt = try getBlockStmt(result.root, 1); |
| 4162 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
| 4163 | |
| 4164 | // TODO: Test payload type. |
| 4165 | } |
| 4166 | } |
| 4167 | |
| 4168 | @test unsafe fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) { |
| 4169 | let mut testArena275 = testArena(); |
| 4170 | let testStorage275: 'test275 = &mut testArena275 in { |
| 4171 | let mut a = testResolver(testStorage275); |
| 4172 | let program = "union R { Ok = 7, Err = 11 } R::Ok;"; |
| 4173 | let result = try resolveProgramStr(&mut a, program); |
| 4174 | try expectNoErrors(&result); |
| 4175 | |
| 4176 | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
| 4177 | let stmt = try getBlockStmt(result.root, 1); |
| 4178 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
| 4179 | } |
| 4180 | } |
| 4181 | |
| 4182 | @test unsafe fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) { |
| 4183 | let mut testArena276 = testArena(); |
| 4184 | let testStorage276: 'test276 = &mut testArena276 in { |
| 4185 | let mut a = testResolver(testStorage276); |
| 4186 | let program = "union R { Ok(i32), Error(bool) } R::Ok(true);"; |
| 4187 | let result = try resolveProgramStr(&mut a, program); |
| 4188 | let err = try expectError(&result); |
| 4189 | try expectTypeMismatch(err, super::Type::I32, super::Type::Bool); |
| 4190 | |
| 4191 | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
| 4192 | let payload = getUnionVariantPayload(ty, "Ok"); |
| 4193 | try testing::expect(payload == super::Type::I32); |
| 4194 | |
| 4195 | let errNode = err.node |
| 4196 | else throw testing::TestError::Failed; |
| 4197 | let case ast::NodeValue::Bool(_) = errNode.value |
| 4198 | else throw testing::TestError::Failed; |
| 4199 | } |
| 4200 | } |
| 4201 | |
| 4202 | @test unsafe fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) { |
| 4203 | let mut testArena277 = testArena(); |
| 4204 | let testStorage277: 'test277 = &mut testArena277 in { |
| 4205 | let mut a = testResolver(testStorage277); |
| 4206 | let program = "union Status { Ok, Error } Status::Ok(42);"; |
| 4207 | let result = try resolveProgramStr(&mut a, program); |
| 4208 | let err = try expectError(&result); |
| 4209 | |
| 4210 | let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind |
| 4211 | else throw testing::TestError::Failed; |
| 4212 | let node = err.node |
| 4213 | else throw testing::TestError::Failed; |
| 4214 | let case ast::NodeValue::Call(_) = node.value |
| 4215 | else throw testing::TestError::Failed; |
| 4216 | } |
| 4217 | } |
| 4218 | |
| 4219 | @test unsafe fn testResolveUnionVariantUnknown() throws (testing::TestError) { |
| 4220 | let mut testArena278 = testArena(); |
| 4221 | let testStorage278: 'test278 = &mut testArena278 in { |
| 4222 | let mut a = testResolver(testStorage278); |
| 4223 | let program = "union Status { Ok, Error } Status::Unknown;"; |
| 4224 | let result = try resolveProgramStr(&mut a, program); |
| 4225 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
| 4226 | } |
| 4227 | } |
| 4228 | |
| 4229 | @test unsafe fn testResolveScopeAccessUndefinedType() throws (testing::TestError) { |
| 4230 | let mut testArena279 = testArena(); |
| 4231 | let testStorage279: 'test279 = &mut testArena279 in { |
| 4232 | let mut a = testResolver(testStorage279); |
| 4233 | let result = try resolveProgramStr(&mut a, "Unknown::X;"); |
| 4234 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown")); |
| 4235 | } |
| 4236 | } |
| 4237 | |
| 4238 | @test unsafe fn testResolveUnionVariantVoidPayload() throws (testing::TestError) { |
| 4239 | let mut testArena280 = testArena(); |
| 4240 | let testStorage280: 'test280 = &mut testArena280 in { |
| 4241 | let mut a = testResolver(testStorage280); |
| 4242 | let program = "union R { Success(i32), Pending } R::Pending;"; |
| 4243 | let result = try resolveProgramStr(&mut a, program); |
| 4244 | try expectNoErrors(&result); |
| 4245 | |
| 4246 | let ty = try getTypeInScopeOf(&a, result.root, "R"); |
| 4247 | let payload = getUnionVariantPayload(ty, "Pending"); |
| 4248 | try testing::expect(payload == super::Type::Void); |
| 4249 | |
| 4250 | let stmt = try getBlockStmt(result.root, 1); |
| 4251 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
| 4252 | } |
| 4253 | } |
| 4254 | |
| 4255 | @test unsafe fn testResolveUnionVariantRecordPayload() throws (testing::TestError) { |
| 4256 | let mut testArena281 = testArena(); |
| 4257 | let testStorage281: 'test281 = &mut testArena281 in { |
| 4258 | let mut a = testResolver(testStorage281); |
| 4259 | let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });"; |
| 4260 | let result = try resolveProgramStr(&mut a, program); |
| 4261 | try expectNoErrors(&result); |
| 4262 | |
| 4263 | let ty = try getTypeInScopeOf(&a, result.root, "S"); |
| 4264 | let stmt = try getBlockStmt(result.root, 2); |
| 4265 | try expectExprStmtType(&a, stmt, super::Type::Nominal(ty)); |
| 4266 | } |
| 4267 | } |
| 4268 | |
| 4269 | @test unsafe fn testResolveBuiltinSizeOf() throws (testing::TestError) { |
| 4270 | try resolveAndExpectConstExpr("@sizeOf(u8)", 1); |
| 4271 | try resolveAndExpectConstExpr("@sizeOf(u16)", 2); |
| 4272 | try resolveAndExpectConstExpr("@sizeOf(u32)", 4); |
| 4273 | try resolveAndExpectConstExpr("@sizeOf(i32)", 4); |
| 4274 | try resolveAndExpectConstExpr("@sizeOf(bool)", 1); |
| 4275 | try resolveAndExpectConstExpr("@sizeOf(*u32)", 8); |
| 4276 | try resolveAndExpectConstExpr("@sizeOf([u8; 10])", 10); |
| 4277 | try resolveAndExpectConstExpr("@sizeOf(*[u32])", 16); |
| 4278 | try resolveAndExpectConstExpr("@sizeOf(?u8)", 2); |
| 4279 | try resolveAndExpectConstExpr("@sizeOf(?u16)", 4); |
| 4280 | try resolveAndExpectConstExpr("@sizeOf(?u32)", 8); |
| 4281 | try resolveAndExpectConstExpr("@sizeOf(*opaque)", 8); |
| 4282 | try resolveAndExpectConstStmt("record T { x: u8 } @sizeOf(T);", 1); |
| 4283 | try resolveAndExpectConstStmt("record T { x: i32 } @sizeOf(T);", 4); |
| 4284 | try resolveAndExpectConstStmt("record T { x: i32, y: i8 } @sizeOf(T);", 8); |
| 4285 | try resolveAndExpectConstStmt("record T { x: i8, y: i32 } @sizeOf(T);", 8); |
| 4286 | try resolveAndExpectConstStmt("record T { x: i8, y: i32 } @sizeOf(T);", 8); |
| 4287 | try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @sizeOf(T);", 8); |
| 4288 | try resolveAndExpectConstStmt("record T { x: u8, y: u32, z: u8 }; @sizeOf(T);", 12); |
| 4289 | try resolveAndExpectConstStmt("union T { A, B, C }; @sizeOf(T);", 1); |
| 4290 | try resolveAndExpectConstStmt("union T { A, B(u32), C }; @sizeOf(T);", 8); |
| 4291 | try resolveAndExpectConstStmt("union T { A, B(u16), C }; @sizeOf(T);", 4); |
| 4292 | try resolveAndExpectConstStmt("union T { A(u32), B(u16), C(u16) }; @sizeOf(T);", 8); |
| 4293 | try resolveAndExpectConstStmt("union T { A(u32), B(u16), C([u8; 16]) }; @sizeOf(T);", 20); |
| 4294 | } |
| 4295 | |
| 4296 | @test unsafe fn testResolveBuiltinAlignOf() throws (testing::TestError) { |
| 4297 | try resolveAndExpectConstExpr("@alignOf(u8)", 1); |
| 4298 | try resolveAndExpectConstExpr("@alignOf(u16)", 2); |
| 4299 | try resolveAndExpectConstExpr("@alignOf(u32)", 4); |
| 4300 | try resolveAndExpectConstExpr("@alignOf(i32)", 4); |
| 4301 | try resolveAndExpectConstExpr("@alignOf(bool)", 1); |
| 4302 | try resolveAndExpectConstExpr("@alignOf(*u8)", 8); |
| 4303 | try resolveAndExpectConstExpr("@alignOf(*u16)", 8); |
| 4304 | try resolveAndExpectConstExpr("@alignOf(*u32)", 8); |
| 4305 | try resolveAndExpectConstExpr("@alignOf(*opaque)", 8); |
| 4306 | try resolveAndExpectConstExpr("@alignOf([u8; 8])", 1); |
| 4307 | try resolveAndExpectConstExpr("@alignOf([u16; 8])", 2); |
| 4308 | try resolveAndExpectConstExpr("@alignOf([u32; 8])", 4); |
| 4309 | try resolveAndExpectConstExpr("@alignOf(*[u32])", 8); |
| 4310 | try resolveAndExpectConstExpr("@alignOf(?u8)", 1); |
| 4311 | try resolveAndExpectConstExpr("@alignOf(?u16)", 2); |
| 4312 | try resolveAndExpectConstExpr("@alignOf(?u32)", 4); |
| 4313 | try resolveAndExpectConstStmt("record T { x: u8, y: u16 }; @alignOf(T);", 2); |
| 4314 | try resolveAndExpectConstStmt("record T { x: u8, y: u32, z: u8 }; @alignOf(T);", 4); |
| 4315 | try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @alignOf(T);", 4); |
| 4316 | try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1); |
| 4317 | try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4); |
| 4318 | } |
| 4319 | |
| 4320 | @test unsafe fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) { |
| 4321 | let mut testArena282 = testArena(); |
| 4322 | let testStorage282: 'test282 = &mut testArena282 in { |
| 4323 | let mut a = testResolver(testStorage282); |
| 4324 | let program = "record T { x: u8, y: u32 } @sizeOf(T);"; |
| 4325 | let result = try resolveProgramStr(&mut a, program); |
| 4326 | try expectNoErrors(&result); |
| 4327 | |
| 4328 | let stmt = try getBlockStmt(result.root, 1); |
| 4329 | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
| 4330 | try expectConstInt(&a, expr, 8); |
| 4331 | } |
| 4332 | } |
| 4333 | |
| 4334 | @test unsafe fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) { |
| 4335 | let mut testArena283 = testArena(); |
| 4336 | let testStorage283: 'test283 = &mut testArena283 in { |
| 4337 | let mut a = testResolver(testStorage283); |
| 4338 | let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);"; |
| 4339 | let result = try resolveProgramStr(&mut a, program); |
| 4340 | try expectNoErrors(&result); |
| 4341 | |
| 4342 | let stmt = try getBlockStmt(result.root, 1); |
| 4343 | let expr = try expectExprStmtType(&a, stmt, super::Type::U32); |
| 4344 | try expectConstInt(&a, expr, 8); |
| 4345 | } |
| 4346 | } |
| 4347 | |
| 4348 | @test unsafe fn testResolveAlignAnnotation() throws (testing::TestError) { |
| 4349 | { |
| 4350 | let mut testArena284 = testArena(); |
| 4351 | let testStorage284: 'test284 = &mut testArena284 in { |
| 4352 | let mut a = testResolver(testStorage284); |
| 4353 | let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;"); |
| 4354 | try expectNoErrors(&result); |
| 4355 | |
| 4356 | let stmt = try getBlockStmt(result.root, 0); |
| 4357 | let sym = super::symbolFor(&a, stmt) |
| 4358 | else throw testing::TestError::Failed; |
| 4359 | let case super::SymbolData::Value { type: valType, .. } = sym.data |
| 4360 | else throw testing::TestError::Failed; |
| 4361 | let layout = super::getLayout(&a, sym.node, valType); |
| 4362 | try testing::expect(layout.alignment == 8); |
| 4363 | } |
| 4364 | } { |
| 4365 | let mut testArena285 = testArena(); |
| 4366 | let testStorage285: 'test285 = &mut testArena285 in { |
| 4367 | let mut a = testResolver(testStorage285); |
| 4368 | let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;"); |
| 4369 | let err = try expectError(&result); |
| 4370 | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
| 4371 | else throw testing::TestError::Failed; |
| 4372 | try testing::expect(val == 3); |
| 4373 | } |
| 4374 | } { |
| 4375 | let mut testArena286 = testArena(); |
| 4376 | let testStorage286: 'test286 = &mut testArena286 in { |
| 4377 | let mut a = testResolver(testStorage286); |
| 4378 | let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;"); |
| 4379 | let err = try expectError(&result); |
| 4380 | let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind |
| 4381 | else throw testing::TestError::Failed; |
| 4382 | try testing::expect(val == 7); |
| 4383 | } |
| 4384 | } |
| 4385 | } |
| 4386 | |
| 4387 | @test unsafe fn testResolveVoidAssignmentError() throws (testing::TestError) { |
| 4388 | { |
| 4389 | let mut testArena287 = testArena(); |
| 4390 | let testStorage287: 'test287 = &mut testArena287 in { |
| 4391 | let mut a = testResolver(testStorage287); |
| 4392 | let program = "fn voidFn() {} let _ = voidFn();"; |
| 4393 | let result = try resolveProgramStr(&mut a, program); |
| 4394 | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
| 4395 | } |
| 4396 | } { |
| 4397 | let mut testArena288 = testArena(); |
| 4398 | let testStorage288: 'test288 = &mut testArena288 in { |
| 4399 | let mut a = testResolver(testStorage288); |
| 4400 | let program = "fn voidFn() {} let x = voidFn();"; |
| 4401 | let result = try resolveProgramStr(&mut a, program); |
| 4402 | try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid); |
| 4403 | } |
| 4404 | } |
| 4405 | } |
| 4406 | |
| 4407 | // |
| 4408 | // Module Declaration Tests |
| 4409 | // |
| 4410 | |
| 4411 | @test unsafe fn testResolveEmptyMod() throws (testing::TestError) { |
| 4412 | let mut testArena289 = testArena(); |
| 4413 | let testStorage289: 'test289 = &mut testArena289 in { |
| 4414 | let mut a = testResolver(testStorage289); |
| 4415 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4416 | let mut graph = &mut MODULE_GRAPH; |
| 4417 | |
| 4418 | let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena); |
| 4419 | let childId = try registerModule(graph, rootId, "child", "{}", &mut arena); |
| 4420 | let result = try resolveModuleTree(&mut a, rootId); |
| 4421 | try expectNoErrors(&result); |
| 4422 | } |
| 4423 | } |
| 4424 | |
| 4425 | @test unsafe fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) { |
| 4426 | let mut testArena290 = testArena(); |
| 4427 | let testStorage290: 'test290 = &mut testArena290 in { |
| 4428 | let mut a = testResolver(testStorage290); |
| 4429 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4430 | |
| 4431 | // Register root and util modules. |
| 4432 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena); |
| 4433 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena); |
| 4434 | |
| 4435 | // Resolve should fail: the parent module is not in scope. |
| 4436 | let result = try resolveModuleTree(&mut a, rootId); |
| 4437 | let err = try expectError(&result); |
| 4438 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 4439 | else throw testing::TestError::Failed; |
| 4440 | try testing::expect(mem::eq(name, "helper")); |
| 4441 | } |
| 4442 | } |
| 4443 | |
| 4444 | @test unsafe fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) { |
| 4445 | let mut testArena291 = testArena(); |
| 4446 | let testStorage291: 'test291 = &mut testArena291 in { |
| 4447 | let mut a = testResolver(testStorage291); |
| 4448 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4449 | |
| 4450 | // Register root and util modules. |
| 4451 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena); |
| 4452 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena); |
| 4453 | |
| 4454 | // Resolve should succeed: parent can access child. |
| 4455 | let result = try resolveModuleTree(&mut a, rootId); |
| 4456 | try expectNoErrors(&result); |
| 4457 | } |
| 4458 | } |
| 4459 | |
| 4460 | @test unsafe fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) { |
| 4461 | let mut testArena292 = testArena(); |
| 4462 | let testStorage292: 'test292 = &mut testArena292 in { |
| 4463 | let mut a = testResolver(testStorage292); |
| 4464 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4465 | |
| 4466 | // Register root with two sibling modules. |
| 4467 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
| 4468 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena); |
| 4469 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
| 4470 | |
| 4471 | // Resolve should fail: siblings can't access each other directly. |
| 4472 | let result = try resolveModuleTree(&mut a, rootId); |
| 4473 | let err = try expectError(&result); |
| 4474 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 4475 | else throw testing::TestError::Failed; |
| 4476 | try testing::expect(mem::eq(name, "patrick")); |
| 4477 | } |
| 4478 | } |
| 4479 | |
| 4480 | @test unsafe fn testResolveSiblingModulesViaRoot() throws (testing::TestError) { |
| 4481 | let mut testArena293 = testArena(); |
| 4482 | let testStorage293: 'test293 = &mut testArena293 in { |
| 4483 | let mut a = testResolver(testStorage293); |
| 4484 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4485 | |
| 4486 | // Register root with two sibling modules. |
| 4487 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
| 4488 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena); |
| 4489 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
| 4490 | |
| 4491 | // Resolve should succeed: siblings can access each other via root. |
| 4492 | let result = try resolveModuleTree(&mut a, rootId); |
| 4493 | try expectNoErrors(&result); |
| 4494 | } |
| 4495 | } |
| 4496 | |
| 4497 | @test unsafe fn testResolveModuleMutualRecursion() throws (testing::TestError) { |
| 4498 | let mut testArena294 = testArena(); |
| 4499 | let testStorage294: 'test294 = &mut testArena294 in { |
| 4500 | let mut a = testResolver(testStorage294); |
| 4501 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4502 | |
| 4503 | // Register root with two sibling modules that call each other. |
| 4504 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena); |
| 4505 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena); |
| 4506 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena); |
| 4507 | |
| 4508 | // Resolve should succeed: cyclic use is allowed. |
| 4509 | let result = try resolveModuleTree(&mut a, rootId); |
| 4510 | try expectNoErrors(&result); |
| 4511 | } |
| 4512 | } |
| 4513 | |
| 4514 | @test unsafe fn testResolveAccessModuleType() throws (testing::TestError) { |
| 4515 | let mut testArena295 = testArena(); |
| 4516 | let testStorage295: 'test295 = &mut testArena295 in { |
| 4517 | let mut a = testResolver(testStorage295); |
| 4518 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4519 | |
| 4520 | // Register root with types module containing a record. |
| 4521 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 4522 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena); |
| 4523 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena); |
| 4524 | |
| 4525 | // Resolve should succeed: types can be accessed. |
| 4526 | let result = try resolveModuleTree(&mut a, rootId); |
| 4527 | try expectNoErrors(&result); |
| 4528 | } |
| 4529 | } |
| 4530 | |
| 4531 | @test unsafe fn testResolveAccessModuleConstant() throws (testing::TestError) { |
| 4532 | let mut testArena296 = testArena(); |
| 4533 | let testStorage296: 'test296 = &mut testArena296 in { |
| 4534 | let mut a = testResolver(testStorage296); |
| 4535 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4536 | |
| 4537 | // Register root with constants module. |
| 4538 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 4539 | let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena); |
| 4540 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena); |
| 4541 | |
| 4542 | // Resolve should succeed: constants can be accessed. |
| 4543 | let result = try resolveModuleTree(&mut a, rootId); |
| 4544 | try expectNoErrors(&result); |
| 4545 | } |
| 4546 | } |
| 4547 | |
| 4548 | @test unsafe fn testResolveRootSymbolMustBeImported() throws (testing::TestError) { |
| 4549 | let mut testArena297 = testArena(); |
| 4550 | let testStorage297: 'test297 = &mut testArena297 in { |
| 4551 | let mut a = testResolver(testStorage297); |
| 4552 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4553 | |
| 4554 | // Register deeply nested modules: `root::app::services::auth`. |
| 4555 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena); |
| 4556 | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena); |
| 4557 | |
| 4558 | // Resolve should fail: the `root` module must be imported. |
| 4559 | let result = try resolveModuleTree(&mut a, rootId); |
| 4560 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root")); |
| 4561 | } |
| 4562 | } |
| 4563 | |
| 4564 | @test unsafe fn testResolveUseImportsNestedSymbol() throws (testing::TestError) { |
| 4565 | let mut testArena298 = testArena(); |
| 4566 | let testStorage298: 'test298 = &mut testArena298 in { |
| 4567 | let mut a = testResolver(testStorage298); |
| 4568 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4569 | |
| 4570 | // Register deeply nested modules: `root::app::services::auth`. |
| 4571 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena); |
| 4572 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena); |
| 4573 | let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena); |
| 4574 | let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena); |
| 4575 | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena); |
| 4576 | let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena); |
| 4577 | |
| 4578 | // Resolve should succeed: use imports the module symbol. |
| 4579 | let result = try resolveModuleTree(&mut a, rootId); |
| 4580 | try expectNoErrors(&result); |
| 4581 | } |
| 4582 | } |
| 4583 | |
| 4584 | @test unsafe fn testResolveUseNonExistentModule() throws (testing::TestError) { |
| 4585 | let mut testArena299 = testArena(); |
| 4586 | let testStorage299: 'test299 = &mut testArena299 in { |
| 4587 | let mut a = testResolver(testStorage299); |
| 4588 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4589 | |
| 4590 | // Register root with app trying to use a non-existent module. |
| 4591 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena); |
| 4592 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena); |
| 4593 | |
| 4594 | // Resolve should fail: module doesn't exist. |
| 4595 | let result = try resolveModuleTree(&mut a, rootId); |
| 4596 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown")); |
| 4597 | } |
| 4598 | } |
| 4599 | |
| 4600 | @test unsafe fn testResolveUsePrivateFn() throws (testing::TestError) { |
| 4601 | let mut testArena300 = testArena(); |
| 4602 | let testStorage300: 'test300 = &mut testArena300 in { |
| 4603 | let mut a = testResolver(testStorage300); |
| 4604 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4605 | |
| 4606 | // Register root with util module containing a private function. |
| 4607 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena); |
| 4608 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena); |
| 4609 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena); |
| 4610 | |
| 4611 | // Resolve should fail: function is not public. |
| 4612 | let result = try resolveModuleTree(&mut a, rootId); |
| 4613 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
| 4614 | } |
| 4615 | } |
| 4616 | |
| 4617 | @test unsafe fn testResolveUsePrivateMod() throws (testing::TestError) { |
| 4618 | let mut testArena301 = testArena(); |
| 4619 | let testStorage301: 'test301 = &mut testArena301 in { |
| 4620 | let mut a = testResolver(testStorage301); |
| 4621 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4622 | |
| 4623 | // Register root with public and private child modules. |
| 4624 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena); |
| 4625 | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena); |
| 4626 | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena); |
| 4627 | |
| 4628 | // Resolve should fail: module is not public. |
| 4629 | let result = try resolveModuleTree(&mut a, rootId); |
| 4630 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
| 4631 | } |
| 4632 | } |
| 4633 | |
| 4634 | @test unsafe fn testResolveUsePublicMod() throws (testing::TestError) { |
| 4635 | let mut testArena302 = testArena(); |
| 4636 | let testStorage302: 'test302 = &mut testArena302 in { |
| 4637 | let mut a = testResolver(testStorage302); |
| 4638 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4639 | |
| 4640 | // Register root with public and private child modules. |
| 4641 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena); |
| 4642 | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena); |
| 4643 | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena); |
| 4644 | |
| 4645 | // Resolve should succeed: module is public. |
| 4646 | let result = try resolveModuleTree(&mut a, rootId); |
| 4647 | try expectNoErrors(&result); |
| 4648 | } |
| 4649 | } |
| 4650 | |
| 4651 | @test unsafe fn testResolveUseNonPublicType() throws (testing::TestError) { |
| 4652 | let mut testArena303 = testArena(); |
| 4653 | let testStorage303: 'test303 = &mut testArena303 in { |
| 4654 | let mut a = testResolver(testStorage303); |
| 4655 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4656 | |
| 4657 | // Register root with types module containing a private record. |
| 4658 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 4659 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena); |
| 4660 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena); |
| 4661 | |
| 4662 | // Resolve should fail: record is not public. |
| 4663 | let result = try resolveModuleTree(&mut a, rootId); |
| 4664 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv")); |
| 4665 | } |
| 4666 | } |
| 4667 | |
| 4668 | @test unsafe fn testResolveImportPublicType() throws (testing::TestError) { |
| 4669 | let mut testArena304 = testArena(); |
| 4670 | let testStorage304: 'test304 = &mut testArena304 in { |
| 4671 | let mut a = testResolver(testStorage304); |
| 4672 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4673 | |
| 4674 | // Register root with types module containing a public record. |
| 4675 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
| 4676 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena); |
| 4677 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena); |
| 4678 | |
| 4679 | // Resolve should succeed: record is public. |
| 4680 | let result = try resolveModuleTree(&mut a, rootId); |
| 4681 | try expectNoErrors(&result); |
| 4682 | } |
| 4683 | } |
| 4684 | |
| 4685 | @test unsafe fn testResolveUseNonPublicStatic() throws (testing::TestError) { |
| 4686 | let mut testArena305 = testArena(); |
| 4687 | let testStorage305: 'test305 = &mut testArena305 in { |
| 4688 | let mut a = testResolver(testStorage305); |
| 4689 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4690 | |
| 4691 | // Register root with statics module containing a private static. |
| 4692 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
| 4693 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena); |
| 4694 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena); |
| 4695 | |
| 4696 | // Resolve should fail: static is not public. |
| 4697 | let result = try resolveModuleTree(&mut a, rootId); |
| 4698 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE")); |
| 4699 | } |
| 4700 | } |
| 4701 | |
| 4702 | @test unsafe fn testResolveImportPublicStatic() throws (testing::TestError) { |
| 4703 | let mut testArena306 = testArena(); |
| 4704 | let testStorage306: 'test306 = &mut testArena306 in { |
| 4705 | let mut a = testResolver(testStorage306); |
| 4706 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4707 | |
| 4708 | // Register root with statics module containing a public static. |
| 4709 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
| 4710 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena); |
| 4711 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena); |
| 4712 | |
| 4713 | // Resolve should succeed: static is public. |
| 4714 | let result = try resolveModuleTree(&mut a, rootId); |
| 4715 | try expectNoErrors(&result); |
| 4716 | } |
| 4717 | } |
| 4718 | |
| 4719 | @test unsafe fn testResolveAccessSuper() throws (testing::TestError) { |
| 4720 | { |
| 4721 | let mut testArena307 = testArena(); |
| 4722 | let testStorage307: 'test307 = &mut testArena307 in { |
| 4723 | let mut a = testResolver(testStorage307); |
| 4724 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4725 | |
| 4726 | // Test function access. |
| 4727 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena); |
| 4728 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena); |
| 4729 | let result = try resolveModuleTree(&mut a, rootId); |
| 4730 | try expectNoErrors(&result); |
| 4731 | } |
| 4732 | } { |
| 4733 | let mut testArena308 = testArena(); |
| 4734 | let testStorage308: 'test308 = &mut testArena308 in { |
| 4735 | let mut a = testResolver(testStorage308); |
| 4736 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4737 | |
| 4738 | // Test type access. |
| 4739 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena); |
| 4740 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena); |
| 4741 | let result = try resolveModuleTree(&mut a, rootId); |
| 4742 | try expectNoErrors(&result); |
| 4743 | } |
| 4744 | } |
| 4745 | } |
| 4746 | |
| 4747 | /// Test nested super access to union variants (e.g. `super::E::A`). |
| 4748 | @test unsafe fn testResolveSuperUnionVariant() throws (testing::TestError) { |
| 4749 | let mut testArena309 = testArena(); |
| 4750 | let testStorage309: 'test309 = &mut testArena309 in { |
| 4751 | let mut a = testResolver(testStorage309); |
| 4752 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4753 | |
| 4754 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena); |
| 4755 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c", |
| 4756 | "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }", |
| 4757 | &mut arena); |
| 4758 | let result = try resolveModuleTree(&mut a, rootId); |
| 4759 | try expectNoErrors(&result); |
| 4760 | } |
| 4761 | } |
| 4762 | |
| 4763 | @test unsafe fn testResolveUseSuper() throws (testing::TestError) { |
| 4764 | let mut testArena310 = testArena(); |
| 4765 | let testStorage310: 'test310 = &mut testArena310 in { |
| 4766 | let mut a = testResolver(testStorage310); |
| 4767 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4768 | |
| 4769 | // Register root with a function, and a child module that uses super to access it. |
| 4770 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena); |
| 4771 | let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena); |
| 4772 | let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena); |
| 4773 | |
| 4774 | // Resolve should succeed - super allows accessing parent module. |
| 4775 | let result = try resolveModuleTree(&mut a, rootId); |
| 4776 | try expectNoErrors(&result); |
| 4777 | } |
| 4778 | } |
| 4779 | |
| 4780 | @test unsafe fn testResolveModNotFound() throws (testing::TestError) { |
| 4781 | let mut testArena311 = testArena(); |
| 4782 | let testStorage311: 'test311 = &mut testArena311 in { |
| 4783 | let mut a = testResolver(testStorage311); |
| 4784 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4785 | |
| 4786 | // Register root that declares a module that doesn't exist. |
| 4787 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena); |
| 4788 | |
| 4789 | // Resolve should fail: module doesn't exist. |
| 4790 | let result = try resolveModuleTree(&mut a, rootId); |
| 4791 | let err = try expectError(&result); |
| 4792 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 4793 | else throw testing::TestError::Failed; |
| 4794 | try testing::expect(mem::eq(name, "unknown")); |
| 4795 | } |
| 4796 | } |
| 4797 | |
| 4798 | @test unsafe fn testResolveDuplicateSubModule() throws (testing::TestError) { |
| 4799 | let mut testArena312 = testArena(); |
| 4800 | let testStorage312: 'test312 = &mut testArena312 in { |
| 4801 | let mut a = testResolver(testStorage312); |
| 4802 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4803 | |
| 4804 | // Register root that declares a module twice. |
| 4805 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena); |
| 4806 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
| 4807 | |
| 4808 | // Resolve should fail: can't declare the same module twice. |
| 4809 | let result = try resolveModuleTree(&mut a, rootId); |
| 4810 | let err = try expectError(&result); |
| 4811 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
| 4812 | } |
| 4813 | } |
| 4814 | |
| 4815 | @test unsafe fn testResolveUseSubModule() throws (testing::TestError) { |
| 4816 | let mut testArena313 = testArena(); |
| 4817 | let testStorage313: 'test313 = &mut testArena313 in { |
| 4818 | let mut a = testResolver(testStorage313); |
| 4819 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4820 | |
| 4821 | // Register root that declares and imports the same module. |
| 4822 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena); |
| 4823 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena); |
| 4824 | |
| 4825 | // Resolve should fail: Both `mod` and `use` are trying to create the same binding. |
| 4826 | let result = try resolveModuleTree(&mut a, rootId); |
| 4827 | let err = try expectError(&result); |
| 4828 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child")); |
| 4829 | } |
| 4830 | } |
| 4831 | |
| 4832 | @test unsafe fn testResolveDuplicateUse() throws (testing::TestError) { |
| 4833 | let mut testArena314 = testArena(); |
| 4834 | let testStorage314: 'test314 = &mut testArena314 in { |
| 4835 | let mut a = testResolver(testStorage314); |
| 4836 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4837 | |
| 4838 | // Register a module that imports the same module twice. |
| 4839 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena); |
| 4840 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena); |
| 4841 | |
| 4842 | // Resolve should fail. |
| 4843 | let result = try resolveModuleTree(&mut a, rootId); |
| 4844 | let err = try expectError(&result); |
| 4845 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root")); |
| 4846 | } |
| 4847 | } |
| 4848 | |
| 4849 | /// Test that opaque pointers are allowed in record fields. |
| 4850 | @test unsafe fn testOpaquePointerInRecordField() throws (testing::TestError) { |
| 4851 | let mut testArena315 = testArena(); |
| 4852 | let testStorage315: 'test315 = &mut testArena315 in { |
| 4853 | let mut a = testResolver(testStorage315); |
| 4854 | let result = try resolveProgramStr(&mut a, "record T { x: *opaque }"); |
| 4855 | try expectNoErrors(&result); |
| 4856 | } |
| 4857 | } |
| 4858 | |
| 4859 | /// You cannot use `@sizeOf` or `@alignOf` on opaque type. |
| 4860 | @test unsafe fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) { |
| 4861 | let mut testArena316 = testArena(); |
| 4862 | let testStorage316: 'test316 = &mut testArena316 in { |
| 4863 | let mut a = testResolver(testStorage316); |
| 4864 | |
| 4865 | let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)"); |
| 4866 | let err1 = try expectError(&result1); |
| 4867 | try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed); |
| 4868 | |
| 4869 | let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)"); |
| 4870 | let err2 = try expectError(&result2); |
| 4871 | try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed); |
| 4872 | } |
| 4873 | } |
| 4874 | |
| 4875 | /// Test that immutable slice/pointer parameters cannot be borrowed mutably. |
| 4876 | @test unsafe fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) { |
| 4877 | let mut testArena317 = testArena(); |
| 4878 | let testStorage317: 'test317 = &mut testArena317 in { |
| 4879 | let mut a = testResolver(testStorage317); |
| 4880 | let program = "fn f(p: *i32) { let x = &mut *p; }"; |
| 4881 | let result = try resolveProgramStr(&mut a, program); |
| 4882 | let err = try expectError(&result); |
| 4883 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4884 | } |
| 4885 | } |
| 4886 | |
| 4887 | /// Test that immutable slice parameters cannot be borrowed mutably. |
| 4888 | @test unsafe fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) { |
| 4889 | let mut testArena318 = testArena(); |
| 4890 | let testStorage318: 'test318 = &mut testArena318 in { |
| 4891 | let mut a = testResolver(testStorage318); |
| 4892 | let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }"; |
| 4893 | let result = try resolveProgramStr(&mut a, program); |
| 4894 | let err = try expectError(&result); |
| 4895 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4896 | } |
| 4897 | } |
| 4898 | |
| 4899 | /// Test that mutable pointer parameters can be borrowed mutably. |
| 4900 | @test unsafe fn testMutableBorrowFromMutablePointer() throws (testing::TestError) { |
| 4901 | let mut testArena319 = testArena(); |
| 4902 | let testStorage319: 'test319 = &mut testArena319 in { |
| 4903 | let mut a = testResolver(testStorage319); |
| 4904 | let program = "fn f(p: *mut i32) { let x: *mut i32 = &mut *p; }"; |
| 4905 | let result = try resolveProgramStr(&mut a, program); |
| 4906 | try expectNoErrors(&result); |
| 4907 | } |
| 4908 | } |
| 4909 | |
| 4910 | /// Test that mutable slice parameters can be borrowed mutably. |
| 4911 | @test unsafe fn testMutableBorrowFromMutableSlice() throws (testing::TestError) { |
| 4912 | let mut testArena320 = testArena(); |
| 4913 | let testStorage320: 'test320 = &mut testArena320 in { |
| 4914 | let mut a = testResolver(testStorage320); |
| 4915 | let program = "fn f(s: *mut [i32]) { let x: *mut i32 = &mut s[0]; }"; |
| 4916 | let result = try resolveProgramStr(&mut a, program); |
| 4917 | try expectNoErrors(&result); |
| 4918 | } |
| 4919 | } |
| 4920 | |
| 4921 | /// Test borrowing mutably from a field access on a call returning `*mut`. |
| 4922 | @test unsafe fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) { |
| 4923 | let mut testArena321 = testArena(); |
| 4924 | let testStorage321: 'test321 = &mut testArena321 in { |
| 4925 | let mut a = testResolver(testStorage321); |
| 4926 | let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } fn f() { static b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; }"; |
| 4927 | let result = try resolveProgramStr(&mut a, program); |
| 4928 | try expectNoErrors(&result); |
| 4929 | } |
| 4930 | } |
| 4931 | |
| 4932 | /// Test that calls returning immutable pointers cannot be mutably borrowed. |
| 4933 | @test unsafe fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) { |
| 4934 | let mut testArena322 = testArena(); |
| 4935 | let testStorage322: 'test322 = &mut testArena322 in { |
| 4936 | let mut a = testResolver(testStorage322); |
| 4937 | let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn f() { constant b: Box = Box { x: 1 }; let px: *mut i32 = &mut idBox(&b).x; }"; |
| 4938 | let result = try resolveProgramStr(&mut a, program); |
| 4939 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4940 | } |
| 4941 | } |
| 4942 | |
| 4943 | /// Test borrowing mutably from a public static through scope access. |
| 4944 | @test unsafe fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) { |
| 4945 | let mut testArena323 = testArena(); |
| 4946 | let testStorage323: 'test323 = &mut testArena323 in { |
| 4947 | let mut a = testResolver(testStorage323); |
| 4948 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4949 | |
| 4950 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
| 4951 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena); |
| 4952 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() { let p: *mut i32 = &mut statics::COUNTER; set *p = 7; }", &mut arena); |
| 4953 | |
| 4954 | let result = try resolveModuleTree(&mut a, rootId); |
| 4955 | try expectNoErrors(&result); |
| 4956 | } |
| 4957 | } |
| 4958 | |
| 4959 | /// Test that constants through scope access cannot be mutably borrowed. |
| 4960 | @test unsafe fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) { |
| 4961 | let mut testArena324 = testArena(); |
| 4962 | let testStorage324: 'test324 = &mut testArena324 in { |
| 4963 | let mut a = testResolver(testStorage324); |
| 4964 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 4965 | |
| 4966 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 4967 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena); |
| 4968 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() { let p: *mut i32 = &mut consts::LIMIT; set *p = 9; }", &mut arena); |
| 4969 | |
| 4970 | let result = try resolveModuleTree(&mut a, rootId); |
| 4971 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4972 | } |
| 4973 | } |
| 4974 | |
| 4975 | /// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer. |
| 4976 | @test unsafe fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) { |
| 4977 | let mut testArena325 = testArena(); |
| 4978 | let testStorage325: 'test325 = &mut testArena325 in { |
| 4979 | let mut a = testResolver(testStorage325); |
| 4980 | let program = "fn f() { static x: i32 = 1; let p: *i32 = &x; let y = &mut *p; }"; |
| 4981 | let result = try resolveProgramStr(&mut a, program); |
| 4982 | let err = try expectError(&result); |
| 4983 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4984 | } |
| 4985 | } |
| 4986 | |
| 4987 | /// Test that mutable pointer to immutable slice cannot be assigned through index. |
| 4988 | /// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but |
| 4989 | /// the inner slice is immutable, so we shouldn't be able to mutate the elements. |
| 4990 | @test unsafe fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) { |
| 4991 | let mut testArena326 = testArena(); |
| 4992 | let testStorage326: 'test326 = &mut testArena326 in { |
| 4993 | let mut a = testResolver(testStorage326); |
| 4994 | let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }"; |
| 4995 | let result = try resolveProgramStr(&mut a, program); |
| 4996 | |
| 4997 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 4998 | } |
| 4999 | } |
| 5000 | |
| 5001 | /// Test that mutable slice parameters can be assigned through index. |
| 5002 | @test unsafe fn testAssignThroughMutableSliceParam() throws (testing::TestError) { |
| 5003 | { |
| 5004 | // Mutable slice param: direct assignment should work |
| 5005 | let mut testArena327 = testArena(); |
| 5006 | let testStorage327: 'test327 = &mut testArena327 in { |
| 5007 | let mut a = testResolver(testStorage327); |
| 5008 | let program = "fn f(slice: *mut [i32]) { set slice[0] = 1; }"; |
| 5009 | let result = try resolveProgramStr(&mut a, program); |
| 5010 | try expectNoErrors(&result); |
| 5011 | } |
| 5012 | } { |
| 5013 | // Immutable slice param: direct assignment should fail |
| 5014 | let mut testArena328 = testArena(); |
| 5015 | let testStorage328: 'test328 = &mut testArena328 in { |
| 5016 | let mut a = testResolver(testStorage328); |
| 5017 | let program = "fn f(slice: *[i32]) { set slice[0] = 1; }"; |
| 5018 | let result = try resolveProgramStr(&mut a, program); |
| 5019 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 5020 | } |
| 5021 | } |
| 5022 | } |
| 5023 | |
| 5024 | /// Test range end type coercion with assignable types. |
| 5025 | @test unsafe fn testRangeEndTypeCoercion() throws (testing::TestError) { |
| 5026 | { |
| 5027 | let mut testArena329 = testArena(); |
| 5028 | let testStorage329: 'test329 = &mut testArena329 in { |
| 5029 | let mut a = testResolver(testStorage329); |
| 5030 | let program = "fn f(end: u32) { for i in 0..end {} }"; |
| 5031 | let result = try resolveProgramStr(&mut a, program); |
| 5032 | try expectNoErrors(&result); |
| 5033 | } |
| 5034 | } { |
| 5035 | let mut testArena330 = testArena(); |
| 5036 | let testStorage330: 'test330 = &mut testArena330 in { |
| 5037 | let mut a = testResolver(testStorage330); |
| 5038 | let program = "fn f(start: u32) { for i in start..9 {} }"; |
| 5039 | let result = try resolveProgramStr(&mut a, program); |
| 5040 | try expectNoErrors(&result); |
| 5041 | } |
| 5042 | } |
| 5043 | } |
| 5044 | |
| 5045 | /// Mixed-width range bounds require an explicit cast. |
| 5046 | @test unsafe fn testRangeEndTypeSubType() throws (testing::TestError) { |
| 5047 | { |
| 5048 | let mut testArena331 = testArena(); |
| 5049 | let testStorage331: 'test331 = &mut testArena331 in { |
| 5050 | let mut a = testResolver(testStorage331); |
| 5051 | let program = "fn f(start: i8, end: u32) { for i in start..end {} }"; |
| 5052 | let result = try resolveProgramStr(&mut a, program); |
| 5053 | let err = try expectError(&result); |
| 5054 | try expectTypeMismatch(err, super::Type::I8, super::Type::U32); |
| 5055 | } |
| 5056 | } { |
| 5057 | let mut testArena332 = testArena(); |
| 5058 | let testStorage332: 'test332 = &mut testArena332 in { |
| 5059 | let mut a = testResolver(testStorage332); |
| 5060 | let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }"; |
| 5061 | let result = try resolveProgramStr(&mut a, program); |
| 5062 | try expectNoErrors(&result); |
| 5063 | } |
| 5064 | } |
| 5065 | } |
| 5066 | |
| 5067 | /// Test that try-catch expressions in statement context accept mismatched types. |
| 5068 | @test unsafe fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) { |
| 5069 | let mut testArena333 = testArena(); |
| 5070 | let testStorage333: 'test333 = &mut testArena333 in { |
| 5071 | let mut a = testResolver(testStorage333); |
| 5072 | let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
| 5073 | let result = try resolveProgramStr(&mut a, program); |
| 5074 | try expectNoErrors(&result); |
| 5075 | } |
| 5076 | } |
| 5077 | |
| 5078 | /// Test that try-catch blocks in value context require divergence or void. |
| 5079 | @test unsafe fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) { |
| 5080 | let mut testArena334 = testArena(); |
| 5081 | let testStorage334: 'test334 = &mut testArena334 in { |
| 5082 | let mut a = testResolver(testStorage334); |
| 5083 | let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }"; |
| 5084 | let result = try resolveProgramStr(&mut a, program); |
| 5085 | let err = try expectError(&result); |
| 5086 | try expectTypeMismatch(err, super::Type::Bool, super::Type::Void); |
| 5087 | } |
| 5088 | } |
| 5089 | |
| 5090 | /// Test that try-catch blocks in value context work when they diverge. |
| 5091 | @test unsafe fn testTryCatchInValueContextDiverges() throws (testing::TestError) { |
| 5092 | let mut testArena335 = testArena(); |
| 5093 | let testStorage335: 'test335 = &mut testArena335 in { |
| 5094 | let mut a = testResolver(testStorage335); |
| 5095 | let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }"; |
| 5096 | let result = try resolveProgramStr(&mut a, program); |
| 5097 | try expectNoErrors(&result); |
| 5098 | } |
| 5099 | } |
| 5100 | |
| 5101 | /// Test that `try?` lifts result type to optional. |
| 5102 | @test unsafe fn testTryOptionalLiftsToOptional() throws (testing::TestError) { |
| 5103 | let mut testArena336 = testArena(); |
| 5104 | let testStorage336: 'test336 = &mut testArena336 in { |
| 5105 | let mut a = testResolver(testStorage336); |
| 5106 | let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }"; |
| 5107 | let result = try resolveProgramStr(&mut a, program); |
| 5108 | try expectNoErrors(&result); |
| 5109 | } |
| 5110 | } |
| 5111 | |
| 5112 | /// Test that record fields can be assigned if the record binding is mutable. |
| 5113 | @test unsafe fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) { |
| 5114 | let mut testArena337 = testArena(); |
| 5115 | let testStorage337: 'test337 = &mut testArena337 in { |
| 5116 | let mut a = testResolver(testStorage337); |
| 5117 | let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }"; |
| 5118 | let result = try resolveProgramStr(&mut a, program); |
| 5119 | try expectNoErrors(&result); |
| 5120 | } |
| 5121 | } |
| 5122 | |
| 5123 | /// Test that record fields cannot be assigned if the record binding is immutable. |
| 5124 | @test unsafe fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) { |
| 5125 | let mut testArena338 = testArena(); |
| 5126 | let testStorage338: 'test338 = &mut testArena338 in { |
| 5127 | let mut a = testResolver(testStorage338); |
| 5128 | let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }"; |
| 5129 | let result = try resolveProgramStr(&mut a, program); |
| 5130 | let err = try expectError(&result); |
| 5131 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 5132 | } |
| 5133 | } |
| 5134 | |
| 5135 | /// Test that record fields can be assigned through a mutable pointer. |
| 5136 | @test unsafe fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) { |
| 5137 | let mut testArena339 = testArena(); |
| 5138 | let testStorage339: 'test339 = &mut testArena339 in { |
| 5139 | let mut a = testResolver(testStorage339); |
| 5140 | let program = "record S { x: i32 } fn f(p: *mut S) { set p.x = 2; }"; |
| 5141 | let result = try resolveProgramStr(&mut a, program); |
| 5142 | try expectNoErrors(&result); |
| 5143 | } |
| 5144 | } |
| 5145 | |
| 5146 | /// Test that record fields cannot be assigned through an immutable pointer. |
| 5147 | @test unsafe fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) { |
| 5148 | let mut testArena340 = testArena(); |
| 5149 | let testStorage340: 'test340 = &mut testArena340 in { |
| 5150 | let mut a = testResolver(testStorage340); |
| 5151 | let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }"; |
| 5152 | let result = try resolveProgramStr(&mut a, program); |
| 5153 | let err = try expectError(&result); |
| 5154 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 5155 | } |
| 5156 | } |
| 5157 | |
| 5158 | // Opaque pointer tests. |
| 5159 | |
| 5160 | /// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast. |
| 5161 | @test unsafe fn testOpaquePointerAutoCoercion() throws (testing::TestError) { |
| 5162 | let mut testArena341 = testArena(); |
| 5163 | let testStorage341: 'test341 = &mut testArena341 in { |
| 5164 | let mut a = testResolver(testStorage341); |
| 5165 | let result = try resolveProgramStr(&mut a, "unsafe fn f(x: *i32) { let mut ptr: *i32 = x; let o: *opaque = ptr; set ptr = o as *i32; }"); |
| 5166 | try expectNoErrors(&result); |
| 5167 | } |
| 5168 | } |
| 5169 | |
| 5170 | /// You cannot assign an opaque pointer to a non-opaque pointer without a cast. |
| 5171 | @test unsafe fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) { |
| 5172 | let mut testArena342 = testArena(); |
| 5173 | let testStorage342: 'test342 = &mut testArena342 in { |
| 5174 | let mut a = testResolver(testStorage342); |
| 5175 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let ptr: *i32 = o; }"); |
| 5176 | let err = try expectError(&result); |
| 5177 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
| 5178 | else throw testing::TestError::Failed; |
| 5179 | let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected |
| 5180 | else throw testing::TestError::Failed; |
| 5181 | let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual |
| 5182 | else throw testing::TestError::Failed; |
| 5183 | |
| 5184 | try testing::expect(*expectedTarget == super::Type::I32); |
| 5185 | try testing::expect(*actualTarget == super::Type::Opaque); |
| 5186 | } |
| 5187 | } |
| 5188 | |
| 5189 | /// You cannot have a value of type `opaque` (function parameter). |
| 5190 | @test unsafe fn testOpaqueValue() throws (testing::TestError) { |
| 5191 | { |
| 5192 | let mut testArena343 = testArena(); |
| 5193 | let testStorage343: 'test343 = &mut testArena343 in { |
| 5194 | let mut a = testResolver(testStorage343); |
| 5195 | let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}"); |
| 5196 | let err = try expectError(&result); |
| 5197 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
| 5198 | } |
| 5199 | } { |
| 5200 | let mut testArena344 = testArena(); |
| 5201 | let testStorage344: 'test344 = &mut testArena344 in { |
| 5202 | let mut a = testResolver(testStorage344); |
| 5203 | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let x: opaque = undefined; }"); |
| 5204 | let err = try expectError(&result); |
| 5205 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
| 5206 | } |
| 5207 | } { |
| 5208 | let mut testArena345 = testArena(); |
| 5209 | let testStorage345: 'test345 = &mut testArena345 in { |
| 5210 | let mut a = testResolver(testStorage345); |
| 5211 | let result = try resolveProgramStr(&mut a, "record R { x: opaque }"); |
| 5212 | let err = try expectError(&result); |
| 5213 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
| 5214 | } |
| 5215 | } |
| 5216 | } |
| 5217 | |
| 5218 | /// You cannot dereference an opaque pointer, you have to cast it first. |
| 5219 | @test unsafe fn testOpaquePointerNoDereference() throws (testing::TestError) { |
| 5220 | let mut testArena346 = testArena(); |
| 5221 | let testStorage346: 'test346 = &mut testArena346 in { |
| 5222 | let mut a = testResolver(testStorage346); |
| 5223 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = *o; }"); |
| 5224 | let err = try expectError(&result); |
| 5225 | try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref); |
| 5226 | } |
| 5227 | } |
| 5228 | |
| 5229 | /// Test that you can dereference after casting. |
| 5230 | @test unsafe fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) { |
| 5231 | let mut testArena347 = testArena(); |
| 5232 | let testStorage347: 'test347 = &mut testArena347 in { |
| 5233 | let mut a = testResolver(testStorage347); |
| 5234 | let result = try resolveProgramStr(&mut a, "unsafe fn f() { let o: *opaque = undefined; let x = *(o as *i32); }"); |
| 5235 | try expectNoErrors(&result); |
| 5236 | } |
| 5237 | } |
| 5238 | |
| 5239 | /// You cannot do pointer arithmetic with an opaque pointer. |
| 5240 | @test unsafe fn testOpaquePointerNoArithmetic() throws (testing::TestError) { |
| 5241 | { |
| 5242 | let mut testArena348 = testArena(); |
| 5243 | let testStorage348: 'test348 = &mut testArena348 in { |
| 5244 | let mut a = testResolver(testStorage348); |
| 5245 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o + 1; }"); |
| 5246 | let err = try expectError(&result); |
| 5247 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
| 5248 | } |
| 5249 | } { |
| 5250 | let mut testArena349 = testArena(); |
| 5251 | let testStorage349: 'test349 = &mut testArena349 in { |
| 5252 | let mut a = testResolver(testStorage349); |
| 5253 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 + o; }"); |
| 5254 | let err = try expectError(&result); |
| 5255 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
| 5256 | } |
| 5257 | } { |
| 5258 | let mut testArena350 = testArena(); |
| 5259 | let testStorage350: 'test350 = &mut testArena350 in { |
| 5260 | let mut a = testResolver(testStorage350); |
| 5261 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = o - 1; }"); |
| 5262 | let err = try expectError(&result); |
| 5263 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
| 5264 | } |
| 5265 | } { |
| 5266 | let mut testArena351 = testArena(); |
| 5267 | let testStorage351: 'test351 = &mut testArena351 in { |
| 5268 | let mut a = testResolver(testStorage351); |
| 5269 | let result = try resolveProgramStr(&mut a, "fn f(a: *i32) { let o: *opaque = a; let x = 1 - o; }"); |
| 5270 | let err = try expectError(&result); |
| 5271 | try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic); |
| 5272 | } |
| 5273 | } |
| 5274 | } |
| 5275 | |
| 5276 | // Wildcard import/reexport tests. |
| 5277 | |
| 5278 | /// Test transitive re-export. |
| 5279 | @test unsafe fn testWildcardReexportTransitive() throws (testing::TestError) { |
| 5280 | let mut testArena352 = testArena(); |
| 5281 | let testStorage352: 'test352 = &mut testArena352 in { |
| 5282 | let mut a = testResolver(testStorage352); |
| 5283 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 5284 | |
| 5285 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena); |
| 5286 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena); |
| 5287 | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena); |
| 5288 | let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena); |
| 5289 | let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena); |
| 5290 | |
| 5291 | let result = try resolveModuleTree(&mut a, rootId); |
| 5292 | try expectNoErrors(&result); |
| 5293 | } |
| 5294 | } |
| 5295 | |
| 5296 | /// Test that wildcard import can access public symbols. |
| 5297 | @test unsafe fn testWildcardImportPublicOnly() throws (testing::TestError) { |
| 5298 | let mut testArena353 = testArena(); |
| 5299 | let testStorage353: 'test353 = &mut testArena353 in { |
| 5300 | let mut a = testResolver(testStorage353); |
| 5301 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 5302 | |
| 5303 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
| 5304 | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export record Value { number: i32 } export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
| 5305 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn id(value: Value) -> Value { return value; } fn main() -> i32 { return public() + id(Value { number: 2 }).number; }", &mut arena); |
| 5306 | |
| 5307 | let result = try resolveModuleTree(&mut a, rootId); |
| 5308 | try expectNoErrors(&result); |
| 5309 | } |
| 5310 | } |
| 5311 | |
| 5312 | /// Test that wildcard import cannot access private symbols. |
| 5313 | @test unsafe fn testWildcardImportSkipsPrivate() throws (testing::TestError) { |
| 5314 | let mut testArena354 = testArena(); |
| 5315 | let testStorage354: 'test354 = &mut testArena354 in { |
| 5316 | let mut a = testResolver(testStorage354); |
| 5317 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 5318 | |
| 5319 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
| 5320 | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
| 5321 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena); |
| 5322 | |
| 5323 | let result = try resolveModuleTree(&mut a, rootId); |
| 5324 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
| 5325 | } |
| 5326 | } |
| 5327 | |
| 5328 | /// Test that a constant array can use another constant as its length. |
| 5329 | @test unsafe fn testConstArrayWithConstLength() throws (testing::TestError) { |
| 5330 | let mut testArena355 = testArena(); |
| 5331 | let testStorage355: 'test355 = &mut testArena355 in { |
| 5332 | let mut a = testResolver(testStorage355); |
| 5333 | let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];"; |
| 5334 | let result = try resolveProgramStr(&mut a, program); |
| 5335 | try expectNoErrors(&result); |
| 5336 | |
| 5337 | // Verify the array constant has the correct type with length 3. |
| 5338 | let arrStmt = try getBlockStmt(result.root, 1); |
| 5339 | let sym = super::symbolFor(&a, arrStmt) |
| 5340 | else throw testing::TestError::Failed; |
| 5341 | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
| 5342 | else throw testing::TestError::Failed; |
| 5343 | try testing::expect(arrType.length == 3); |
| 5344 | } |
| 5345 | } |
| 5346 | |
| 5347 | /// Test that a record field can use a constant as its array length. |
| 5348 | @test unsafe fn testRecordFieldWithConstArrayLength() throws (testing::TestError) { |
| 5349 | let mut testArena356 = testArena(); |
| 5350 | let testStorage356: 'test356 = &mut testArena356 in { |
| 5351 | let mut a = testResolver(testStorage356); |
| 5352 | let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }"; |
| 5353 | let result = try resolveProgramStr(&mut a, program); |
| 5354 | try expectNoErrors(&result); |
| 5355 | } |
| 5356 | } |
| 5357 | |
| 5358 | /// Test that a constant can have a record literal value (lazy record body resolution). |
| 5359 | @test unsafe fn testConstWithRecordLiteral() throws (testing::TestError) { |
| 5360 | let mut testArena357 = testArena(); |
| 5361 | let testStorage357: 'test357 = &mut testArena357 in { |
| 5362 | let mut a = testResolver(testStorage357); |
| 5363 | let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };"; |
| 5364 | let result = try resolveProgramStr(&mut a, program); |
| 5365 | try expectNoErrors(&result); |
| 5366 | } |
| 5367 | } |
| 5368 | |
| 5369 | /// Test that a constant can have a union variant value (lazy union body resolution). |
| 5370 | @test unsafe fn testConstWithUnionVariant() throws (testing::TestError) { |
| 5371 | let mut testArena358 = testArena(); |
| 5372 | let testStorage358: 'test358 = &mut testArena358 in { |
| 5373 | let mut a = testResolver(testStorage358); |
| 5374 | let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;"; |
| 5375 | let result = try resolveProgramStr(&mut a, program); |
| 5376 | try expectNoErrors(&result); |
| 5377 | } |
| 5378 | } |
| 5379 | |
| 5380 | /// Test that record field types can reference imported types. |
| 5381 | /// |
| 5382 | /// This tests that `use` statements are processed before record body resolution, |
| 5383 | /// allowing record fields to use types from imported modules. |
| 5384 | @test unsafe fn testRecordFieldUsesImportedType() throws (testing::TestError) { |
| 5385 | let mut testArena359 = testArena(); |
| 5386 | let testStorage359: 'test359 = &mut testArena359 in { |
| 5387 | let mut a = testResolver(testStorage359); |
| 5388 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 5389 | |
| 5390 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena); |
| 5391 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena); |
| 5392 | let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena); |
| 5393 | |
| 5394 | let result = try resolveModuleTree(&mut a, rootId); |
| 5395 | try expectNoErrors(&result); |
| 5396 | } |
| 5397 | } |
| 5398 | |
| 5399 | /// Test that imported constants can be used in array size expressions. |
| 5400 | /// |
| 5401 | /// This tests that constant values are propagated through scope access expressions, |
| 5402 | /// enabling compile-time evaluation of array sizes using imported constants. |
| 5403 | @test unsafe fn testImportedConstantInArraySize() throws (testing::TestError) { |
| 5404 | let mut testArena360 = testArena(); |
| 5405 | let testStorage360: 'test360 = &mut testArena360 in { |
| 5406 | let mut a = testResolver(testStorage360); |
| 5407 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 5408 | |
| 5409 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 5410 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena); |
| 5411 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = [0; consts::SIZE];", &mut arena); |
| 5412 | |
| 5413 | let result = try resolveModuleTree(&mut a, rootId); |
| 5414 | try expectNoErrors(&result); |
| 5415 | } |
| 5416 | } |
| 5417 | |
| 5418 | /// Test that `if let case` binds payload variables in the then branch. |
| 5419 | /// |
| 5420 | /// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should |
| 5421 | /// be bound to the payload value within the then branch scope. |
| 5422 | @test unsafe fn testResolveIfCaseBindsPayload() throws (testing::TestError) { |
| 5423 | let mut testArena361 = testArena(); |
| 5424 | let testStorage361: 'test361 = &mut testArena361 in { |
| 5425 | let mut a = testResolver(testStorage361); |
| 5426 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }"; |
| 5427 | let result = try resolveProgramStr(&mut a, program); |
| 5428 | try expectNoErrors(&result); |
| 5429 | } |
| 5430 | } |
| 5431 | |
| 5432 | /// Test that `if let case` payload binding is scoped to the then branch. |
| 5433 | /// |
| 5434 | /// The payload variable should not be accessible outside the then branch. |
| 5435 | @test unsafe fn testResolveIfCasePayloadScopeError() throws (testing::TestError) { |
| 5436 | let mut testArena362 = testArena(); |
| 5437 | let testStorage362: 'test362 = &mut testArena362 in { |
| 5438 | let mut a = testResolver(testStorage362); |
| 5439 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }"; |
| 5440 | let result = try resolveProgramStr(&mut a, program); |
| 5441 | let err = try expectError(&result); |
| 5442 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 5443 | else throw testing::TestError::Failed; |
| 5444 | try testing::expect(mem::eq(name, "x")); |
| 5445 | } |
| 5446 | } |
| 5447 | |
| 5448 | /// Test that `let case` binds payload variables in the current scope. |
| 5449 | /// |
| 5450 | /// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x` |
| 5451 | /// should be bound in the scope after the statement. |
| 5452 | @test unsafe fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) { |
| 5453 | let mut testArena363 = testArena(); |
| 5454 | let testStorage363: 'test363 = &mut testArena363 in { |
| 5455 | let mut a = testResolver(testStorage363); |
| 5456 | let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }"; |
| 5457 | let result = try resolveProgramStr(&mut a, program); |
| 5458 | try expectNoErrors(&result); |
| 5459 | } |
| 5460 | } |
| 5461 | |
| 5462 | /// Test that function pointers with identical signatures are assignable. |
| 5463 | /// |
| 5464 | /// Two function types with the same parameters, return type, and throw list |
| 5465 | /// should be considered structurally equal, even if they are separate allocations. |
| 5466 | @test unsafe fn testFnPointerAssignability() throws (testing::TestError) { |
| 5467 | let mut testArena364 = testArena(); |
| 5468 | let testStorage364: 'test364 = &mut testArena364 in { |
| 5469 | let mut a = testResolver(testStorage364); |
| 5470 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn double(n: i32) -> i32 { return n * 2; } apply(double, 5);"; |
| 5471 | let result = try resolveProgramStr(&mut a, program); |
| 5472 | try expectNoErrors(&result); |
| 5473 | } |
| 5474 | } |
| 5475 | |
| 5476 | /// Test that function pointers with different parameter types are not assignable. |
| 5477 | @test unsafe fn testFnPointerParamMismatch() throws (testing::TestError) { |
| 5478 | let mut testArena365 = testArena(); |
| 5479 | let testStorage365: 'test365 = &mut testArena365 in { |
| 5480 | let mut a = testResolver(testStorage365); |
| 5481 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i8) -> i32 { return n as i32; } apply(other, 5);"; |
| 5482 | let result = try resolveProgramStr(&mut a, program); |
| 5483 | let err = try expectError(&result); |
| 5484 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5485 | else throw testing::TestError::Failed; |
| 5486 | } |
| 5487 | } |
| 5488 | |
| 5489 | /// Test that function pointers with different return types are not assignable. |
| 5490 | @test unsafe fn testFnPointerReturnMismatch() throws (testing::TestError) { |
| 5491 | let mut testArena366 = testArena(); |
| 5492 | let testStorage366: 'test366 = &mut testArena366 in { |
| 5493 | let mut a = testResolver(testStorage366); |
| 5494 | let program = "fn apply(f: fn(i32) -> i32, x: i32) -> i32 { return f(x); } fn other(n: i32) -> i8 { return n as i8; } apply(other, 5);"; |
| 5495 | let result = try resolveProgramStr(&mut a, program); |
| 5496 | let err = try expectError(&result); |
| 5497 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5498 | else throw testing::TestError::Failed; |
| 5499 | } |
| 5500 | } |
| 5501 | |
| 5502 | /// Test that named records use nominal typing, not structural. |
| 5503 | /// |
| 5504 | /// Two different named record types with identical fields should NOT be |
| 5505 | /// assignable to each other, because they are distinct nominal types. |
| 5506 | @test unsafe fn testNamedRecordNominalTyping() throws (testing::TestError) { |
| 5507 | let mut testArena367 = testArena(); |
| 5508 | let testStorage367: 'test367 = &mut testArena367 in { |
| 5509 | let mut a = testResolver(testStorage367); |
| 5510 | let program = "record Point { x: i32, y: i32 } record Vec2 { x: i32, y: i32 } fn take(p: Point) -> i32 { return p.x; } let v = Vec2 { x: 1, y: 2 }; take(v);"; |
| 5511 | let result = try resolveProgramStr(&mut a, program); |
| 5512 | let err = try expectError(&result); |
| 5513 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5514 | else throw testing::TestError::Failed; |
| 5515 | } |
| 5516 | } |
| 5517 | |
| 5518 | /// Test that union variants with labeled record payloads can be constructed. |
| 5519 | @test unsafe fn testUnionVariantAnonRecordPayload() throws (testing::TestError) { |
| 5520 | let mut testArena368 = testArena(); |
| 5521 | let testStorage368: 'test368 = &mut testArena368 in { |
| 5522 | let mut a = testResolver(testStorage368); |
| 5523 | let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };"; |
| 5524 | let result = try resolveProgramStr(&mut a, program); |
| 5525 | try expectNoErrors(&result); |
| 5526 | } |
| 5527 | } |
| 5528 | |
| 5529 | /// Test that unlabeled record literals with positional fields work correctly. |
| 5530 | /// |
| 5531 | /// When a record is declared with positional fields (e.g., `record R(i32, bool)`), |
| 5532 | /// the literal must use constructor call syntax with positional arguments. |
| 5533 | @test unsafe fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) { |
| 5534 | let mut testArena369 = testArena(); |
| 5535 | let testStorage369: 'test369 = &mut testArena369 in { |
| 5536 | let mut a = testResolver(testStorage369); |
| 5537 | let program = "record R(i32, bool); let r: R = R(1, true);"; |
| 5538 | let result = try resolveProgramStr(&mut a, program); |
| 5539 | try expectNoErrors(&result); |
| 5540 | } |
| 5541 | } |
| 5542 | |
| 5543 | /// Test that using brace syntax for an unlabeled record causes an error. |
| 5544 | @test unsafe fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) { |
| 5545 | let mut testArena370 = testArena(); |
| 5546 | let testStorage370: 'test370 = &mut testArena370 in { |
| 5547 | let mut a = testResolver(testStorage370); |
| 5548 | let program = "record R(i32); let r = R { x: 1 };"; |
| 5549 | let result = try resolveProgramStr(&mut a, program); |
| 5550 | try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch); |
| 5551 | } |
| 5552 | } |
| 5553 | |
| 5554 | /// Test that providing too many fields for an unlabeled record causes count mismatch. |
| 5555 | @test unsafe fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) { |
| 5556 | let mut testArena371 = testArena(); |
| 5557 | let testStorage371: 'test371 = &mut testArena371 in { |
| 5558 | let mut a = testResolver(testStorage371); |
| 5559 | let program = "record R(i32, bool); let r = R(1, true, 3);"; |
| 5560 | let result = try resolveProgramStr(&mut a, program); |
| 5561 | let err = try expectError(&result); |
| 5562 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 5563 | else throw testing::TestError::Failed; |
| 5564 | } |
| 5565 | } |
| 5566 | |
| 5567 | /// Test that match pattern with wrong number of bindings causes count mismatch. |
| 5568 | @test unsafe fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) { |
| 5569 | let mut testArena372 = testArena(); |
| 5570 | let testStorage372: 'test372 = &mut testArena372 in { |
| 5571 | let mut a = testResolver(testStorage372); |
| 5572 | let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }"; |
| 5573 | let result = try resolveProgramStr(&mut a, program); |
| 5574 | let err = try expectError(&result); |
| 5575 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 5576 | else throw testing::TestError::Failed; |
| 5577 | } |
| 5578 | } |
| 5579 | |
| 5580 | /// Test that shorthand field syntax works in record literals. |
| 5581 | /// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`. |
| 5582 | @test unsafe fn testResolveRecordLiteralShorthand() throws (testing::TestError) { |
| 5583 | let mut testArena373 = testArena(); |
| 5584 | let testStorage373: 'test373 = &mut testArena373 in { |
| 5585 | let mut a = testResolver(testStorage373); |
| 5586 | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }"; |
| 5587 | let result = try resolveProgramStr(&mut a, program); |
| 5588 | try expectNoErrors(&result); |
| 5589 | } |
| 5590 | } |
| 5591 | |
| 5592 | /// Test shorthand field syntax with mixed explicit and shorthand fields. |
| 5593 | @test unsafe fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) { |
| 5594 | let mut testArena374 = testArena(); |
| 5595 | let testStorage374: 'test374 = &mut testArena374 in { |
| 5596 | let mut a = testResolver(testStorage374); |
| 5597 | let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }"; |
| 5598 | let result = try resolveProgramStr(&mut a, program); |
| 5599 | try expectNoErrors(&result); |
| 5600 | } |
| 5601 | } |
| 5602 | |
| 5603 | /// Test record-style union variant patterns with shorthand syntax. |
| 5604 | @test unsafe fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) { |
| 5605 | let mut testArena375 = testArena(); |
| 5606 | let testStorage375: 'test375 = &mut testArena375 in { |
| 5607 | let mut a = testResolver(testStorage375); |
| 5608 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }"; |
| 5609 | let result = try resolveProgramStr(&mut a, program); |
| 5610 | try expectNoErrors(&result); |
| 5611 | } |
| 5612 | } |
| 5613 | |
| 5614 | /// Test record pattern with mixed shorthand and explicit labels. |
| 5615 | @test unsafe fn testResolveMatchRecordPatternMixed() throws (testing::TestError) { |
| 5616 | let mut testArena376 = testArena(); |
| 5617 | let testStorage376: 'test376 = &mut testArena376 in { |
| 5618 | let mut a = testResolver(testStorage376); |
| 5619 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height: h } => return width + h } }"; |
| 5620 | let result = try resolveProgramStr(&mut a, program); |
| 5621 | try expectNoErrors(&result); |
| 5622 | } |
| 5623 | } |
| 5624 | |
| 5625 | /// Test record pattern with fields in reverse order. |
| 5626 | @test unsafe fn testResolveMatchRecordPatternReversed() throws (testing::TestError) { |
| 5627 | let mut testArena377 = testArena(); |
| 5628 | let testStorage377: 'test377 = &mut testArena377 in { |
| 5629 | let mut a = testResolver(testStorage377); |
| 5630 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height: h, width: w } => return w + h } }"; |
| 5631 | let result = try resolveProgramStr(&mut a, program); |
| 5632 | try expectNoErrors(&result); |
| 5633 | } |
| 5634 | } |
| 5635 | |
| 5636 | /// Test record pattern with shorthand syntax in reverse order. |
| 5637 | /// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order. |
| 5638 | @test unsafe fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) { |
| 5639 | let mut testArena378 = testArena(); |
| 5640 | let testStorage378: 'test378 = &mut testArena378 in { |
| 5641 | let mut a = testResolver(testStorage378); |
| 5642 | let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }"; |
| 5643 | let result = try resolveProgramStr(&mut a, program); |
| 5644 | try expectNoErrors(&result); |
| 5645 | } |
| 5646 | } |
| 5647 | |
| 5648 | /// Test record pattern with `..` ignoring fields. |
| 5649 | @test unsafe fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) { |
| 5650 | { |
| 5651 | let mut testArena379 = testArena(); |
| 5652 | let testStorage379: 'test379 = &mut testArena379 in { |
| 5653 | let mut a = testResolver(testStorage379); |
| 5654 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }"; |
| 5655 | let result = try resolveProgramStr(&mut a, program); |
| 5656 | try expectNoErrors(&result); |
| 5657 | } |
| 5658 | } { |
| 5659 | let mut testArena380 = testArena(); |
| 5660 | let testStorage380: 'test380 = &mut testArena380 in { |
| 5661 | let mut a = testResolver(testStorage380); |
| 5662 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x: val, .. } => return val } }"; |
| 5663 | let result = try resolveProgramStr(&mut a, program); |
| 5664 | try expectNoErrors(&result); |
| 5665 | } |
| 5666 | } { |
| 5667 | let mut testArena381 = testArena(); |
| 5668 | let testStorage381: 'test381 = &mut testArena381 in { |
| 5669 | let mut a = testResolver(testStorage381); |
| 5670 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }"; |
| 5671 | let result = try resolveProgramStr(&mut a, program); |
| 5672 | try expectNoErrors(&result); |
| 5673 | } |
| 5674 | } { |
| 5675 | let mut testArena382 = testArena(); |
| 5676 | let testStorage382: 'test382 = &mut testArena382 in { |
| 5677 | let mut a = testResolver(testStorage382); |
| 5678 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, x, .. } => return x + z } }"; |
| 5679 | let result = try resolveProgramStr(&mut a, program); |
| 5680 | try expectNoErrors(&result); |
| 5681 | } |
| 5682 | } { |
| 5683 | let mut testArena383 = testArena(); |
| 5684 | let testStorage383: 'test383 = &mut testArena383 in { |
| 5685 | let mut a = testResolver(testStorage383); |
| 5686 | let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }"; |
| 5687 | let result = try resolveProgramStr(&mut a, program); |
| 5688 | try expectNoErrors(&result); |
| 5689 | } |
| 5690 | } |
| 5691 | } |
| 5692 | |
| 5693 | /// Test standalone record pattern matching with unlabeled patterns. |
| 5694 | @test unsafe fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) { |
| 5695 | let mut testArena384 = testArena(); |
| 5696 | let testStorage384: 'test384 = &mut testArena384 in { |
| 5697 | let mut a = testResolver(testStorage384); |
| 5698 | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }"; |
| 5699 | let result = try resolveProgramStr(&mut a, program); |
| 5700 | try expectNoErrors(&result); |
| 5701 | } |
| 5702 | } |
| 5703 | |
| 5704 | /// Test standalone record pattern matching with labeled patterns. |
| 5705 | /// Pattern syntax: `T { x }` matches a named record and binds x to the field. |
| 5706 | @test unsafe fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) { |
| 5707 | let mut testArena385 = testArena(); |
| 5708 | let testStorage385: 'test385 = &mut testArena385 in { |
| 5709 | let mut a = testResolver(testStorage385); |
| 5710 | let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }"; |
| 5711 | let result = try resolveProgramStr(&mut a, program); |
| 5712 | try expectNoErrors(&result); |
| 5713 | } |
| 5714 | } |
| 5715 | |
| 5716 | /// Test standalone record pattern with multiple fields. |
| 5717 | /// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields. |
| 5718 | @test unsafe fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) { |
| 5719 | let mut testArena386 = testArena(); |
| 5720 | let testStorage386: 'test386 = &mut testArena386 in { |
| 5721 | let mut a = testResolver(testStorage386); |
| 5722 | let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }"; |
| 5723 | let result = try resolveProgramStr(&mut a, program); |
| 5724 | try expectNoErrors(&result); |
| 5725 | } |
| 5726 | } |
| 5727 | |
| 5728 | /// Test standalone record pattern with wrong field count. |
| 5729 | /// Pattern `S(x, y)` should fail for a single-field record. |
| 5730 | @test unsafe fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) { |
| 5731 | let mut testArena387 = testArena(); |
| 5732 | let testStorage387: 'test387 = &mut testArena387 in { |
| 5733 | let mut a = testResolver(testStorage387); |
| 5734 | let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }"; |
| 5735 | let result = try resolveProgramStr(&mut a, program); |
| 5736 | let err = try expectError(&result); |
| 5737 | let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind |
| 5738 | else throw testing::TestError::Failed; |
| 5739 | } |
| 5740 | } |
| 5741 | |
| 5742 | /// Test array pattern matching with element bindings. |
| 5743 | /// Pattern syntax: `[x, y]` matches an array and binds elements. |
| 5744 | @test unsafe fn testResolveMatchArrayPattern() throws (testing::TestError) { |
| 5745 | let mut testArena388 = testArena(); |
| 5746 | let testStorage388: 'test388 = &mut testArena388 in { |
| 5747 | let mut a = testResolver(testStorage388); |
| 5748 | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }"; |
| 5749 | let result = try resolveProgramStr(&mut a, program); |
| 5750 | try expectNoErrors(&result); |
| 5751 | } |
| 5752 | } |
| 5753 | |
| 5754 | /// Test array pattern with placeholder elements. |
| 5755 | /// Pattern syntax: `[_, y]` ignores first element. |
| 5756 | @test unsafe fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) { |
| 5757 | let mut testArena389 = testArena(); |
| 5758 | let testStorage389: 'test389 = &mut testArena389 in { |
| 5759 | let mut a = testResolver(testStorage389); |
| 5760 | let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }"; |
| 5761 | let result = try resolveProgramStr(&mut a, program); |
| 5762 | try expectNoErrors(&result); |
| 5763 | } |
| 5764 | } |
| 5765 | |
| 5766 | /// Test identifier pattern that binds the whole value. |
| 5767 | /// Pattern syntax: `x` matches any value and binds it. |
| 5768 | @test unsafe fn testResolveMatchIdentPattern() throws (testing::TestError) { |
| 5769 | let mut testArena390 = testArena(); |
| 5770 | let testStorage390: 'test390 = &mut testArena390 in { |
| 5771 | let mut a = testResolver(testStorage390); |
| 5772 | let program = "fn f(val: i32) -> i32 { match val { x => return x } }"; |
| 5773 | let result = try resolveProgramStr(&mut a, program); |
| 5774 | try expectNoErrors(&result); |
| 5775 | } |
| 5776 | } |
| 5777 | |
| 5778 | /// Test numeric literal pattern matching. |
| 5779 | @test unsafe fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) { |
| 5780 | let mut testArena391 = testArena(); |
| 5781 | let testStorage391: 'test391 = &mut testArena391 in { |
| 5782 | let mut a = testResolver(testStorage391); |
| 5783 | let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }"; |
| 5784 | let result = try resolveProgramStr(&mut a, program); |
| 5785 | try expectNoErrors(&result); |
| 5786 | } |
| 5787 | } |
| 5788 | |
| 5789 | /// Test string literal pattern matching. |
| 5790 | @test unsafe fn testResolveMatchStringLiteralPattern() throws (testing::TestError) { |
| 5791 | let mut testArena392 = testArena(); |
| 5792 | let testStorage392: 'test392 = &mut testArena392 in { |
| 5793 | let mut a = testResolver(testStorage392); |
| 5794 | let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }"; |
| 5795 | let result = try resolveProgramStr(&mut a, program); |
| 5796 | try expectNoErrors(&result); |
| 5797 | } |
| 5798 | } |
| 5799 | |
| 5800 | /// Test boolean literal pattern matching. |
| 5801 | @test unsafe fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) { |
| 5802 | let mut testArena393 = testArena(); |
| 5803 | let testStorage393: 'test393 = &mut testArena393 in { |
| 5804 | let mut a = testResolver(testStorage393); |
| 5805 | let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }"; |
| 5806 | let result = try resolveProgramStr(&mut a, program); |
| 5807 | try expectNoErrors(&result); |
| 5808 | } |
| 5809 | } |
| 5810 | |
| 5811 | /// Test @sliceOf with correct arguments succeeds. |
| 5812 | @test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) { |
| 5813 | // Immutable pointer. |
| 5814 | { |
| 5815 | let mut testArena394 = testArena(); |
| 5816 | let testStorage394: 'test394 = &mut testArena394 in { |
| 5817 | let mut a = testResolver(testStorage394); |
| 5818 | let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
| 5819 | let result = try resolveProgramStr(&mut a, program); |
| 5820 | try expectNoErrors(&result); |
| 5821 | } |
| 5822 | } |
| 5823 | // Mutable pointer produces mutable slice. |
| 5824 | { |
| 5825 | let mut testArena395 = testArena(); |
| 5826 | let testStorage395: 'test395 = &mut testArena395 in { |
| 5827 | let mut a = testResolver(testStorage395); |
| 5828 | let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
| 5829 | let result = try resolveProgramStr(&mut a, program); |
| 5830 | try expectNoErrors(&result); |
| 5831 | } |
| 5832 | } |
| 5833 | } |
| 5834 | |
| 5835 | /// Test @sliceOf with wrong argument count produces an error. |
| 5836 | @test unsafe fn testResolveSliceOfWrongArgCount() throws (testing::TestError) { |
| 5837 | // No arguments. |
| 5838 | { |
| 5839 | let mut testArena396 = testArena(); |
| 5840 | let testStorage396: 'test396 = &mut testArena396 in { |
| 5841 | let mut a = testResolver(testStorage396); |
| 5842 | let program = "fn f() -> *[u8] { return @sliceOf(); }"; |
| 5843 | let result = try resolveProgramStr(&mut a, program); |
| 5844 | let err = try expectError(&result); |
| 5845 | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
| 5846 | else throw testing::TestError::Failed; |
| 5847 | try testing::expect(mismatch.expected == 2); |
| 5848 | try testing::expect(mismatch.actual == 0); |
| 5849 | } |
| 5850 | } |
| 5851 | // Too few arguments. |
| 5852 | { |
| 5853 | let mut testArena397 = testArena(); |
| 5854 | let testStorage397: 'test397 = &mut testArena397 in { |
| 5855 | let mut a = testResolver(testStorage397); |
| 5856 | let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }"; |
| 5857 | let result = try resolveProgramStr(&mut a, program); |
| 5858 | let err = try expectError(&result); |
| 5859 | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
| 5860 | else throw testing::TestError::Failed; |
| 5861 | try testing::expect(mismatch.expected == 2); |
| 5862 | try testing::expect(mismatch.actual == 1); |
| 5863 | } |
| 5864 | } |
| 5865 | // Too many arguments. |
| 5866 | { |
| 5867 | let mut testArena398 = testArena(); |
| 5868 | let testStorage398: 'test398 = &mut testArena398 in { |
| 5869 | let mut a = testResolver(testStorage398); |
| 5870 | let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }"; |
| 5871 | let result = try resolveProgramStr(&mut a, program); |
| 5872 | let err = try expectError(&result); |
| 5873 | let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind |
| 5874 | else throw testing::TestError::Failed; |
| 5875 | try testing::expect(mismatch.expected == 2); |
| 5876 | try testing::expect(mismatch.actual == 4); |
| 5877 | } |
| 5878 | } |
| 5879 | } |
| 5880 | |
| 5881 | /// Test @sliceOf with wrong argument types produces errors. |
| 5882 | @test unsafe fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) { |
| 5883 | // Non-pointer first argument. |
| 5884 | { |
| 5885 | let mut testArena399 = testArena(); |
| 5886 | let testStorage399: 'test399 = &mut testArena399 in { |
| 5887 | let mut a = testResolver(testStorage399); |
| 5888 | let program = "fn f(val: u32, len: u32) -> *[u8] { return @sliceOf(val, len); }"; |
| 5889 | let result = try resolveProgramStr(&mut a, program); |
| 5890 | let err = try expectError(&result); |
| 5891 | let case super::ErrorKind::ExpectedPointer = err.kind |
| 5892 | else throw testing::TestError::Failed; |
| 5893 | } |
| 5894 | } |
| 5895 | // Array instead of pointer. |
| 5896 | { |
| 5897 | let mut testArena400 = testArena(); |
| 5898 | let testStorage400: 'test400 = &mut testArena400 in { |
| 5899 | let mut a = testResolver(testStorage400); |
| 5900 | let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return @sliceOf(arr, len); }"; |
| 5901 | let result = try resolveProgramStr(&mut a, program); |
| 5902 | let err = try expectError(&result); |
| 5903 | let case super::ErrorKind::ExpectedPointer = err.kind |
| 5904 | else throw testing::TestError::Failed; |
| 5905 | } |
| 5906 | } |
| 5907 | // Non-numeric second argument. |
| 5908 | { |
| 5909 | let mut testArena401 = testArena(); |
| 5910 | let testStorage401: 'test401 = &mut testArena401 in { |
| 5911 | let mut a = testResolver(testStorage401); |
| 5912 | let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return @sliceOf(ptr, len); }"; |
| 5913 | let result = try resolveProgramStr(&mut a, program); |
| 5914 | let err = try expectError(&result); |
| 5915 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5916 | else throw testing::TestError::Failed; |
| 5917 | } |
| 5918 | } |
| 5919 | // Pointer second argument. |
| 5920 | { |
| 5921 | let mut testArena402 = testArena(); |
| 5922 | let testStorage402: 'test402 = &mut testArena402 in { |
| 5923 | let mut a = testResolver(testStorage402); |
| 5924 | let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
| 5925 | let result = try resolveProgramStr(&mut a, program); |
| 5926 | let err = try expectError(&result); |
| 5927 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5928 | else throw testing::TestError::Failed; |
| 5929 | } |
| 5930 | } |
| 5931 | } |
| 5932 | |
| 5933 | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
| 5934 | @test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) { |
| 5935 | { |
| 5936 | let mut testArena403 = testArena(); |
| 5937 | let testStorage403: 'test403 = &mut testArena403 in { |
| 5938 | let mut a = testResolver(testStorage403); |
| 5939 | let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
| 5940 | let result = try resolveProgramStr(&mut a, program); |
| 5941 | try expectNoErrors(&result); |
| 5942 | } |
| 5943 | } |
| 5944 | // Mutable pointer produces mutable slice. |
| 5945 | { |
| 5946 | let mut testArena404 = testArena(); |
| 5947 | let testStorage404: 'test404 = &mut testArena404 in { |
| 5948 | let mut a = testResolver(testStorage404); |
| 5949 | let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
| 5950 | let result = try resolveProgramStr(&mut a, program); |
| 5951 | try expectNoErrors(&result); |
| 5952 | } |
| 5953 | } |
| 5954 | } |
| 5955 | |
| 5956 | /// Test @sliceOf with 3 arguments but wrong cap type. |
| 5957 | @test unsafe fn testResolveSliceOfCapWrongType() throws (testing::TestError) { |
| 5958 | let mut testArena405 = testArena(); |
| 5959 | let testStorage405: 'test405 = &mut testArena405 in { |
| 5960 | let mut a = testResolver(testStorage405); |
| 5961 | let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
| 5962 | let result = try resolveProgramStr(&mut a, program); |
| 5963 | let err = try expectError(&result); |
| 5964 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 5965 | else throw testing::TestError::Failed; |
| 5966 | } |
| 5967 | } |
| 5968 | |
| 5969 | /// Test .cap field access on slices resolves to u32. |
| 5970 | @test unsafe fn testResolveSliceCapField() throws (testing::TestError) { |
| 5971 | let mut testArena406 = testArena(); |
| 5972 | let testStorage406: 'test406 = &mut testArena406 in { |
| 5973 | let mut a = testResolver(testStorage406); |
| 5974 | let program = "fn f(s: *[u8]) -> u32 { return s.cap; }"; |
| 5975 | let result = try resolveProgramStr(&mut a, program); |
| 5976 | try expectNoErrors(&result); |
| 5977 | } |
| 5978 | } |
| 5979 | |
| 5980 | /// Test `.append()` on immutable slice produces an error. |
| 5981 | @test unsafe fn testResolveSliceAppendImmutable() throws (testing::TestError) { |
| 5982 | let mut testArena407 = testArena(); |
| 5983 | let testStorage407: 'test407 = &mut testArena407 in { |
| 5984 | let mut a = testResolver(testStorage407); |
| 5985 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *[i32], a: A) { s.append(1, a); }"; |
| 5986 | let result = try resolveProgramStr(&mut a, program); |
| 5987 | let err = try expectError(&result); |
| 5988 | let case super::ErrorKind::ImmutableBinding = err.kind |
| 5989 | else throw testing::TestError::Failed; |
| 5990 | } |
| 5991 | } |
| 5992 | |
| 5993 | /// Test `.append()` with wrong argument count produces an error. |
| 5994 | @test unsafe fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) { |
| 5995 | // Too few arguments. |
| 5996 | { |
| 5997 | let mut testArena408 = testArena(); |
| 5998 | let testStorage408: 'test408 = &mut testArena408 in { |
| 5999 | let mut a = testResolver(testStorage408); |
| 6000 | let program = "fn f(s: *mut [i32]) { s.append(1); }"; |
| 6001 | let result = try resolveProgramStr(&mut a, program); |
| 6002 | let err = try expectError(&result); |
| 6003 | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
| 6004 | else throw testing::TestError::Failed; |
| 6005 | try testing::expect(m.expected == 2); |
| 6006 | try testing::expect(m.actual == 1); |
| 6007 | } |
| 6008 | } |
| 6009 | // Too many arguments. |
| 6010 | { |
| 6011 | let mut testArena409 = testArena(); |
| 6012 | let testStorage409: 'test409 = &mut testArena409 in { |
| 6013 | let mut a = testResolver(testStorage409); |
| 6014 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a, 0); }"; |
| 6015 | let result = try resolveProgramStr(&mut a, program); |
| 6016 | let err = try expectError(&result); |
| 6017 | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
| 6018 | else throw testing::TestError::Failed; |
| 6019 | try testing::expect(m.expected == 2); |
| 6020 | try testing::expect(m.actual == 3); |
| 6021 | } |
| 6022 | } |
| 6023 | } |
| 6024 | |
| 6025 | /// Test `.append()` with correct arguments succeeds. |
| 6026 | @test unsafe fn testResolveSliceAppendCorrect() throws (testing::TestError) { |
| 6027 | let mut testArena410 = testArena(); |
| 6028 | let testStorage410: 'test410 = &mut testArena410 in { |
| 6029 | let mut a = testResolver(testStorage410); |
| 6030 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(1, a); }"; |
| 6031 | let result = try resolveProgramStr(&mut a, program); |
| 6032 | try expectNoErrors(&result); |
| 6033 | } |
| 6034 | } |
| 6035 | |
| 6036 | /// Test `.append()` with wrong element type produces an error. |
| 6037 | @test unsafe fn testResolveSliceAppendWrongElemType() throws (testing::TestError) { |
| 6038 | let mut testArena411 = testArena(); |
| 6039 | let testStorage411: 'test411 = &mut testArena411 in { |
| 6040 | let mut a = testResolver(testStorage411); |
| 6041 | let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn f(s: *mut [i32], a: A) { s.append(true, a); }"; |
| 6042 | let result = try resolveProgramStr(&mut a, program); |
| 6043 | let err = try expectError(&result); |
| 6044 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6045 | else throw testing::TestError::Failed; |
| 6046 | } |
| 6047 | } |
| 6048 | |
| 6049 | /// Test `.delete()` on immutable slice produces an error. |
| 6050 | @test unsafe fn testResolveSliceDeleteImmutable() throws (testing::TestError) { |
| 6051 | let mut testArena412 = testArena(); |
| 6052 | let testStorage412: 'test412 = &mut testArena412 in { |
| 6053 | let mut a = testResolver(testStorage412); |
| 6054 | let program = "fn f(s: *[i32]) { s.delete(0); }"; |
| 6055 | let result = try resolveProgramStr(&mut a, program); |
| 6056 | let err = try expectError(&result); |
| 6057 | let case super::ErrorKind::ImmutableBinding = err.kind |
| 6058 | else throw testing::TestError::Failed; |
| 6059 | } |
| 6060 | } |
| 6061 | |
| 6062 | /// Test `.delete()` with wrong argument count produces an error. |
| 6063 | @test unsafe fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) { |
| 6064 | // No arguments. |
| 6065 | { |
| 6066 | let mut testArena413 = testArena(); |
| 6067 | let testStorage413: 'test413 = &mut testArena413 in { |
| 6068 | let mut a = testResolver(testStorage413); |
| 6069 | let program = "fn f(s: *mut [i32]) { s.delete(); }"; |
| 6070 | let result = try resolveProgramStr(&mut a, program); |
| 6071 | let err = try expectError(&result); |
| 6072 | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
| 6073 | else throw testing::TestError::Failed; |
| 6074 | try testing::expect(m.expected == 1); |
| 6075 | try testing::expect(m.actual == 0); |
| 6076 | } |
| 6077 | } |
| 6078 | // Too many arguments. |
| 6079 | { |
| 6080 | let mut testArena414 = testArena(); |
| 6081 | let testStorage414: 'test414 = &mut testArena414 in { |
| 6082 | let mut a = testResolver(testStorage414); |
| 6083 | let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }"; |
| 6084 | let result = try resolveProgramStr(&mut a, program); |
| 6085 | let err = try expectError(&result); |
| 6086 | let case super::ErrorKind::FnArgCountMismatch(m) = err.kind |
| 6087 | else throw testing::TestError::Failed; |
| 6088 | try testing::expect(m.expected == 1); |
| 6089 | try testing::expect(m.actual == 2); |
| 6090 | } |
| 6091 | } |
| 6092 | } |
| 6093 | |
| 6094 | /// Test `.delete()` with correct arguments succeeds. |
| 6095 | @test unsafe fn testResolveSliceDeleteCorrect() throws (testing::TestError) { |
| 6096 | let mut testArena415 = testArena(); |
| 6097 | let testStorage415: 'test415 = &mut testArena415 in { |
| 6098 | let mut a = testResolver(testStorage415); |
| 6099 | let program = "fn f(s: *mut [i32]) { s.delete(0); }"; |
| 6100 | let result = try resolveProgramStr(&mut a, program); |
| 6101 | try expectNoErrors(&result); |
| 6102 | } |
| 6103 | } |
| 6104 | |
| 6105 | /// Test `.delete()` with wrong argument type produces an error. |
| 6106 | @test unsafe fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) { |
| 6107 | let mut testArena416 = testArena(); |
| 6108 | let testStorage416: 'test416 = &mut testArena416 in { |
| 6109 | let mut a = testResolver(testStorage416); |
| 6110 | let program = "fn f(s: *mut [i32]) { s.delete(true); }"; |
| 6111 | let result = try resolveProgramStr(&mut a, program); |
| 6112 | let err = try expectError(&result); |
| 6113 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6114 | else throw testing::TestError::Failed; |
| 6115 | } |
| 6116 | } |
| 6117 | |
| 6118 | /// Test `match &opt` produces immutable pointer bindings. |
| 6119 | @test unsafe fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
| 6120 | let mut testArena417 = testArena(); |
| 6121 | let testStorage417: 'test417 = &mut testArena417 in { |
| 6122 | let mut a = testResolver(testStorage417); |
| 6123 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
| 6124 | let result = try resolveProgramStr(&mut a, program); |
| 6125 | try expectNoErrors(&result); |
| 6126 | |
| 6127 | let fnBlock = try getFnBody(&a, result.root, "f"); |
| 6128 | let matchNode = fnBlock.statements[1]; |
| 6129 | let case ast::NodeValue::Match(sw) = matchNode.value |
| 6130 | else throw testing::TestError::Failed; |
| 6131 | let caseNode = sw.prongs[0]; |
| 6132 | |
| 6133 | let scope = super::scopeFor(&a, caseNode) |
| 6134 | else throw testing::TestError::Failed; |
| 6135 | let payloadSym = super::findSymbolInScope(scope, "x") |
| 6136 | else throw testing::TestError::Failed; |
| 6137 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
| 6138 | else throw testing::TestError::Failed; |
| 6139 | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
| 6140 | else throw testing::TestError::Failed; |
| 6141 | try testing::expect(not mutable); |
| 6142 | try testing::expect(*target == super::Type::I32); |
| 6143 | } |
| 6144 | } |
| 6145 | |
| 6146 | /// Test `match &mut opt` produces mutable pointer bindings. |
| 6147 | @test unsafe fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) { |
| 6148 | let mut testArena418 = testArena(); |
| 6149 | let testStorage418: 'test418 = &mut testArena418 in { |
| 6150 | let mut a = testResolver(testStorage418); |
| 6151 | let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }"; |
| 6152 | let result = try resolveProgramStr(&mut a, program); |
| 6153 | try expectNoErrors(&result); |
| 6154 | |
| 6155 | let fnBlock = try getFnBody(&a, result.root, "f"); |
| 6156 | let matchNode = fnBlock.statements[1]; |
| 6157 | let case ast::NodeValue::Match(sw) = matchNode.value |
| 6158 | else throw testing::TestError::Failed; |
| 6159 | let caseNode = sw.prongs[0]; |
| 6160 | |
| 6161 | let scope = super::scopeFor(&a, caseNode) |
| 6162 | else throw testing::TestError::Failed; |
| 6163 | let payloadSym = super::findSymbolInScope(scope, "x") |
| 6164 | else throw testing::TestError::Failed; |
| 6165 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
| 6166 | else throw testing::TestError::Failed; |
| 6167 | let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType |
| 6168 | else throw testing::TestError::Failed; |
| 6169 | try testing::expect(mutable); |
| 6170 | try testing::expect(*target == super::Type::I32); |
| 6171 | } |
| 6172 | } |
| 6173 | |
| 6174 | /// Non-constant integer widening must use an explicit cast. |
| 6175 | @test unsafe fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) { |
| 6176 | { |
| 6177 | let mut testArena419 = testArena(); |
| 6178 | let testStorage419: 'test419 = &mut testArena419 in { |
| 6179 | let mut a = testResolver(testStorage419); |
| 6180 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;"); |
| 6181 | let err = try expectError(&result); |
| 6182 | try expectTypeMismatch(err, super::Type::U32, super::Type::U8); |
| 6183 | } |
| 6184 | } { |
| 6185 | let mut testArena420 = testArena(); |
| 6186 | let testStorage420: 'test420 = &mut testArena420 in { |
| 6187 | let mut a = testResolver(testStorage420); |
| 6188 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;"); |
| 6189 | let err = try expectError(&result); |
| 6190 | try expectTypeMismatch(err, super::Type::U16, super::Type::U8); |
| 6191 | } |
| 6192 | } { |
| 6193 | let mut testArena421 = testArena(); |
| 6194 | let testStorage421: 'test421 = &mut testArena421 in { |
| 6195 | let mut a = testResolver(testStorage421); |
| 6196 | let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;"); |
| 6197 | let err = try expectError(&result); |
| 6198 | try expectTypeMismatch(err, super::Type::U32, super::Type::U16); |
| 6199 | } |
| 6200 | } { |
| 6201 | let mut testArena422 = testArena(); |
| 6202 | let testStorage422: 'test422 = &mut testArena422 in { |
| 6203 | let mut a = testResolver(testStorage422); |
| 6204 | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;"); |
| 6205 | let err = try expectError(&result); |
| 6206 | try expectTypeMismatch(err, super::Type::I32, super::Type::I8); |
| 6207 | } |
| 6208 | } { |
| 6209 | let mut testArena423 = testArena(); |
| 6210 | let testStorage423: 'test423 = &mut testArena423 in { |
| 6211 | let mut a = testResolver(testStorage423); |
| 6212 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;"); |
| 6213 | try expectNoErrors(&result); |
| 6214 | } |
| 6215 | } { |
| 6216 | let mut testArena424 = testArena(); |
| 6217 | let testStorage424: 'test424 = &mut testArena424 in { |
| 6218 | let mut a = testResolver(testStorage424); |
| 6219 | let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;"); |
| 6220 | try expectNoErrors(&result); |
| 6221 | } |
| 6222 | } |
| 6223 | } |
| 6224 | |
| 6225 | /// Mixed-width integer binary ops require an explicit cast. |
| 6226 | @test unsafe fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) { |
| 6227 | { |
| 6228 | let mut testArena425 = testArena(); |
| 6229 | let testStorage425: 'test425 = &mut testArena425 in { |
| 6230 | let mut a = testResolver(testStorage425); |
| 6231 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;"); |
| 6232 | let err = try expectError(&result); |
| 6233 | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
| 6234 | } |
| 6235 | } { |
| 6236 | let mut testArena426 = testArena(); |
| 6237 | let testStorage426: 'test426 = &mut testArena426 in { |
| 6238 | let mut a = testResolver(testStorage426); |
| 6239 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;"); |
| 6240 | let err = try expectError(&result); |
| 6241 | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
| 6242 | } |
| 6243 | } { |
| 6244 | let mut testArena427 = testArena(); |
| 6245 | let testStorage427: 'test427 = &mut testArena427 in { |
| 6246 | let mut a = testResolver(testStorage427); |
| 6247 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;"); |
| 6248 | let err = try expectError(&result); |
| 6249 | try expectTypeMismatch(err, super::Type::U8, super::Type::U32); |
| 6250 | } |
| 6251 | } { |
| 6252 | let mut testArena428 = testArena(); |
| 6253 | let testStorage428: 'test428 = &mut testArena428 in { |
| 6254 | let mut a = testResolver(testStorage428); |
| 6255 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;"); |
| 6256 | try expectNoErrors(&result); |
| 6257 | } |
| 6258 | } { |
| 6259 | let mut testArena429 = testArena(); |
| 6260 | let testStorage429: 'test429 = &mut testArena429 in { |
| 6261 | let mut a = testResolver(testStorage429); |
| 6262 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;"); |
| 6263 | try expectNoErrors(&result); |
| 6264 | } |
| 6265 | } { |
| 6266 | let mut testArena430 = testArena(); |
| 6267 | let testStorage430: 'test430 = &mut testArena430 in { |
| 6268 | let mut a = testResolver(testStorage430); |
| 6269 | let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;"); |
| 6270 | try expectNoErrors(&result); |
| 6271 | } |
| 6272 | } |
| 6273 | } |
| 6274 | |
| 6275 | /// A mutable slice pointer should be assignable to an immutable slice pointer. |
| 6276 | @test unsafe fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) { |
| 6277 | let mut testArena431 = testArena(); |
| 6278 | let testStorage431: 'test431 = &mut testArena431 in { |
| 6279 | let mut a = testResolver(testStorage431); |
| 6280 | let result = try resolveBlockStr(&mut a, "static arr: [i32; 3] = [1, 2, 3]; let p: *mut [i32] = &mut arr[..]; let q: *[i32] = p;"); |
| 6281 | try expectNoErrors(&result); |
| 6282 | } |
| 6283 | } |
| 6284 | |
| 6285 | /// Comprehensive tests for `as` cast expressions. |
| 6286 | @test unsafe fn testResolveAsCasts() throws (testing::TestError) { |
| 6287 | { // Pointer to numeric. |
| 6288 | let mut testArena432 = testArena(); |
| 6289 | let testStorage432: 'test432 = &mut testArena432 in { |
| 6290 | let mut a = testResolver(testStorage432); |
| 6291 | let result = try resolveBlockStr(&mut a, "static x: i32 = 0; let p = &x; p as u32;"); |
| 6292 | try expectNoErrors(&result); |
| 6293 | } |
| 6294 | } { // Function pointer to numeric. |
| 6295 | let mut testArena433 = testArena(); |
| 6296 | let testStorage433: 'test433 = &mut testArena433 in { |
| 6297 | let mut a = testResolver(testStorage433); |
| 6298 | let result = try resolveProgramStr(&mut a, "fn run(f: fn()) { f as u32; }"); |
| 6299 | try expectNoErrors(&result); |
| 6300 | } |
| 6301 | } { // *u8 to *i32 (u8 to i32 is valid). |
| 6302 | let mut testArena434 = testArena(); |
| 6303 | let testStorage434: 'test434 = &mut testArena434 in { |
| 6304 | let mut a = testResolver(testStorage434); |
| 6305 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: *u8 = undefined; p as *i32; }"); |
| 6306 | try expectNoErrors(&result); |
| 6307 | } |
| 6308 | } { // **u8 to **i32 (*u8 to *i32 is valid). |
| 6309 | let mut testArena435 = testArena(); |
| 6310 | let testStorage435: 'test435 = &mut testArena435 in { |
| 6311 | let mut a = testResolver(testStorage435); |
| 6312 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let p: **u8 = undefined; p as **i32; }"); |
| 6313 | try expectNoErrors(&result); |
| 6314 | } |
| 6315 | } |
| 6316 | |
| 6317 | { // *[i32] to *[opaque]. |
| 6318 | let mut testArena436 = testArena(); |
| 6319 | let testStorage436: 'test436 = &mut testArena436 in { |
| 6320 | let mut a = testResolver(testStorage436); |
| 6321 | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *[opaque]; }"); |
| 6322 | try expectNoErrors(&result); |
| 6323 | } |
| 6324 | } { // *[opaque] to *[i32]. |
| 6325 | let mut testArena437 = testArena(); |
| 6326 | let testStorage437: 'test437 = &mut testArena437 in { |
| 6327 | let mut a = testResolver(testStorage437); |
| 6328 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[opaque] = undefined; s as *[i32]; }"); |
| 6329 | try expectNoErrors(&result); |
| 6330 | } |
| 6331 | } |
| 6332 | |
| 6333 | { // *[i32] to *[u8]. |
| 6334 | let mut testArena438 = testArena(); |
| 6335 | let testStorage438: 'test438 = &mut testArena438 in { |
| 6336 | let mut a = testResolver(testStorage438); |
| 6337 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[i32] = undefined; s as *[u8]; }"); |
| 6338 | try expectNoErrors(&result); |
| 6339 | } |
| 6340 | } { // *[record] to *[u8]. |
| 6341 | let mut testArena439 = testArena(); |
| 6342 | let testStorage439: 'test439 = &mut testArena439 in { |
| 6343 | let mut a = testResolver(testStorage439); |
| 6344 | let result = try resolveProgramStr(&mut a, "record R { x: i32 } unsafe fn f(s: *[R]) { s as *[u8]; }"); |
| 6345 | try expectNoErrors(&result); |
| 6346 | } |
| 6347 | } |
| 6348 | |
| 6349 | { // *[u8] to *[i32]. |
| 6350 | let mut testArena440 = testArena(); |
| 6351 | let testStorage440: 'test440 = &mut testArena440 in { |
| 6352 | let mut a = testResolver(testStorage440); |
| 6353 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[u8] = undefined; s as *[i32]; }"); |
| 6354 | try expectNoErrors(&result); |
| 6355 | } |
| 6356 | } { // *[*u8] to *[*i32] |
| 6357 | let mut testArena441 = testArena(); |
| 6358 | let testStorage441: 'test441 = &mut testArena441 in { |
| 6359 | let mut a = testResolver(testStorage441); |
| 6360 | let result = try resolveProgramStr(&mut a, "unsafe fn run() { let s: *[*u8] = undefined; s as *[*i32]; }"); |
| 6361 | try expectNoErrors(&result); |
| 6362 | } |
| 6363 | } |
| 6364 | |
| 6365 | { // Identity cast: *mut [i32] to *mut [i32]. |
| 6366 | let mut testArena442 = testArena(); |
| 6367 | let testStorage442: 'test442 = &mut testArena442 in { |
| 6368 | let mut a = testResolver(testStorage442); |
| 6369 | let result = try resolveProgramStr(&mut a, "fn run(s: *mut [i32]) { s as *mut [i32]; }"); |
| 6370 | try expectNoErrors(&result); |
| 6371 | } |
| 6372 | } { // Identity cast: *i32 to *i32. |
| 6373 | let mut testArena443 = testArena(); |
| 6374 | let testStorage443: 'test443 = &mut testArena443 in { |
| 6375 | let mut a = testResolver(testStorage443); |
| 6376 | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *i32; }"); |
| 6377 | try expectNoErrors(&result); |
| 6378 | } |
| 6379 | } { // Identity cast: i32 to i32. |
| 6380 | let mut testArena444 = testArena(); |
| 6381 | let testStorage444: 'test444 = &mut testArena444 in { |
| 6382 | let mut a = testResolver(testStorage444); |
| 6383 | let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;"); |
| 6384 | try expectNoErrors(&result); |
| 6385 | } |
| 6386 | } |
| 6387 | } |
| 6388 | |
| 6389 | /// Tests for invalid `as` casts that should be rejected. |
| 6390 | @test unsafe fn testResolveAsCastsInvalid() throws (testing::TestError) { |
| 6391 | { // Pointer to slice is invalid. |
| 6392 | let mut testArena445 = testArena(); |
| 6393 | let testStorage445: 'test445 = &mut testArena445 in { |
| 6394 | let mut a = testResolver(testStorage445); |
| 6395 | let result = try resolveProgramStr(&mut a, "fn run(p: *i32) { p as *[i32]; }"); |
| 6396 | let err = try expectError(&result); |
| 6397 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6398 | else throw testing::TestError::Failed; |
| 6399 | } |
| 6400 | } { // Slice to pointer is invalid. |
| 6401 | let mut testArena446 = testArena(); |
| 6402 | let testStorage446: 'test446 = &mut testArena446 in { |
| 6403 | let mut a = testResolver(testStorage446); |
| 6404 | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as *i32; }"); |
| 6405 | let err = try expectError(&result); |
| 6406 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6407 | else throw testing::TestError::Failed; |
| 6408 | } |
| 6409 | } { // *T to *i32 is invalid. |
| 6410 | let mut testArena447 = testArena(); |
| 6411 | let testStorage447: 'test447 = &mut testArena447 in { |
| 6412 | let mut a = testResolver(testStorage447); |
| 6413 | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *i32; }"); |
| 6414 | let err = try expectError(&result); |
| 6415 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6416 | else throw testing::TestError::Failed; |
| 6417 | } |
| 6418 | } { // *[T] to *[i32] is invalid. |
| 6419 | let mut testArena448 = testArena(); |
| 6420 | let testStorage448: 'test448 = &mut testArena448 in { |
| 6421 | let mut a = testResolver(testStorage448); |
| 6422 | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) { s as *[i32]; }"); |
| 6423 | let err = try expectError(&result); |
| 6424 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6425 | else throw testing::TestError::Failed; |
| 6426 | } |
| 6427 | } { // Slice to numeric is invalid. |
| 6428 | let mut testArena449 = testArena(); |
| 6429 | let testStorage449: 'test449 = &mut testArena449 in { |
| 6430 | let mut a = testResolver(testStorage449); |
| 6431 | let result = try resolveProgramStr(&mut a, "fn run(s: *[i32]) { s as u32; }"); |
| 6432 | let err = try expectError(&result); |
| 6433 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6434 | else throw testing::TestError::Failed; |
| 6435 | } |
| 6436 | } { // *record to *u8 is invalid. |
| 6437 | let mut testArena450 = testArena(); |
| 6438 | let testStorage450: 'test450 = &mut testArena450 in { |
| 6439 | let mut a = testResolver(testStorage450); |
| 6440 | let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) { p as *u8; }"); |
| 6441 | let err = try expectError(&result); |
| 6442 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 6443 | else throw testing::TestError::Failed; |
| 6444 | } |
| 6445 | } |
| 6446 | } |
| 6447 | |
| 6448 | /// Test that catch binding is available in catch block scope. |
| 6449 | @test unsafe fn testResolveTryCatchBinding() throws (testing::TestError) { |
| 6450 | { |
| 6451 | let mut testArena451 = testArena(); |
| 6452 | let testStorage451: 'test451 = &mut testArena451 in { |
| 6453 | let mut a = testResolver(testStorage451); |
| 6454 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }"; |
| 6455 | let result = try resolveProgramStr(&mut a, program); |
| 6456 | try expectNoErrors(&result); |
| 6457 | } |
| 6458 | } { |
| 6459 | let mut testArena452 = testArena(); |
| 6460 | let testStorage452: 'test452 = &mut testArena452 in { |
| 6461 | let mut a = testResolver(testStorage452); |
| 6462 | let program = "union Error { A, B } fn fallible() -> u32 throws (Error) { throw Error::A; } fn caller() -> u32 { return try fallible() catch e { if e == Error::A { return 1; } else { return 2; } }; }"; |
| 6463 | let result = try resolveProgramStr(&mut a, program); |
| 6464 | try expectNoErrors(&result); |
| 6465 | } |
| 6466 | } { |
| 6467 | let mut testArena453 = testArena(); |
| 6468 | let testStorage453: 'test453 = &mut testArena453 in { |
| 6469 | let mut a = testResolver(testStorage453); |
| 6470 | let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { if err == Error::Fail { return 1; } return 0; }; }"; |
| 6471 | let result = try resolveProgramStr(&mut a, program); |
| 6472 | try expectNoErrors(&result); |
| 6473 | } |
| 6474 | } { |
| 6475 | let mut testArena454 = testArena(); |
| 6476 | let testStorage454: 'test454 = &mut testArena454 in { |
| 6477 | let mut a = testResolver(testStorage454); |
| 6478 | let program = "union Error { Fail(u32) } fn fallible() -> u32 throws (Error) { throw Error::Fail(42); } fn caller() -> u32 { return try fallible() catch err { match err { case Error::Fail(x) => return x, } }; }"; |
| 6479 | let result = try resolveProgramStr(&mut a, program); |
| 6480 | try expectNoErrors(&result); |
| 6481 | } |
| 6482 | } |
| 6483 | } |
| 6484 | |
| 6485 | /// Test that duplicate union variant patterns are detected. |
| 6486 | @test unsafe fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) { |
| 6487 | { |
| 6488 | let mut testArena455 = testArena(); |
| 6489 | let testStorage455: 'test455 = &mut testArena455 in { |
| 6490 | let mut a = testResolver(testStorage455); |
| 6491 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }"; |
| 6492 | let result = try resolveProgramStr(&mut a, program); |
| 6493 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 6494 | } |
| 6495 | } { |
| 6496 | // No duplicate: distinct variants are fine. |
| 6497 | let mut testArena456 = testArena(); |
| 6498 | let testStorage456: 'test456 = &mut testArena456 in { |
| 6499 | let mut a = testResolver(testStorage456); |
| 6500 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }"; |
| 6501 | let result = try resolveProgramStr(&mut a, program); |
| 6502 | try expectNoErrors(&result); |
| 6503 | } |
| 6504 | } |
| 6505 | } |
| 6506 | |
| 6507 | /// Test that duplicate bool patterns are detected. |
| 6508 | @test unsafe fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) { |
| 6509 | { |
| 6510 | let mut testArena457 = testArena(); |
| 6511 | let testStorage457: 'test457 = &mut testArena457 in { |
| 6512 | let mut a = testResolver(testStorage457); |
| 6513 | let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }"; |
| 6514 | let result = try resolveProgramStr(&mut a, program); |
| 6515 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 6516 | } |
| 6517 | } { |
| 6518 | let mut testArena458 = testArena(); |
| 6519 | let testStorage458: 'test458 = &mut testArena458 in { |
| 6520 | let mut a = testResolver(testStorage458); |
| 6521 | let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }"; |
| 6522 | let result = try resolveProgramStr(&mut a, program); |
| 6523 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 6524 | } |
| 6525 | } |
| 6526 | } |
| 6527 | |
| 6528 | /// Test that duplicate nil patterns in optional match are detected. |
| 6529 | @test unsafe fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) { |
| 6530 | { |
| 6531 | let mut testArena459 = testArena(); |
| 6532 | let testStorage459: 'test459 = &mut testArena459 in { |
| 6533 | let mut a = testResolver(testStorage459); |
| 6534 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }"; |
| 6535 | let result = try resolveProgramStr(&mut a, program); |
| 6536 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 6537 | } |
| 6538 | } { |
| 6539 | // Duplicate value binding. |
| 6540 | let mut testArena460 = testArena(); |
| 6541 | let testStorage460: 'test460 = &mut testArena460 in { |
| 6542 | let mut a = testResolver(testStorage460); |
| 6543 | let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }"; |
| 6544 | let result = try resolveProgramStr(&mut a, program); |
| 6545 | try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern); |
| 6546 | } |
| 6547 | } |
| 6548 | } |
| 6549 | |
| 6550 | /// Test that guarded match arms are not considered duplicates. |
| 6551 | @test unsafe fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) { |
| 6552 | { |
| 6553 | // Guarded union variant followed by same variant is fine. |
| 6554 | let mut testArena461 = testArena(); |
| 6555 | let testStorage461: 'test461 = &mut testArena461 in { |
| 6556 | let mut a = testResolver(testStorage461); |
| 6557 | let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }"; |
| 6558 | let result = try resolveProgramStr(&mut a, program); |
| 6559 | try expectNoErrors(&result); |
| 6560 | } |
| 6561 | } { |
| 6562 | // Guarded bool pattern followed by same bool is fine. |
| 6563 | let mut testArena462 = testArena(); |
| 6564 | let testStorage462: 'test462 = &mut testArena462 in { |
| 6565 | let mut a = testResolver(testStorage462); |
| 6566 | let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }"; |
| 6567 | let result = try resolveProgramStr(&mut a, program); |
| 6568 | try expectNoErrors(&result); |
| 6569 | } |
| 6570 | } { |
| 6571 | // Guarded nil pattern followed by nil is fine. |
| 6572 | let mut testArena463 = testArena(); |
| 6573 | let testStorage463: 'test463 = &mut testArena463 in { |
| 6574 | let mut a = testResolver(testStorage463); |
| 6575 | let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }"; |
| 6576 | let result = try resolveProgramStr(&mut a, program); |
| 6577 | try expectNoErrors(&result); |
| 6578 | } |
| 6579 | } { |
| 6580 | // Guarded value binding followed by another binding is fine. |
| 6581 | let mut testArena464 = testArena(); |
| 6582 | let testStorage464: 'test464 = &mut testArena464 in { |
| 6583 | let mut a = testResolver(testStorage464); |
| 6584 | let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }"; |
| 6585 | let result = try resolveProgramStr(&mut a, program); |
| 6586 | try expectNoErrors(&result); |
| 6587 | } |
| 6588 | } |
| 6589 | } |
| 6590 | |
| 6591 | /// Test that unreachable else is detected when all union variants are covered. |
| 6592 | @test unsafe fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) { |
| 6593 | { |
| 6594 | let mut testArena465 = testArena(); |
| 6595 | let testStorage465: 'test465 = &mut testArena465 in { |
| 6596 | let mut a = testResolver(testStorage465); |
| 6597 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }"; |
| 6598 | let result = try resolveProgramStr(&mut a, program); |
| 6599 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 6600 | } |
| 6601 | } { |
| 6602 | // Partial coverage with else is fine. |
| 6603 | let mut testArena466 = testArena(); |
| 6604 | let testStorage466: 'test466 = &mut testArena466 in { |
| 6605 | let mut a = testResolver(testStorage466); |
| 6606 | let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }"; |
| 6607 | let result = try resolveProgramStr(&mut a, program); |
| 6608 | try expectNoErrors(&result); |
| 6609 | } |
| 6610 | } |
| 6611 | } |
| 6612 | |
| 6613 | /// Test that unreachable else is detected when both bool cases are covered. |
| 6614 | @test unsafe fn testResolveMatchUnreachableElseBool() throws (testing::TestError) { |
| 6615 | { |
| 6616 | let mut testArena467 = testArena(); |
| 6617 | let testStorage467: 'test467 = &mut testArena467 in { |
| 6618 | let mut a = testResolver(testStorage467); |
| 6619 | let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }"; |
| 6620 | let result = try resolveProgramStr(&mut a, program); |
| 6621 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 6622 | } |
| 6623 | } { |
| 6624 | // Only one case with else is fine. |
| 6625 | let mut testArena468 = testArena(); |
| 6626 | let testStorage468: 'test468 = &mut testArena468 in { |
| 6627 | let mut a = testResolver(testStorage468); |
| 6628 | let program = "fn f(x: bool) { match x { case true => {}, else => {} } }"; |
| 6629 | let result = try resolveProgramStr(&mut a, program); |
| 6630 | try expectNoErrors(&result); |
| 6631 | } |
| 6632 | } |
| 6633 | } |
| 6634 | |
| 6635 | /// Test that unreachable else is detected when both optional cases are covered. |
| 6636 | @test unsafe fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) { |
| 6637 | { |
| 6638 | let mut testArena469 = testArena(); |
| 6639 | let testStorage469: 'test469 = &mut testArena469 in { |
| 6640 | let mut a = testResolver(testStorage469); |
| 6641 | let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }"; |
| 6642 | let result = try resolveProgramStr(&mut a, program); |
| 6643 | try expectErrorKind(&result, super::ErrorKind::UnreachableElse); |
| 6644 | } |
| 6645 | } { |
| 6646 | // Only value binding with else is fine. |
| 6647 | let mut testArena470 = testArena(); |
| 6648 | let testStorage470: 'test470 = &mut testArena470 in { |
| 6649 | let mut a = testResolver(testStorage470); |
| 6650 | let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }"; |
| 6651 | let result = try resolveProgramStr(&mut a, program); |
| 6652 | try expectNoErrors(&result); |
| 6653 | } |
| 6654 | } |
| 6655 | } |
| 6656 | |
| 6657 | // --- Multi-error typed catch tests --- |
| 6658 | |
| 6659 | @test unsafe fn testTypedCatchExhaustive() throws (testing::TestError) { |
| 6660 | let mut testArena471 = testArena(); |
| 6661 | let testStorage471: 'test471 = &mut testArena471 in { |
| 6662 | let mut a = testResolver(testStorage471); |
| 6663 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrB { return 1; }; }"; |
| 6664 | let result = try resolveProgramStr(&mut a, program); |
| 6665 | try expectNoErrors(&result); |
| 6666 | } |
| 6667 | } |
| 6668 | |
| 6669 | @test unsafe fn testTypedCatchNonExhaustive() throws (testing::TestError) { |
| 6670 | let mut testArena472 = testArena(); |
| 6671 | let testStorage472: 'test472 = &mut testArena472 in { |
| 6672 | let mut a = testResolver(testStorage472); |
| 6673 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; }; }"; |
| 6674 | let result = try resolveProgramStr(&mut a, program); |
| 6675 | try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive); |
| 6676 | } |
| 6677 | } |
| 6678 | |
| 6679 | @test unsafe fn testTypedCatchDuplicate() throws (testing::TestError) { |
| 6680 | let mut testArena473 = testArena(); |
| 6681 | let testStorage473: 'test473 = &mut testArena473 in { |
| 6682 | let mut a = testResolver(testStorage473); |
| 6683 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch e as ErrA { return 1; }; }"; |
| 6684 | let result = try resolveProgramStr(&mut a, program); |
| 6685 | try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType); |
| 6686 | } |
| 6687 | } |
| 6688 | |
| 6689 | @test unsafe fn testTypedCatchWithCatchAll() throws (testing::TestError) { |
| 6690 | let mut testArena474 = testArena(); |
| 6691 | let testStorage474: 'test474 = &mut testArena474 in { |
| 6692 | let mut a = testResolver(testStorage474); |
| 6693 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrA { return 0; } catch { return 1; }; }"; |
| 6694 | let result = try resolveProgramStr(&mut a, program); |
| 6695 | try expectNoErrors(&result); |
| 6696 | } |
| 6697 | } |
| 6698 | |
| 6699 | @test unsafe fn testTypedCatchWrongType() throws (testing::TestError) { |
| 6700 | let mut testArena475 = testArena(); |
| 6701 | let testStorage475: 'test475 = &mut testArena475 in { |
| 6702 | let mut a = testResolver(testStorage475); |
| 6703 | let program = "union ErrA { A } union ErrB { B } union ErrC { C } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e as ErrC { return 0; } catch e as ErrA { return 1; }; }"; |
| 6704 | let result = try resolveProgramStr(&mut a, program); |
| 6705 | try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError); |
| 6706 | } |
| 6707 | } |
| 6708 | |
| 6709 | @test unsafe fn testInferredCatchMultiError() throws (testing::TestError) { |
| 6710 | let mut testArena476 = testArena(); |
| 6711 | let testStorage476: 'test476 = &mut testArena476 in { |
| 6712 | let mut a = testResolver(testStorage476); |
| 6713 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }"; |
| 6714 | let result = try resolveProgramStr(&mut a, program); |
| 6715 | try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError); |
| 6716 | } |
| 6717 | } |
| 6718 | |
| 6719 | @test unsafe fn testResolveInstanceMissingMethod() throws (testing::TestError) { |
| 6720 | let mut testArena477 = testArena(); |
| 6721 | let testStorage477: 'test477 = &mut testArena477 in { |
| 6722 | let mut a = testResolver(testStorage477); |
| 6723 | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}"; |
| 6724 | let result = try resolveProgramStr(&mut a, program); |
| 6725 | try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f")); |
| 6726 | } |
| 6727 | } |
| 6728 | |
| 6729 | @test unsafe fn testResolveInstanceUnknownMethod() throws (testing::TestError) { |
| 6730 | let mut testArena478 = testArena(); |
| 6731 | let testStorage478: 'test478 = &mut testArena478 in { |
| 6732 | let mut a = testResolver(testStorage478); |
| 6733 | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }"; |
| 6734 | let result = try resolveProgramStr(&mut a, program); |
| 6735 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
| 6736 | } |
| 6737 | } |
| 6738 | |
| 6739 | @test unsafe fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) { |
| 6740 | let mut testArena479 = testArena(); |
| 6741 | let testStorage479: 'test479 = &mut testArena479 in { |
| 6742 | let mut a = testResolver(testStorage479); |
| 6743 | let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }"; |
| 6744 | let result = try resolveProgramStr(&mut a, program); |
| 6745 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add")); |
| 6746 | } |
| 6747 | } |
| 6748 | |
| 6749 | @test unsafe fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) { |
| 6750 | let mut testArena480 = testArena(); |
| 6751 | let testStorage480: 'test480 = &mut testArena480 in { |
| 6752 | let mut a = testResolver(testStorage480); |
| 6753 | let program = "record Counter { value: i32 } record Wrong { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Wrong) add(n: i32) -> i32 { return n; } }"; |
| 6754 | let result = try resolveProgramStr(&mut a, program); |
| 6755 | let err = try expectError(&result); |
| 6756 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6757 | else throw testing::TestError::Failed; |
| 6758 | } |
| 6759 | } |
| 6760 | |
| 6761 | @test unsafe fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) { |
| 6762 | let mut testArena481 = testArena(); |
| 6763 | let testStorage481: 'test481 = &mut testArena481 in { |
| 6764 | let mut a = testResolver(testStorage481); |
| 6765 | let program = "union Error { Fail } record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32 throws (Error); } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 throws (Error) { throw Error::Fail; return n; } } fn caller(a: *mut opaque Adder) -> i32 { return a.add(1); }"; |
| 6766 | let result = try resolveProgramStr(&mut a, program); |
| 6767 | try expectErrorKind(&result, super::ErrorKind::MissingTry); |
| 6768 | } |
| 6769 | } |
| 6770 | |
| 6771 | /// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type). |
| 6772 | /// The instance method could mutate through what was originally an immutable pointer. |
| 6773 | @test unsafe fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) { |
| 6774 | let mut testArena482 = testArena(); |
| 6775 | let testStorage482: 'test482 = &mut testArena482 in { |
| 6776 | let mut a = testResolver(testStorage482); |
| 6777 | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) read() -> i32; } instance Reader for Counter { fn (c: *mut Counter) read() -> i32 { set c.value = c.value + 1; return c.value; } }"; |
| 6778 | let result = try resolveProgramStr(&mut a, program); |
| 6779 | // Should reject: instance declares *mut receiver but trait only requires immutable. |
| 6780 | try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch); |
| 6781 | } |
| 6782 | } |
| 6783 | |
| 6784 | /// Instance method declares different parameter types than the trait. |
| 6785 | /// The resolver should reject the mismatch rather than silently using the trait's types. |
| 6786 | @test unsafe fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) { |
| 6787 | let mut testArena483 = testArena(); |
| 6788 | let testStorage483: 'test483 = &mut testArena483 in { |
| 6789 | let mut a = testResolver(testStorage483); |
| 6790 | let program = "record Acc { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Acc { fn (a: *mut Acc) add(n: u8) -> i32 { set a.value = a.value + n as i32; return a.value; } }"; |
| 6791 | let result = try resolveProgramStr(&mut a, program); |
| 6792 | // Should reject: instance param type u8 doesn't match trait param type i32. |
| 6793 | let err = try expectError(&result); |
| 6794 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6795 | else throw testing::TestError::Failed; |
| 6796 | } |
| 6797 | } |
| 6798 | |
| 6799 | /// Duplicate instance declarations for the same (trait, type) pair should be rejected. |
| 6800 | @test unsafe fn testResolveInstanceDuplicateRejected() throws (testing::TestError) { |
| 6801 | let mut testArena484 = testArena(); |
| 6802 | let testStorage484: 'test484 = &mut testArena484 in { |
| 6803 | let mut a = testResolver(testStorage484); |
| 6804 | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n + 100; return c.value; } }"; |
| 6805 | let result = try resolveProgramStr(&mut a, program); |
| 6806 | // Should reject: duplicate instance for (Adder, Counter). |
| 6807 | try expectErrorKind(&result, super::ErrorKind::DuplicateInstance); |
| 6808 | } |
| 6809 | } |
| 6810 | |
| 6811 | /// Trait method receiver must point to the declaring trait type. |
| 6812 | @test unsafe fn testResolveTraitReceiverMismatch() throws (testing::TestError) { |
| 6813 | let mut testArena485 = testArena(); |
| 6814 | let testStorage485: 'test485 = &mut testArena485 in { |
| 6815 | let mut a = testResolver(testStorage485); |
| 6816 | let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }"; |
| 6817 | let result = try resolveProgramStr(&mut a, program); |
| 6818 | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
| 6819 | } |
| 6820 | } |
| 6821 | |
| 6822 | /// Using a trait name as a value expression should be rejected. |
| 6823 | @test unsafe fn testResolveTraitNameAsValueRejected() throws (testing::TestError) { |
| 6824 | let mut testArena486 = testArena(); |
| 6825 | let testStorage486: 'test486 = &mut testArena486 in { |
| 6826 | let mut a = testResolver(testStorage486); |
| 6827 | let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }"; |
| 6828 | let result = try resolveProgramStr(&mut a, program); |
| 6829 | try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName); |
| 6830 | } |
| 6831 | } |
| 6832 | |
| 6833 | /// Cross-module trait: coerce to trait object and dispatch from a different module. |
| 6834 | @test unsafe fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
| 6835 | let mut testArena487 = testArena(); |
| 6836 | let testStorage487: 'test487 = &mut testArena487 in { |
| 6837 | let mut a = testResolver(testStorage487); |
| 6838 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 6839 | |
| 6840 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
| 6841 | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
| 6842 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
| 6843 | |
| 6844 | let result = try resolveModuleTree(&mut a, rootId); |
| 6845 | try expectNoErrors(&result); |
| 6846 | } |
| 6847 | } |
| 6848 | |
| 6849 | /// Instance in a different module from trait and type. |
| 6850 | @test unsafe fn testResolveInstanceCrossModule() throws (testing::TestError) { |
| 6851 | let mut testArena488 = testArena(); |
| 6852 | let testStorage488: 'test488 = &mut testArena488 in { |
| 6853 | let mut a = testResolver(testStorage488); |
| 6854 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 6855 | |
| 6856 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
| 6857 | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
| 6858 | let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } }", &mut arena); |
| 6859 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { static c: defs::Counter = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
| 6860 | |
| 6861 | let result = try resolveModuleTree(&mut a, rootId); |
| 6862 | try expectNoErrors(&result); |
| 6863 | } |
| 6864 | } |
| 6865 | |
| 6866 | /// Calling a mutable-receiver trait method on an immutable trait object |
| 6867 | /// must be rejected. |
| 6868 | @test unsafe fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) { |
| 6869 | let mut testArena489 = testArena(); |
| 6870 | let testStorage489: 'test489 = &mut testArena489 in { |
| 6871 | let mut a = testResolver(testStorage489); |
| 6872 | let program = "record Counter { value: i32 } trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { set c.value = c.value + n; return c.value; } } fn caller(a: *opaque Adder) -> i32 { return a.add(1); }"; |
| 6873 | let result = try resolveProgramStr(&mut a, program); |
| 6874 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 6875 | } |
| 6876 | } |
| 6877 | |
| 6878 | /// Immutable methods on an immutable trait object should be accepted. |
| 6879 | @test unsafe fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) { |
| 6880 | let mut testArena490 = testArena(); |
| 6881 | let testStorage490: 'test490 = &mut testArena490 in { |
| 6882 | let mut a = testResolver(testStorage490); |
| 6883 | let program = "record Counter { value: i32 } trait Reader { fn (*Reader) get() -> i32; } instance Reader for Counter { fn (c: *Counter) get() -> i32 { return c.value; } } fn caller(r: *opaque Reader) -> i32 { return r.get(); }"; |
| 6884 | let result = try resolveProgramStr(&mut a, program); |
| 6885 | try expectNoErrors(&result); |
| 6886 | } |
| 6887 | } |
| 6888 | |
| 6889 | /// Both mutable and immutable methods on a mutable trait object should work. |
| 6890 | @test unsafe fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) { |
| 6891 | let mut testArena491 = testArena(); |
| 6892 | let testStorage491: 'test491 = &mut testArena491 in { |
| 6893 | let mut a = testResolver(testStorage491); |
| 6894 | let program = "record Counter { value: i32 } trait Ops { fn (&mut Ops) inc(); fn (&Ops) get() -> i32; } instance Ops for Counter { fn (c: &mut Counter) inc() { set c.value = c.value + 1; } fn (c: &Counter) get() -> i32 { return c.value; } } fn caller(o: *mut opaque Ops) -> i32 { o.inc(); return o.get(); }"; |
| 6895 | let result = try resolveProgramStr(&mut a, program); |
| 6896 | try expectNoErrors(&result); |
| 6897 | } |
| 6898 | } |
| 6899 | |
| 6900 | /// Instance method body type must match the trait return type. |
| 6901 | /// The trait declares `-> i32` but the body returns `bool`. |
| 6902 | @test unsafe fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) { |
| 6903 | let mut testArena492 = testArena(); |
| 6904 | let testStorage492: 'test492 = &mut testArena492 in { |
| 6905 | let mut a = testResolver(testStorage492); |
| 6906 | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }"; |
| 6907 | let result = try resolveProgramStr(&mut a, program); |
| 6908 | let err = try expectError(&result); |
| 6909 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6910 | else throw testing::TestError::Failed; |
| 6911 | } |
| 6912 | } |
| 6913 | |
| 6914 | /// Diamond supertrait inheritance: traits B and C both extend A. |
| 6915 | /// Declaring them independently should work fine. |
| 6916 | @test unsafe fn testResolveTraitDiamondSupertrait() throws (testing::TestError) { |
| 6917 | let mut testArena493 = testArena(); |
| 6918 | let testStorage493: 'test493 = &mut testArena493 in { |
| 6919 | let mut a = testResolver(testStorage493); |
| 6920 | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }"; |
| 6921 | let result = try resolveProgramStr(&mut a, program); |
| 6922 | try expectNoErrors(&result); |
| 6923 | } |
| 6924 | } |
| 6925 | |
| 6926 | /// Diamond supertrait with a combined trait that would cause duplicate |
| 6927 | /// method names should be detected. |
| 6928 | @test unsafe fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) { |
| 6929 | let mut testArena494 = testArena(); |
| 6930 | let testStorage494: 'test494 = &mut testArena494 in { |
| 6931 | let mut a = testResolver(testStorage494); |
| 6932 | let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; } trait D: B + C { fn (*D) i() -> i32; }"; |
| 6933 | let result = try resolveProgramStr(&mut a, program); |
| 6934 | // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`. |
| 6935 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f")); |
| 6936 | } |
| 6937 | } |
| 6938 | |
| 6939 | /// Supertrait instance must exist when declaring a combined trait instance. |
| 6940 | @test unsafe fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) { |
| 6941 | let mut testArena495 = testArena(); |
| 6942 | let testStorage495: 'test495 = &mut testArena495 in { |
| 6943 | let mut a = testResolver(testStorage495); |
| 6944 | let program = "trait Base { fn (*Base) f() -> i32; } trait Child: Base { fn (*Child) g() -> i32; } record R { x: i32 } instance Child for R { fn (r: *R) g() -> i32 { return r.x; } }"; |
| 6945 | let result = try resolveProgramStr(&mut a, program); |
| 6946 | try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base")); |
| 6947 | } |
| 6948 | } |
| 6949 | |
| 6950 | /// Instance method omits return type when the trait declares `-> i32`. |
| 6951 | /// This is rejected -- the return type must be stated explicitly. |
| 6952 | @test unsafe fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) { |
| 6953 | let mut testArena496 = testArena(); |
| 6954 | let testStorage496: 'test496 = &mut testArena496 in { |
| 6955 | let mut a = testResolver(testStorage496); |
| 6956 | let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }"; |
| 6957 | let result = try resolveProgramStr(&mut a, program); |
| 6958 | let err = try expectError(&result); |
| 6959 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6960 | else throw testing::TestError::Failed; |
| 6961 | } |
| 6962 | } |
| 6963 | |
| 6964 | /// Instance method declares throws but the trait method does not throw. |
| 6965 | @test unsafe fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) { |
| 6966 | let mut testArena497 = testArena(); |
| 6967 | let testStorage497: 'test497 = &mut testArena497 in { |
| 6968 | let mut a = testResolver(testStorage497); |
| 6969 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> i32 throws (E) { return r.x; } }"; |
| 6970 | let result = try resolveProgramStr(&mut a, program); |
| 6971 | let err = try expectError(&result); |
| 6972 | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
| 6973 | else throw testing::TestError::Failed; |
| 6974 | } |
| 6975 | } |
| 6976 | |
| 6977 | /// Instance method declares a different throws type than the trait. |
| 6978 | @test unsafe fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) { |
| 6979 | let mut testArena498 = testArena(); |
| 6980 | let testStorage498: 'test498 = &mut testArena498 in { |
| 6981 | let mut a = testResolver(testStorage498); |
| 6982 | let program = "union E1 { Fail } union E2 { Oops } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E1); } instance T for R { fn (r: *R) get() -> i32 throws (E2) { return r.x; } }"; |
| 6983 | let result = try resolveProgramStr(&mut a, program); |
| 6984 | let err = try expectError(&result); |
| 6985 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 6986 | else throw testing::TestError::Failed; |
| 6987 | } |
| 6988 | } |
| 6989 | |
| 6990 | /// Instance method omits throws clause when trait declares throws. |
| 6991 | /// This is rejected -- the throws clause must match exactly. |
| 6992 | @test unsafe fn testResolveInstanceThrowsOmitted() throws (testing::TestError) { |
| 6993 | let mut testArena499 = testArena(); |
| 6994 | let testStorage499: 'test499 = &mut testArena499 in { |
| 6995 | let mut a = testResolver(testStorage499); |
| 6996 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 { throw E::Fail; return r.x; } }"; |
| 6997 | let result = try resolveProgramStr(&mut a, program); |
| 6998 | let err = try expectError(&result); |
| 6999 | let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind |
| 7000 | else throw testing::TestError::Failed; |
| 7001 | } |
| 7002 | } |
| 7003 | |
| 7004 | /// Instance method correctly matches the trait's throws clause. |
| 7005 | @test unsafe fn testResolveInstanceThrowsMatch() throws (testing::TestError) { |
| 7006 | let mut testArena500 = testArena(); |
| 7007 | let testStorage500: 'test500 = &mut testArena500 in { |
| 7008 | let mut a = testResolver(testStorage500); |
| 7009 | let program = "union E { Fail } record R { x: i32 } trait T { fn (*T) get() -> i32 throws (E); } instance T for R { fn (r: *R) get() -> i32 throws (E) { throw E::Fail; return r.x; } }"; |
| 7010 | let result = try resolveProgramStr(&mut a, program); |
| 7011 | try expectNoErrors(&result); |
| 7012 | } |
| 7013 | } |
| 7014 | |
| 7015 | // Constant expression folding tests ////////////////////////////////////////// |
| 7016 | |
| 7017 | /// Resolve a program and verify that the constant at the given statement index |
| 7018 | /// has the expected integer magnitude. |
| 7019 | unsafe fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64) |
| 7020 | throws (testing::TestError) |
| 7021 | { |
| 7022 | let mut testArena501 = testArena(); |
| 7023 | let testStorage501: 'test501 = &mut testArena501 in { |
| 7024 | let mut a = testResolver(testStorage501); |
| 7025 | let result = try resolveProgramStr(&mut a, program); |
| 7026 | try expectNoErrors(&result); |
| 7027 | |
| 7028 | let stmt = try getBlockStmt(result.root, stmtIdx); |
| 7029 | let sym = super::symbolFor(&a, stmt) |
| 7030 | else throw testing::TestError::Failed; |
| 7031 | let case super::SymbolData::Constant { value, .. } = sym.data |
| 7032 | else throw testing::TestError::Failed; |
| 7033 | let val = value else throw testing::TestError::Failed; |
| 7034 | let case super::ConstValue::Int(intVal) = val |
| 7035 | else throw testing::TestError::Failed; |
| 7036 | try testing::expect(intVal.magnitude == expected); |
| 7037 | } |
| 7038 | } |
| 7039 | |
| 7040 | /// Test arithmetic constant folding: add, sub, mul, div. |
| 7041 | @test unsafe fn testConstExprArithmetic() throws (testing::TestError) { |
| 7042 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30); |
| 7043 | try expectConstFold("constant A: i32 = 50; constant B: i32 = 20; constant C: i32 = A - B;", 2, 30); |
| 7044 | try expectConstFold("constant A: i32 = 6; constant B: i32 = 7; constant C: i32 = A * B;", 2, 42); |
| 7045 | try expectConstFold("constant A: i32 = 100; constant B: i32 = 5; constant C: i32 = A / B;", 2, 20); |
| 7046 | } |
| 7047 | |
| 7048 | /// Test bitwise constant folding: and, or, xor. |
| 7049 | @test unsafe fn testConstExprBitwise() throws (testing::TestError) { |
| 7050 | try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A & B;", 2, 0x0F); |
| 7051 | try expectConstFold("constant A: i32 = 0xF0; constant B: i32 = 0x0F; constant C: i32 = A | B;", 2, 0xFF); |
| 7052 | try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A ^ B;", 2, 0xF0); |
| 7053 | } |
| 7054 | |
| 7055 | /// Test shift constant folding. |
| 7056 | @test unsafe fn testConstExprShift() throws (testing::TestError) { |
| 7057 | try expectConstFold("constant A: i32 = 1; constant B: i32 = A << 4;", 1, 16); |
| 7058 | try expectConstFold("constant A: i32 = 32; constant B: i32 = A >> 2;", 1, 8); |
| 7059 | } |
| 7060 | |
| 7061 | /// Test chained constant expressions (C depends on A + B, D depends on C). |
| 7062 | @test unsafe fn testConstExprChained() throws (testing::TestError) { |
| 7063 | try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60); |
| 7064 | } |
| 7065 | |
| 7066 | /// Test constant expression used as array size. |
| 7067 | @test unsafe fn testConstExprAsArraySize() throws (testing::TestError) { |
| 7068 | let mut testArena502 = testArena(); |
| 7069 | let testStorage502: 'test502 = &mut testArena502 in { |
| 7070 | let mut a = testResolver(testStorage502); |
| 7071 | let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];"; |
| 7072 | let result = try resolveProgramStr(&mut a, program); |
| 7073 | try expectNoErrors(&result); |
| 7074 | |
| 7075 | let arrStmt = try getBlockStmt(result.root, 3); |
| 7076 | let sym = super::symbolFor(&a, arrStmt) |
| 7077 | else throw testing::TestError::Failed; |
| 7078 | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
| 7079 | else throw testing::TestError::Failed; |
| 7080 | try testing::expect(arrType.length == 5); |
| 7081 | } |
| 7082 | } |
| 7083 | |
| 7084 | /// Test cross-module constant expression: a constant in one module references |
| 7085 | /// a constant from another module via scope access. |
| 7086 | @test unsafe fn testCrossModuleConstExpr() throws (testing::TestError) { |
| 7087 | let mut testArena503 = testArena(); |
| 7088 | let testStorage503: 'test503 = &mut testArena503 in { |
| 7089 | let mut a = testResolver(testStorage503); |
| 7090 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 7091 | |
| 7092 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 7093 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena); |
| 7094 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena); |
| 7095 | |
| 7096 | let result = try resolveModuleTree(&mut a, rootId); |
| 7097 | try expectNoErrors(&result); |
| 7098 | } |
| 7099 | } |
| 7100 | |
| 7101 | /// Test cross-module constant expression used as array size. |
| 7102 | @test unsafe fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
| 7103 | let mut testArena504 = testArena(); |
| 7104 | let testStorage504: 'test504 = &mut testArena504 in { |
| 7105 | let mut a = testResolver(testStorage504); |
| 7106 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 7107 | |
| 7108 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
| 7109 | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena); |
| 7110 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = [0; TOTAL];", &mut arena); |
| 7111 | |
| 7112 | let result = try resolveModuleTree(&mut a, rootId); |
| 7113 | try expectNoErrors(&result); |
| 7114 | } |
| 7115 | } |
| 7116 | |
| 7117 | /// Test that non-constant expressions in constant declarations are still rejected. |
| 7118 | @test unsafe fn testConstExprNonConstRejected() throws (testing::TestError) { |
| 7119 | let mut testArena505 = testArena(); |
| 7120 | let testStorage505: 'test505 = &mut testArena505 in { |
| 7121 | let mut a = testResolver(testStorage505); |
| 7122 | let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;"; |
| 7123 | let result = try resolveProgramStr(&mut a, program); |
| 7124 | let err = try expectError(&result); |
| 7125 | let case super::ErrorKind::ConstExprRequired = err.kind |
| 7126 | else throw testing::TestError::Failed; |
| 7127 | } |
| 7128 | } |
| 7129 | |
| 7130 | /// Test unary negation in constant expressions. |
| 7131 | @test unsafe fn testConstExprUnaryNeg() throws (testing::TestError) { |
| 7132 | let mut testArena506 = testArena(); |
| 7133 | let testStorage506: 'test506 = &mut testArena506 in { |
| 7134 | let mut a = testResolver(testStorage506); |
| 7135 | let program = "constant A: i32 = 10; constant B: i32 = -A;"; |
| 7136 | let result = try resolveProgramStr(&mut a, program); |
| 7137 | try expectNoErrors(&result); |
| 7138 | } |
| 7139 | } |
| 7140 | |
| 7141 | /// Test unary not in constant expressions. |
| 7142 | @test unsafe fn testConstExprUnaryNot() throws (testing::TestError) { |
| 7143 | let mut testArena507 = testArena(); |
| 7144 | let testStorage507: 'test507 = &mut testArena507 in { |
| 7145 | let mut a = testResolver(testStorage507); |
| 7146 | let program = "constant A: bool = true; constant B: bool = not A;"; |
| 7147 | let result = try resolveProgramStr(&mut a, program); |
| 7148 | try expectNoErrors(&result); |
| 7149 | } |
| 7150 | } |
| 7151 | |
| 7152 | /// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining. |
| 7153 | @test unsafe fn testConstExprCast() throws (testing::TestError) { |
| 7154 | try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42); |
| 7155 | try expectConstFold("constant A: u64 = 10; constant B: u8 = A as u8;", 1, 10); |
| 7156 | try expectConstFold("constant A: i32 = 7; constant B: u32 = A as u32;", 1, 7); |
| 7157 | try expectConstFold("constant A: u32 = 100; constant B: i32 = A as i32;", 1, 100); |
| 7158 | try expectConstFold("constant A: u8 = 5; constant B: u64 = (A as u32) as u64;", 1, 5); |
| 7159 | try expectConstFold("constant A: u8 = 3; constant B: u8 = 4; constant C: i32 = (A as i32) + (B as i32);", 2, 7); |
| 7160 | // Cast of unsuffixed literal arithmetic. |
| 7161 | try expectConstFold("constant A: u32 = (3 + 4) as u32;", 0, 7); |
| 7162 | try expectConstFold("constant A: u32 = ((3 + 4) as u64) as u32;", 0, 7); |
| 7163 | try expectConstFold("constant A: u32 = (3 + 4) as u32 + 1;", 0, 8); |
| 7164 | try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14); |
| 7165 | } |
| 7166 | |
| 7167 | /// Test `as` cast in constant expressions used as array size. |
| 7168 | @test unsafe fn testConstExprCastAsArraySize() throws (testing::TestError) { |
| 7169 | let mut testArena508 = testArena(); |
| 7170 | let testStorage508: 'test508 = &mut testArena508 in { |
| 7171 | let mut a = testResolver(testStorage508); |
| 7172 | let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];"; |
| 7173 | let result = try resolveProgramStr(&mut a, program); |
| 7174 | try expectNoErrors(&result); |
| 7175 | |
| 7176 | let arrStmt = try getBlockStmt(result.root, 2); |
| 7177 | let sym = super::symbolFor(&a, arrStmt) |
| 7178 | else throw testing::TestError::Failed; |
| 7179 | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
| 7180 | else throw testing::TestError::Failed; |
| 7181 | try testing::expect(arrType.length == 4); |
| 7182 | } |
| 7183 | } |
| 7184 | |
| 7185 | /// Test unsuffixed integer literals in constant expressions. |
| 7186 | @test unsafe fn testConstExprUnsuffixedLiterals() throws (testing::TestError) { |
| 7187 | try expectConstFold("constant A: u32 = 4 * 4;", 0, 16); |
| 7188 | try expectConstFold("constant B: u32 = 10; constant C: u32 = B * 2;", 1, 20); |
| 7189 | try expectConstFold("constant D: u32 = 3 + 7;", 0, 10); |
| 7190 | try expectConstFold("constant E: u32 = 2 * 3 + 4;", 0, 10); |
| 7191 | try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7); |
| 7192 | } |
| 7193 | |
| 7194 | /// References cannot escape through return types. |
| 7195 | @test unsafe fn testRefReturnRejected() throws (testing::TestError) { |
| 7196 | let mut testArena509 = testArena(); |
| 7197 | let testStorage509: 'test509 = &mut testArena509 in { |
| 7198 | let mut a = testResolver(testStorage509); |
| 7199 | let program = "record Marker: Once {} fn bad(value: &u32) -> &u32 { return value; }"; |
| 7200 | let result = try resolveProgramStr(&mut a, program); |
| 7201 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7202 | } |
| 7203 | } |
| 7204 | |
| 7205 | /// Case-pattern fallbacks must terminate instead of synthesizing bindings. |
| 7206 | @test unsafe fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) { |
| 7207 | let mut testArena510 = testArena(); |
| 7208 | let testStorage510: 'test510 = &mut testArena510 in { |
| 7209 | let mut a = testResolver(testStorage510); |
| 7210 | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }"; |
| 7211 | let result = try resolveProgramStr(&mut a, program); |
| 7212 | try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate); |
| 7213 | } |
| 7214 | } |
| 7215 | |
| 7216 | /// Case bindings are unavailable on the pattern-failure path. |
| 7217 | @test unsafe fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) { |
| 7218 | let mut testArena511 = testArena(); |
| 7219 | let testStorage511: 'test511 = &mut testArena511 in { |
| 7220 | let mut a = testResolver(testStorage511); |
| 7221 | let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }"; |
| 7222 | let result = try resolveProgramStr(&mut a, program); |
| 7223 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item")); |
| 7224 | } |
| 7225 | } |
| 7226 | |
| 7227 | /// Unsafe pointer dereference requires an unsafe declaration. |
| 7228 | @test unsafe fn testUnsafePointerOperationRejected() throws (testing::TestError) { |
| 7229 | let mut testArena512 = testArena(); |
| 7230 | let testStorage512: 'test512 = &mut testArena512 in { |
| 7231 | let mut a = testResolver(testStorage512); |
| 7232 | let program = "record Marker: Once {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
| 7233 | let result = try resolveProgramStr(&mut a, program); |
| 7234 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7235 | } |
| 7236 | } |
| 7237 | |
| 7238 | /// Unsafe pointers remain freely copyable inside an unsafe declaration. |
| 7239 | @test unsafe fn testUnsafePointerOperationAllowed() throws (testing::TestError) { |
| 7240 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }"; |
| 7241 | try expectAnalyzeOk(program); |
| 7242 | } |
| 7243 | |
| 7244 | /// Safe code cannot call a function that accepts unsafe operations. |
| 7245 | @test unsafe fn testUnsafeFunctionCallRejected() throws (testing::TestError) { |
| 7246 | let mut testArena513 = testArena(); |
| 7247 | let testStorage513: 'test513 = &mut testArena513 in { |
| 7248 | let mut a = testResolver(testStorage513); |
| 7249 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }"; |
| 7250 | let result = try resolveProgramStr(&mut a, program); |
| 7251 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 7252 | } |
| 7253 | } |
| 7254 | |
| 7255 | /// Unsafe function values retain their call-site safety requirement. |
| 7256 | @test unsafe fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) { |
| 7257 | let mut testArena514 = testArena(); |
| 7258 | let testStorage514: 'test514 = &mut testArena514 in { |
| 7259 | let mut a = testResolver(testStorage514); |
| 7260 | let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }"; |
| 7261 | let result = try resolveProgramStr(&mut a, program); |
| 7262 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 7263 | } |
| 7264 | } |
| 7265 | |
| 7266 | /// References cannot be embedded in aggregate fields. |
| 7267 | @test unsafe fn testRefFieldRejected() throws (testing::TestError) { |
| 7268 | let mut testArena515 = testArena(); |
| 7269 | let testStorage515: 'test515 = &mut testArena515 in { |
| 7270 | let mut a = testResolver(testStorage515); |
| 7271 | let program = "record Marker: Once {} record Bad { value: &u32 }"; |
| 7272 | let result = try resolveProgramStr(&mut a, program); |
| 7273 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7274 | } |
| 7275 | } |
| 7276 | |
| 7277 | /// Trait methods may use reference receivers. |
| 7278 | @test unsafe fn testTraitRefReceiver() throws (testing::TestError) { |
| 7279 | let program = "record Marker: Once {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }"; |
| 7280 | try expectAnalyzeOk(program); |
| 7281 | } |
| 7282 | |
| 7283 | /// Trait implementations must preserve the receiver pointer class. |
| 7284 | @test unsafe fn testTraitReceiverClassMismatch() throws (testing::TestError) { |
| 7285 | let mut testArena516 = testArena(); |
| 7286 | let testStorage516: 'test516 = &mut testArena516 in { |
| 7287 | let mut a = testResolver(testStorage516); |
| 7288 | let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }"; |
| 7289 | let result = try resolveProgramStr(&mut a, program); |
| 7290 | try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch); |
| 7291 | } |
| 7292 | } |
| 7293 | |
| 7294 | /// Unmarked composite values may be discarded. |
| 7295 | @test unsafe fn testAffineCompositeMayBeDiscarded() throws (testing::TestError) { |
| 7296 | let program = "record Value { number: u32 } fn run() { let value = Value { number: 1 }; }"; |
| 7297 | try expectAnalyzeOk(program); |
| 7298 | } |
| 7299 | |
| 7300 | /// A by-value use moves an unmarked composite value. |
| 7301 | @test unsafe fn testAffineCompositeUseAfterMoveRejected() throws (testing::TestError) { |
| 7302 | let mut testArena517 = testArena(); |
| 7303 | let testStorage517: 'test517 = &mut testArena517 in { |
| 7304 | let mut a = testResolver(testStorage517); |
| 7305 | let program = "record Value { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
| 7306 | let result = try resolveProgramStr(&mut a, program); |
| 7307 | try expectErrorKind(&result, super::ErrorKind::AffineUseAfterMove("value")); |
| 7308 | } |
| 7309 | } |
| 7310 | |
| 7311 | /// Affine values may move on only one branch when not used later. |
| 7312 | @test unsafe fn testAffineConditionalMoveMayBeDiscarded() throws (testing::TestError) { |
| 7313 | let program = "record Value { number: u32 } fn take(value: Value) {} fn run(condition: bool) { let value = Value { number: 1 }; if condition { take(value); } }"; |
| 7314 | try expectAnalyzeOk(program); |
| 7315 | } |
| 7316 | |
| 7317 | /// A `Copy` composite remains available after a by-value use. |
| 7318 | @test unsafe fn testCopyCompositeMayBeReused() throws (testing::TestError) { |
| 7319 | let program = "record Value: Copy { number: u32 } fn take(value: Value) {} fn run() { let value = Value { number: 1 }; take(value); take(value); }"; |
| 7320 | try expectAnalyzeOk(program); |
| 7321 | } |
| 7322 | |
| 7323 | /// A `Copy` composite may contain only copy values. |
| 7324 | @test unsafe fn testCopyCompositeRejectsAffineField() throws (testing::TestError) { |
| 7325 | let mut testArena518 = testArena(); |
| 7326 | let testStorage518: 'test518 = &mut testArena518 in { |
| 7327 | let mut a = testResolver(testStorage518); |
| 7328 | let program = "record Inner { number: u32 } record Outer: Copy { inner: Inner }"; |
| 7329 | let result = try resolveProgramStr(&mut a, program); |
| 7330 | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
| 7331 | } |
| 7332 | } |
| 7333 | |
| 7334 | /// A composite cannot carry conflicting ownership markers. |
| 7335 | @test unsafe fn testConflictingOwnershipMarkersRejected() throws (testing::TestError) { |
| 7336 | let mut testArena519 = testArena(); |
| 7337 | let testStorage519: 'test519 = &mut testArena519 in { |
| 7338 | let mut a = testResolver(testStorage519); |
| 7339 | let program = "record Value: Copy + Once { number: u32 }"; |
| 7340 | let result = try resolveProgramStr(&mut a, program); |
| 7341 | try expectErrorKind(&result, super::ErrorKind::ConflictingOwnershipMarkers); |
| 7342 | } |
| 7343 | } |
| 7344 | |
| 7345 | /// Linear composites still require one consuming use. |
| 7346 | @test unsafe fn testLinearCompositeMustBeConsumed() throws (testing::TestError) { |
| 7347 | let mut testArena520 = testArena(); |
| 7348 | let testStorage520: 'test520 = &mut testArena520 in { |
| 7349 | let mut a = testResolver(testStorage520); |
| 7350 | let program = "record Token: Once { number: u32 } fn run() { let token = Token { number: 1 }; }"; |
| 7351 | let result = try resolveProgramStr(&mut a, program); |
| 7352 | try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token")); |
| 7353 | } |
| 7354 | } |
| 7355 | |
| 7356 | /// The compiler-known marker cannot be derived more than once. |
| 7357 | @test unsafe fn testDuplicateOnceMarkerRejected() throws (testing::TestError) { |
| 7358 | let mut testArena521 = testArena(); |
| 7359 | let testStorage521: 'test521 = &mut testArena521 in { |
| 7360 | let mut a = testResolver(testStorage521); |
| 7361 | let program = "record Token: Once + Once { value: u32 }"; |
| 7362 | let result = try resolveProgramStr(&mut a, program); |
| 7363 | try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Once")); |
| 7364 | } |
| 7365 | } |
| 7366 | |
| 7367 | /// Stack storage cannot produce a safe stored pointer or slice. |
| 7368 | @test unsafe fn testStackPointerRejected() throws (testing::TestError) { |
| 7369 | let programs = &[ |
| 7370 | "fn run() { let value: u32 = 0; let pointer: *u32 = &value; }", |
| 7371 | "fn run() -> *u32 { let value: u32 = 0; return &value; }", |
| 7372 | "fn run(value: u32) -> *u32 { return &value; }", |
| 7373 | "record Cell { value: u32 } fn run() { let cell = Cell { value: 1 }; let pointer: *u32 = &(cell.value); }", |
| 7374 | "fn run() { let values = [1, 2]; let pointer: *i32 = &values[0]; }", |
| 7375 | "fn run() { let values = [1, 2]; let slice: *[i32] = &values[..]; }", |
| 7376 | "fn run(value: i32) { let slice: *[i32] = &[value]; }", |
| 7377 | "fn run(value: &u32) -> *u32 { return value; }", |
| 7378 | "fn run(value: &u32) -> *u32 { return &*value; }", |
| 7379 | "fn run(values: &[u32]) -> *[u32] { return values; }", |
| 7380 | "fn run(values: &[u32]) -> *[u32] { return &values[..]; }", |
| 7381 | "fn run(values: &[u32]) -> *u32 { return values.ptr; }", |
| 7382 | "fn run() -> *i32 { return &[1, 2][0]; }", |
| 7383 | "fn run() -> *[i32] { return &[1, 2][..]; }", |
| 7384 | "fn run(value: *u32) -> **u32 { return &value; }", |
| 7385 | "fn run(values: *[u32]) -> *u32 { return &values.len; }", |
| 7386 | "unsafe fn run(value: *unsafe u32) -> *u32 { return &*value; }", |
| 7387 | "fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }", |
| 7388 | "record Cell { value: u32 } fn run(value: &Cell) -> *u32 { return &value.value; }", |
| 7389 | "record Cell { value: *u32 } fn run(value: &u32) -> Cell { return Cell { value }; }", |
| 7390 | "fn run(value: &u32) -> *[u32] { return @sliceOf(value, 1); }", |
| 7391 | ]; |
| 7392 | for program in programs { |
| 7393 | let mut testArena522 = testArena(); |
| 7394 | let testStorage522: 'test522 = &mut testArena522 in { |
| 7395 | let mut resolver = testResolver(testStorage522); |
| 7396 | let result = try resolveProgramStr(&mut resolver, program); |
| 7397 | let _ = try expectError(&result); |
| 7398 | } |
| 7399 | } |
| 7400 | } |
| 7401 | |
| 7402 | /// Stack values can be borrowed for a call. |
| 7403 | @test unsafe fn testStackBorrowAllowed() throws (testing::TestError) { |
| 7404 | try expectAnalyzeOk("fn read(value: &u32) -> u32 { return *value; } fn run() -> u32 { let value: u32 = 7; return read(&value); }"); |
| 7405 | try expectAnalyzeOk("fn write(values: &mut [u32]) { set values[0] = 7; } fn run() { let mut values: [u32; 2] = [1, 2]; write(&mut values[..]); }"); |
| 7406 | } |
| 7407 | |
| 7408 | /// Raw pointers and slices can be borrowed in an unsafe context. |
| 7409 | @test unsafe fn testImplicitRawBorrowAllowed() throws (testing::TestError) { |
| 7410 | let programs = &[ |
| 7411 | "fn read(p: &u32) -> u32 { return *p; } unsafe fn run(p: *unsafe u32) -> u32 { return read(p); }", |
| 7412 | "fn write(p: &mut u32) { set *p = 7; } fn run(p: *unsafe mut u32) { unsafe { write(p); write(p); } }", |
| 7413 | "fn read(p: &[u32]) -> u32 { return p[0]; } unsafe fn run(p: *unsafe [u32]) -> u32 { return read(p); }", |
| 7414 | "fn write(p: &mut [u32]) { set p[0] = 7; } unsafe fn run(p: *unsafe mut [u32]) { write(p); write(p); }", |
| 7415 | "fn read(p: &u32) {} unsafe fn run(p: *unsafe mut u32) { read(p); }", |
| 7416 | "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} unsafe fn run(p: *unsafe opaque Read) { read(p); }", |
| 7417 | ]; |
| 7418 | for program in programs { |
| 7419 | try expectAnalyzeOk(program); |
| 7420 | } |
| 7421 | } |
| 7422 | |
| 7423 | /// An implicit borrow of raw storage requires an unsafe context. |
| 7424 | @test unsafe fn testImplicitRawBorrowRequiresUnsafe() throws (testing::TestError) { |
| 7425 | let programs = &[ |
| 7426 | "fn read(p: &u32) {} fn run(p: *unsafe u32) { read(p); }", |
| 7427 | "fn write(p: &mut u32) {} fn run(p: *unsafe mut u32) { write(p); }", |
| 7428 | "fn read(p: &[u32]) {} fn run(p: *unsafe [u32]) { read(p); }", |
| 7429 | "fn write(p: &mut [u32]) {} fn run(p: *unsafe mut [u32]) { write(p); }", |
| 7430 | "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} fn run(p: *unsafe opaque Read) { read(p); }", |
| 7431 | "fn read(p: &u32) {} fn run(p: *unsafe u32) { unsafe { read(p); } read(p); }", |
| 7432 | ]; |
| 7433 | for program in programs { |
| 7434 | let mut testArena523 = testArena(); |
| 7435 | let testStorage523: 'test523 = &mut testArena523 in { |
| 7436 | let mut a = testResolver(testStorage523); |
| 7437 | let result = try resolveProgramStr(&mut a, program); |
| 7438 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7439 | } |
| 7440 | } |
| 7441 | } |
| 7442 | |
| 7443 | /// Raw borrows preserve mutability, storage type, and ownership constraints. |
| 7444 | @test unsafe fn testImplicitRawBorrowPreservesTypes() throws (testing::TestError) { |
| 7445 | let programs = &[ |
| 7446 | "fn write(p: &mut u32) {} unsafe fn run(p: *unsafe u32) { write(p); }", |
| 7447 | "fn write(p: &mut [u32]) {} unsafe fn run(p: *unsafe [u32]) { write(p); }", |
| 7448 | "fn read(p: &u64) {} unsafe fn run(p: *unsafe u32) { read(p); }", |
| 7449 | "fn read(p: &[u64]) {} unsafe fn run(p: *unsafe [u32]) { read(p); }", |
| 7450 | "fn take(p: *u32) {} unsafe fn run(p: *unsafe u32) { take(p); }", |
| 7451 | "fn take(p: *[u32]) {} unsafe fn run(p: *unsafe [u32]) { take(p); }", |
| 7452 | "fn read(p: &*u32) {} unsafe fn run(p: *unsafe *unsafe u32) { read(p); }", |
| 7453 | ]; |
| 7454 | for program in programs { |
| 7455 | let mut testArena524 = testArena(); |
| 7456 | let testStorage524: 'test524 = &mut testArena524 in { |
| 7457 | let mut a = testResolver(testStorage524); |
| 7458 | let result = try resolveProgramStr(&mut a, program); |
| 7459 | let err = try expectError(&result); |
| 7460 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 7461 | else throw testing::TestError::Failed; |
| 7462 | } |
| 7463 | } |
| 7464 | } |
| 7465 | |
| 7466 | /// Implicit raw borrows retain the call's exclusive-borrow checks. |
| 7467 | @test unsafe fn testImplicitRawBorrowConflict() throws (testing::TestError) { |
| 7468 | let mut testArena525 = testArena(); |
| 7469 | let testStorage525: 'test525 = &mut testArena525 in { |
| 7470 | let mut a = testResolver(testStorage525); |
| 7471 | let result = try resolveProgramStr(&mut a, |
| 7472 | "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }"); |
| 7473 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
| 7474 | } |
| 7475 | } |
| 7476 | |
| 7477 | /// Unsafe declarations can store raw pointers to stack values. |
| 7478 | @test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) { |
| 7479 | try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }"); |
| 7480 | try expectAnalyzeOk("unsafe fn run() { let mut values: [u32; 2] = [1, 2]; let slice: *unsafe mut [u32] = &mut values[..] as *unsafe mut [u32]; set slice[0] = 7; }"); |
| 7481 | try expectAnalyzeOk("unsafe fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }"); |
| 7482 | try expectAnalyzeOk("unsafe fn run() { let values: [u32; 2] = [1, 2]; let slice: *unsafe [u32] = &values[..]; }"); |
| 7483 | } |
| 7484 | |
| 7485 | /// Permanent storage can produce safe stored pointers and slices. |
| 7486 | @test unsafe fn testPermanentPointerAllowed() throws (testing::TestError) { |
| 7487 | try expectAnalyzeOk("static VALUE: u32 = 7; fn run() -> *u32 { return &VALUE; }"); |
| 7488 | try expectAnalyzeOk("static VALUES: [u32; 2] = [1, 2]; fn run() -> *[u32] { return &VALUES[..]; }"); |
| 7489 | try expectAnalyzeOk("fn run() -> *[u32] { return &[1, 2]; }"); |
| 7490 | try expectAnalyzeOk("fn run(value: *u32) -> *u32 { return &*value; }"); |
| 7491 | try expectAnalyzeOk("fn run(values: *[u32]) -> *[u32] { return &values[..]; }"); |
| 7492 | try expectAnalyzeOk("record Cell { value: u32 } fn run(value: *Cell) -> *u32 { return &value.value; }"); |
| 7493 | try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return &values[0]; }"); |
| 7494 | try expectAnalyzeOk("unsafe fn run(values: *[u32]) -> *u32 { return values.ptr; }"); |
| 7495 | try expectAnalyzeOk("unsafe fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }"); |
| 7496 | } |
| 7497 | |
| 7498 | /// References are rejected from every nested or storable type position. |
| 7499 | @test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) { |
| 7500 | { |
| 7501 | let mut testArena526 = testArena(); |
| 7502 | let testStorage526: 'test526 = &mut testArena526 in { |
| 7503 | let mut a = testResolver(testStorage526); |
| 7504 | let program = "record Marker: Once {} union Bad { Value(&u32) }"; |
| 7505 | let result = try resolveProgramStr(&mut a, program); |
| 7506 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7507 | } |
| 7508 | } { |
| 7509 | let mut testArena527 = testArena(); |
| 7510 | let testStorage527: 'test527 = &mut testArena527 in { |
| 7511 | let mut a = testResolver(testStorage527); |
| 7512 | let program = "record Marker: Once {} fn bad(value: ?&u32) {}"; |
| 7513 | let result = try resolveProgramStr(&mut a, program); |
| 7514 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7515 | } |
| 7516 | } { |
| 7517 | let mut testArena528 = testArena(); |
| 7518 | let testStorage528: 'test528 = &mut testArena528 in { |
| 7519 | let mut a = testResolver(testStorage528); |
| 7520 | let program = "record Marker: Once {} fn bad(value: [&u32; 1]) {}"; |
| 7521 | let result = try resolveProgramStr(&mut a, program); |
| 7522 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7523 | } |
| 7524 | } { |
| 7525 | let mut testArena529 = testArena(); |
| 7526 | let testStorage529: 'test529 = &mut testArena529 in { |
| 7527 | let mut a = testResolver(testStorage529); |
| 7528 | let program = "record Marker: Once {} fn bad(value: *&u32) {}"; |
| 7529 | let result = try resolveProgramStr(&mut a, program); |
| 7530 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7531 | } |
| 7532 | } { |
| 7533 | let mut testArena530 = testArena(); |
| 7534 | let testStorage530: 'test530 = &mut testArena530 in { |
| 7535 | let mut a = testResolver(testStorage530); |
| 7536 | let program = "record Marker: Once {} static BAD: &u32 = undefined;"; |
| 7537 | let result = try resolveProgramStr(&mut a, program); |
| 7538 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7539 | } |
| 7540 | } { |
| 7541 | let mut testArena531 = testArena(); |
| 7542 | let testStorage531: 'test531 = &mut testArena531 in { |
| 7543 | let mut a = testResolver(testStorage531); |
| 7544 | let program = "record Marker: Once {} fn bad(callback: fn() -> &u32) {}"; |
| 7545 | let result = try resolveProgramStr(&mut a, program); |
| 7546 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 7547 | } |
| 7548 | } |
| 7549 | } |
| 7550 | |
| 7551 | /// Function pointer parameter references remain call-scoped and valid. |
| 7552 | @test unsafe fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) { |
| 7553 | let program = "record Marker: Once {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }"; |
| 7554 | try expectAnalyzeOk(program); |
| 7555 | } |
| 7556 | |
| 7557 | /// Pointer and slice casts cannot change reference ownership. |
| 7558 | @test unsafe fn testRefCastClassPreserved() throws (testing::TestError) { |
| 7559 | { |
| 7560 | let mut testArena532 = testArena(); |
| 7561 | let testStorage532: 'test532 = &mut testArena532 in { |
| 7562 | let mut a = testResolver(testStorage532); |
| 7563 | let program = "record Marker: Once {} fn cast(value: &u32) { value as *u32; }"; |
| 7564 | let result = try resolveProgramStr(&mut a, program); |
| 7565 | let err = try expectError(&result); |
| 7566 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 7567 | else throw testing::TestError::Failed; |
| 7568 | } |
| 7569 | } { |
| 7570 | let mut testArena533 = testArena(); |
| 7571 | let testStorage533: 'test533 = &mut testArena533 in { |
| 7572 | let mut a = testResolver(testStorage533); |
| 7573 | let program = "record Marker: Once {} fn cast(values: &[u32]) { values as *[u32]; }"; |
| 7574 | let result = try resolveProgramStr(&mut a, program); |
| 7575 | let err = try expectError(&result); |
| 7576 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 7577 | else throw testing::TestError::Failed; |
| 7578 | } |
| 7579 | } |
| 7580 | } |
| 7581 | |
| 7582 | /// Every operation that interprets an unsafe address requires an unsafe declaration. |
| 7583 | @test unsafe fn testUnsafePointerOperationsRejected() throws (testing::TestError) { |
| 7584 | { |
| 7585 | let mut testArena534 = testArena(); |
| 7586 | let testStorage534: 'test534 = &mut testArena534 in { |
| 7587 | let mut a = testResolver(testStorage534); |
| 7588 | let program = "record Marker: Once {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }"; |
| 7589 | let result = try resolveProgramStr(&mut a, program); |
| 7590 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7591 | } |
| 7592 | } { |
| 7593 | let mut testArena535 = testArena(); |
| 7594 | let testStorage535: 'test535 = &mut testArena535 in { |
| 7595 | let mut a = testResolver(testStorage535); |
| 7596 | let program = "record Marker: Once {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }"; |
| 7597 | let result = try resolveProgramStr(&mut a, program); |
| 7598 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7599 | } |
| 7600 | } { |
| 7601 | let mut testArena536 = testArena(); |
| 7602 | let testStorage536: 'test536 = &mut testArena536 in { |
| 7603 | let mut a = testResolver(testStorage536); |
| 7604 | let program = "record Marker: Once {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }"; |
| 7605 | let result = try resolveProgramStr(&mut a, program); |
| 7606 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7607 | } |
| 7608 | } { |
| 7609 | let mut testArena537 = testArena(); |
| 7610 | let testStorage537: 'test537 = &mut testArena537 in { |
| 7611 | let mut a = testResolver(testStorage537); |
| 7612 | let program = "record Marker: Once {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }"; |
| 7613 | let result = try resolveProgramStr(&mut a, program); |
| 7614 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7615 | } |
| 7616 | } { |
| 7617 | let mut testArena538 = testArena(); |
| 7618 | let testStorage538: 'test538 = &mut testArena538 in { |
| 7619 | let mut a = testResolver(testStorage538); |
| 7620 | let program = "record Marker: Once {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }"; |
| 7621 | let result = try resolveProgramStr(&mut a, program); |
| 7622 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7623 | } |
| 7624 | } { |
| 7625 | let mut testArena539 = testArena(); |
| 7626 | let testStorage539: 'test539 = &mut testArena539 in { |
| 7627 | let mut a = testResolver(testStorage539); |
| 7628 | let program = "record Marker: Once {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }"; |
| 7629 | let result = try resolveProgramStr(&mut a, program); |
| 7630 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7631 | } |
| 7632 | } { |
| 7633 | let mut testArena540 = testArena(); |
| 7634 | let testStorage540: 'test540 = &mut testArena540 in { |
| 7635 | let mut a = testResolver(testStorage540); |
| 7636 | let program = "record Marker: Once {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }"; |
| 7637 | let result = try resolveProgramStr(&mut a, program); |
| 7638 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7639 | } |
| 7640 | } |
| 7641 | } |
| 7642 | |
| 7643 | /// Pointer offsets require an unsafe function for either operand order. |
| 7644 | @test unsafe fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) { |
| 7645 | let programs = &[ |
| 7646 | "fn run(p: *u8) -> *u8 { return p + 1; }", |
| 7647 | "fn run(p: *u8) -> *u8 { return 1 + p; }", |
| 7648 | "fn run(p: *u8) -> *u8 { return p - 1; }", |
| 7649 | "fn run(input: *mut u8) { let mut p = input; set p += 1; }", |
| 7650 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let p = &DATA[0]; return *(p + 1); }", |
| 7651 | ]; |
| 7652 | for program in programs { |
| 7653 | let mut testArena541 = testArena(); |
| 7654 | let testStorage541: 'test541 = &mut testArena541 in { |
| 7655 | let mut a = testResolver(testStorage541); |
| 7656 | let result = try resolveProgramStr(&mut a, program); |
| 7657 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7658 | } |
| 7659 | } |
| 7660 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return p + 1; }"); |
| 7661 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return 1 + p; }"); |
| 7662 | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut u8 { return p - 1; }"); |
| 7663 | try expectAnalyzeOk("fn run(value: u32) -> u32 { return value + 1; }"); |
| 7664 | } |
| 7665 | |
| 7666 | /// Casts that reinterpret storage require an unsafe function. |
| 7667 | @test unsafe fn testStorageCastsRequireUnsafe() throws (testing::TestError) { |
| 7668 | let programs = &[ |
| 7669 | "fn run(p: *u8) -> *u64 { return p as *u64; }", |
| 7670 | "fn run(p: &u8) -> u64 { return *(p as &u64); }", |
| 7671 | "fn run(p: *mut u8) -> *mut u64 { return p as *mut u64; }", |
| 7672 | "fn run(p: *opaque) -> *u64 { return p as *u64; }", |
| 7673 | "fn run(p: **u8) -> **u64 { return p as **u64; }", |
| 7674 | "fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }", |
| 7675 | "fn run(s: *[opaque]) -> *[u64] { return s as *[u64]; }", |
| 7676 | "fn run(s: &mut [u64]) { let _ = s as &mut [u8]; }", |
| 7677 | "static DATA: [u8; 1] = [42]; fn run() -> u64 { return *(&DATA[0] as *u64); }", |
| 7678 | ]; |
| 7679 | for program in programs { |
| 7680 | let mut testArena542 = testArena(); |
| 7681 | let testStorage542: 'test542 = &mut testArena542 in { |
| 7682 | let mut a = testResolver(testStorage542); |
| 7683 | let result = try resolveProgramStr(&mut a, program); |
| 7684 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7685 | } |
| 7686 | } |
| 7687 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u64 { return p as *u64; }"); |
| 7688 | try expectAnalyzeOk("unsafe fn run(p: &u8) -> u64 { return *(p as &u64); }"); |
| 7689 | try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *[u64] { return s as *[u64]; }"); |
| 7690 | try expectAnalyzeOk("fn run(p: *u8) -> *u8 { return p as *u8; }"); |
| 7691 | try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p as *opaque; }"); |
| 7692 | try expectAnalyzeOk("fn run(s: *[u8]) -> *[opaque] { return s as *[opaque]; }"); |
| 7693 | try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p as *u8; }"); |
| 7694 | } |
| 7695 | |
| 7696 | /// Explicit slice bounds require an unsafe function. |
| 7697 | @test unsafe fn testSliceConstructionRequiresUnsafe() throws (testing::TestError) { |
| 7698 | let programs = &[ |
| 7699 | "fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }", |
| 7700 | "fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }", |
| 7701 | "fn run(p: &u8) -> u8 { return @sliceOf(p, 100)[99]; }", |
| 7702 | "fn run(p: &mut u8) { set @sliceOf(p, 100)[99] = 1; }", |
| 7703 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = @sliceOf(&DATA[0], 100); return s[99]; }", |
| 7704 | ]; |
| 7705 | for program in programs { |
| 7706 | let mut testArena543 = testArena(); |
| 7707 | let testStorage543: 'test543 = &mut testArena543 in { |
| 7708 | let mut a = testResolver(testStorage543); |
| 7709 | let result = try resolveProgramStr(&mut a, program); |
| 7710 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7711 | } |
| 7712 | } |
| 7713 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }"); |
| 7714 | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }"); |
| 7715 | try expectAnalyzeOk("unsafe fn run(p: &u8) -> u8 { return @sliceOf(p, 1)[0]; }"); |
| 7716 | try expectAnalyzeOk("fn run(s: *[u8]) -> *[u8] { return &s[..]; }"); |
| 7717 | } |
| 7718 | |
| 7719 | /// Slice header writes and mutable field borrows require an unsafe function. |
| 7720 | @test unsafe fn testSliceHeaderMutationRequiresUnsafe() throws (testing::TestError) { |
| 7721 | let programs = &[ |
| 7722 | "fn run(s: *mut [u8]) { set s.len = 100; }", |
| 7723 | "fn run(s: *mut [u8]) { set s.cap = 100; }", |
| 7724 | "fn run(s: *mut [u8], p: *mut u8) { set s.ptr = p; }", |
| 7725 | "fn run(s: *mut [u8]) { set s.len += 1; }", |
| 7726 | "fn change(n: &mut u32) { set *n = 100; } fn run(s: *mut [u8]) { change(&mut s.len); }", |
| 7727 | "fn change(n: &mut u32) { set *n = 100; } fn run(s: *mut [u8]) { change(&mut s.cap); }", |
| 7728 | "fn change(p: &mut *u8, q: *u8) { set *p = q; } fn run(q: *u8) { let mut s: *[u8] = &[]; change(&mut s.ptr, q); }", |
| 7729 | "fn run(s: &mut *[u8]) { set s.len = 100; }", |
| 7730 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let mut s = &DATA[..]; set s.len = 100; return s[99]; }", |
| 7731 | ]; |
| 7732 | for program in programs { |
| 7733 | let mut testArena544 = testArena(); |
| 7734 | let testStorage544: 'test544 = &mut testArena544 in { |
| 7735 | let mut a = testResolver(testStorage544); |
| 7736 | let result = try resolveProgramStr(&mut a, program); |
| 7737 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7738 | } |
| 7739 | } |
| 7740 | try expectAnalyzeOk("unsafe fn run(s: *mut [u8]) { set s.len = 0; set s.cap = 0; }"); |
| 7741 | try expectAnalyzeOk("unsafe fn run(s: *mut [u8], p: *mut u8) { set s.ptr = p; }"); |
| 7742 | try expectAnalyzeOk("fn change(n: &mut u32) { set *n = 0; } unsafe fn run(s: *mut [u8]) { change(&mut s.len); }"); |
| 7743 | try expectAnalyzeOk("fn run(s: *mut [u8]) { set s[0] = 1; }"); |
| 7744 | try expectAnalyzeOk("record R { len: u32 } fn run(r: &mut R) { set r.len = 1; }"); |
| 7745 | } |
| 7746 | |
| 7747 | /// Unsafe blocks permit operations within a safe function. |
| 7748 | @test unsafe fn testUnsafeBlocksAllowed() throws (testing::TestError) { |
| 7749 | try expectAnalyzeOk("fn run(p: *u8) -> *u8 { unsafe { return p + 1; } }"); |
| 7750 | try expectAnalyzeOk("unsafe fn read(p: *unsafe u8) -> u8 { return *p; } fn run(p: *unsafe u8) -> u8 { unsafe { return read(p); } }"); |
| 7751 | try expectAnalyzeOk("fn run(p: *u8) -> *[u8] { unsafe { { return @sliceOf(p, 1); } } }"); |
| 7752 | try expectAnalyzeOk("fn run(p: *u8) -> *u64 { unsafe { unsafe { return p as *u64; } } }"); |
| 7753 | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { unsafe {} return p + 1; }"); |
| 7754 | } |
| 7755 | |
| 7756 | /// Unsafe permission ends at the closing brace and at function boundaries. |
| 7757 | @test unsafe fn testUnsafeBlockContextRestored() throws (testing::TestError) { |
| 7758 | let programs = &[ |
| 7759 | "fn run(p: *u8) -> *u8 { unsafe { let q = p + 1; } return p + 1; }", |
| 7760 | "fn run(p: *u8) -> *u8 { unsafe { unsafe {} } return p + 1; }", |
| 7761 | "unsafe fn first(p: *u8) -> *u8 { return p + 1; } fn second(p: *u8) -> *u8 { return p + 1; }", |
| 7762 | ]; |
| 7763 | for program in programs { |
| 7764 | let mut testArena545 = testArena(); |
| 7765 | let testStorage545: 'test545 = &mut testArena545 in { |
| 7766 | let mut a = testResolver(testStorage545); |
| 7767 | let result = try resolveProgramStr(&mut a, program); |
| 7768 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7769 | } |
| 7770 | } |
| 7771 | let mut testArena546 = testArena(); |
| 7772 | let testStorage546: 'test546 = &mut testArena546 in { |
| 7773 | let mut a = testResolver(testStorage546); |
| 7774 | let result = try resolveProgramStr(&mut a, "unsafe fn act() {} fn run() { unsafe { act(); } act(); }"); |
| 7775 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 7776 | } |
| 7777 | } |
| 7778 | |
| 7779 | /// Resolution errors do not extend an unsafe block's permission. |
| 7780 | @test unsafe fn testUnsafeBlockErrorRestoresContext() throws (testing::TestError) { |
| 7781 | let mut testArena547 = testArena(); |
| 7782 | let testStorage547: 'test547 = &mut testArena547 in { |
| 7783 | let mut a = testResolver(testStorage547); |
| 7784 | let result = try resolveProgramStr(&mut a, "fn run(p: *u8) { unsafe { missing; } let q = p + 1; }"); |
| 7785 | let mut found = false; |
| 7786 | for err in result.diagnostics.errors { |
| 7787 | if let case super::ErrorKind::UnsafeOperation = err.kind { |
| 7788 | set found = true; |
| 7789 | } |
| 7790 | } |
| 7791 | try testing::expect(found); |
| 7792 | } |
| 7793 | } |
| 7794 | |
| 7795 | /// Unsafe blocks preserve reference lifetime checks. |
| 7796 | @test unsafe fn testUnsafeBlockPreservesReferences() throws (testing::TestError) { |
| 7797 | let mut testArena548 = testArena(); |
| 7798 | let testStorage548: 'test548 = &mut testArena548 in { |
| 7799 | let mut a = testResolver(testStorage548); |
| 7800 | let result = try resolveProgramStr(&mut a, "fn run() { let mut n: u32 = 1; let p = &n; unsafe { set n = 2; } }"); |
| 7801 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
| 7802 | } |
| 7803 | } |
| 7804 | |
| 7805 | /// Slice pointer access requires an unsafe context for every slice class. |
| 7806 | @test unsafe fn testSlicePointerRequiresUnsafe() throws (testing::TestError) { |
| 7807 | let programs = &[ |
| 7808 | "fn run(s: *[u8]) -> *u8 { return s.ptr; }", |
| 7809 | "fn run(s: *mut [u8]) -> *mut u8 { return s.ptr; }", |
| 7810 | "fn run(s: &[u8]) -> u8 { return *s.ptr; }", |
| 7811 | "fn run(s: &mut [u8]) { set *s.ptr = 1; }", |
| 7812 | "fn run(s: &*[u8]) -> *u8 { return s.ptr; }", |
| 7813 | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = &DATA[1..]; return *s.ptr; }", |
| 7814 | "fn run(s: *[u8]) -> u64 { return s.ptr as u64; }", |
| 7815 | ]; |
| 7816 | for program in programs { |
| 7817 | let mut testArena549 = testArena(); |
| 7818 | let testStorage549: 'test549 = &mut testArena549 in { |
| 7819 | let mut a = testResolver(testStorage549); |
| 7820 | let result = try resolveProgramStr(&mut a, program); |
| 7821 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7822 | } |
| 7823 | } |
| 7824 | try expectAnalyzeOk("unsafe fn run(s: *[u8]) -> *u8 { return s.ptr; }"); |
| 7825 | try expectAnalyzeOk("fn run(s: *[u8]) -> u64 { unsafe { return s.ptr as u64; } }"); |
| 7826 | try expectAnalyzeOk("fn run(s: *[u8]) -> *u8 { return &s[0]; }"); |
| 7827 | try expectAnalyzeOk("fn run(s: *[u8]) -> u32 { return s.len + s.cap; }"); |
| 7828 | try expectAnalyzeOk("record R { ptr: u32 } fn run(r: R) -> u32 { return r.ptr; }"); |
| 7829 | } |
| 7830 | |
| 7831 | /// Unsafe statics require unsafe permission for reads, writes, and addresses. |
| 7832 | @test unsafe fn testUnsafeStaticAccessRejected() throws (testing::TestError) { |
| 7833 | let programs = &[ |
| 7834 | "unsafe static VALUE: u32 = 7; fn run() -> u32 { return VALUE; }", |
| 7835 | "unsafe static VALUE: u32 = 7; fn run() { set VALUE = 8; }", |
| 7836 | "unsafe static VALUE: u32 = 7; fn run() -> *u32 { return &VALUE; }", |
| 7837 | "unsafe static VALUE: u32 = 7; fn run() -> *mut u32 { return &mut VALUE; }", |
| 7838 | "unsafe static DATA: [u8; 1] = [42]; fn run() -> u8 { return DATA[0]; }", |
| 7839 | "unsafe static DATA: [u8; 1] = [42]; fn run() -> u32 { return DATA.len; }", |
| 7840 | "record R { value: u32 } unsafe static DATA: R = R { value: 7 }; fn run() -> u32 { return DATA.value; }", |
| 7841 | ]; |
| 7842 | for program in programs { |
| 7843 | let mut testArena550 = testArena(); |
| 7844 | let testStorage550: 'test550 = &mut testArena550 in { |
| 7845 | let mut a = testResolver(testStorage550); |
| 7846 | let result = try resolveProgramStr(&mut a, program); |
| 7847 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7848 | } |
| 7849 | } |
| 7850 | } |
| 7851 | |
| 7852 | /// Unsafe blocks and functions can access unsafe statics. |
| 7853 | @test unsafe fn testUnsafeStaticAccessAllowed() throws (testing::TestError) { |
| 7854 | try expectAnalyzeOk("unsafe static VALUE: u32 = 7; unsafe fn run() -> u32 { set VALUE = 8; return VALUE; }"); |
| 7855 | try expectAnalyzeOk("unsafe static VALUE: u32 = 7; fn run() -> u32 { unsafe { return VALUE; } }"); |
| 7856 | try expectAnalyzeOk("unsafe static VALUE: u32 = 7; unsafe fn run() -> *mut u32 { return &mut VALUE; }"); |
| 7857 | try expectAnalyzeOk("static VALUE: u32 = 7; fn run() -> u32 { return VALUE; }"); |
| 7858 | try expectAnalyzeOk("constant BYTES: *[u8] = \"x\"; unsafe static DATA: *[u64] = BYTES as *[u64];"); |
| 7859 | } |
| 7860 | |
| 7861 | /// Imports preserve the unsafe access requirement of a static. |
| 7862 | @test unsafe fn testUnsafeStaticImportsRejected() throws (testing::TestError) { |
| 7863 | let programs = &[ |
| 7864 | "use root::storage; fn run() -> u32 { return storage::VALUE; }", |
| 7865 | "use root::storage::*; fn run() -> u32 { return VALUE; }", |
| 7866 | ]; |
| 7867 | for program in programs { |
| 7868 | let mut testArena551 = testArena(); |
| 7869 | let testStorage551: 'test551 = &mut testArena551 in { |
| 7870 | let mut a = testResolver(testStorage551); |
| 7871 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
| 7872 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod storage; mod app;", &mut arena); |
| 7873 | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "storage", "export unsafe static VALUE: u32 = 7;", &mut arena); |
| 7874 | let _ = try registerModule(&mut MODULE_GRAPH, rootId, "app", program, &mut arena); |
| 7875 | let result = try resolveModuleTree(&mut a, rootId); |
| 7876 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7877 | } |
| 7878 | } |
| 7879 | } |
| 7880 | |
| 7881 | /// An invalid unsafe static initializer does not grant permission to other initializers. |
| 7882 | @test unsafe fn testUnsafeStaticInitializerRestoresContext() throws (testing::TestError) { |
| 7883 | let mut testArena552 = testArena(); |
| 7884 | let testStorage552: 'test552 = &mut testArena552 in { |
| 7885 | let mut a = testResolver(testStorage552); |
| 7886 | let result = try resolveProgramStr(&mut a, "unsafe static BAD: u32 = true; static DATA: *[u64] = &[1 as u8] as *[u64];"); |
| 7887 | let _ = try expectError(&result); |
| 7888 | try testing::expect(not a.inUnsafeContext); |
| 7889 | |
| 7890 | let mut testArena553 = testArena(); |
| 7891 | let testStorage553: 'test553 = &mut testArena553 in { |
| 7892 | let mut b = testResolver(testStorage553); |
| 7893 | let next = try resolveProgramStr(&mut b, "constant BYTES: *[u8] = \"x\"; unsafe static VALUE: u32 = 7; static DATA: *[u64] = BYTES as *[u64];"); |
| 7894 | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
| 7895 | } |
| 7896 | } |
| 7897 | } |
| 7898 | |
| 7899 | /// Uninitialized values require an explicit unsafe context in every value position. |
| 7900 | @test unsafe fn testUndefinedRequiresUnsafe() throws (testing::TestError) { |
| 7901 | let programs = &[ |
| 7902 | "fn run() -> u8 { let pointers: [*u8; 1] = undefined; return *pointers[0]; }", |
| 7903 | "fn run() { let n: u32 = undefined; }", |
| 7904 | "fn run() -> *u8 { return undefined; }", |
| 7905 | "fn take(p: *u8) {} fn run() { take(undefined); }", |
| 7906 | "fn run(p: *u8) { let mut q = p; set q = undefined; }", |
| 7907 | "fn run() { let pointers: [*u8; 1] = [undefined]; }", |
| 7908 | "fn run() { let pointers: [*u8; 2] = [undefined; 2]; }", |
| 7909 | "record R: Copy { p: *u8 } fn run() { let r = R { p: undefined }; }", |
| 7910 | "union U: Copy { Value { p: *u8 } } fn run() { let u = U::Value { p: undefined }; }", |
| 7911 | "static P: *u8 = undefined;", |
| 7912 | "constant P: *u8 = undefined;", |
| 7913 | "static DATA: [u8; 4] = undefined;", |
| 7914 | "record R: Copy { p: *u8 } static VALUE: R = R { p: undefined };", |
| 7915 | ]; |
| 7916 | for program in programs { |
| 7917 | let mut testArena554 = testArena(); |
| 7918 | let testStorage554: 'test554 = &mut testArena554 in { |
| 7919 | let mut a = testResolver(testStorage554); |
| 7920 | let result = try resolveProgramStr(&mut a, program); |
| 7921 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7922 | } |
| 7923 | } |
| 7924 | try expectAnalyzeOk("unsafe fn run() { let p: *u8 = undefined; }"); |
| 7925 | try expectAnalyzeOk("fn run() -> u32 { unsafe { let mut n: u32 = undefined; set n = 7; return n; } }"); |
| 7926 | try expectAnalyzeOk("unsafe static P: *u8 = undefined;"); |
| 7927 | try expectAnalyzeOk("record R: Copy { p: *u8 } unsafe static VALUE: R = R { p: undefined };"); |
| 7928 | try expectAnalyzeOk("fn run() { let data: [u8; 4] = [0; 4]; let pointer: ?*u8 = nil; }"); |
| 7929 | try expectAnalyzeOk("static DATA: [u8; 4] = [0; 4];"); |
| 7930 | } |
| 7931 | |
| 7932 | /// Unsafe initialization does not grant permission to subsequent safe expressions. |
| 7933 | @test unsafe fn testUndefinedContextRestored() throws (testing::TestError) { |
| 7934 | let mut testArena555 = testArena(); |
| 7935 | let testStorage555: 'test555 = &mut testArena555 in { |
| 7936 | let mut a = testResolver(testStorage555); |
| 7937 | let result = try resolveProgramStr(&mut a, "fn run() { unsafe { let p: *u8 = undefined; } let q: *u8 = undefined; }"); |
| 7938 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 7939 | let mut testArena556 = testArena(); |
| 7940 | let testStorage556: 'test556 = &mut testArena556 in { |
| 7941 | let mut b = testResolver(testStorage556); |
| 7942 | let next = try resolveProgramStr(&mut b, "unsafe static P: *u8 = undefined; static Q: *u8 = undefined;"); |
| 7943 | try expectErrorKind(&next, super::ErrorKind::UnsafeOperation); |
| 7944 | } |
| 7945 | } |
| 7946 | } |
| 7947 | |
| 7948 | /// Unsafe declarations may compose unsafe operations and calls. |
| 7949 | @test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
| 7950 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }"; |
| 7951 | try expectAnalyzeOk(program); |
| 7952 | } |
| 7953 | |
| 7954 | /// Unsafe code may drop a checked reference to an unsafe pointer. |
| 7955 | @test unsafe fn testUnsafePointerFromReference() throws (testing::TestError) { |
| 7956 | let program = "record Marker: Once {} unsafe fn store(pointer: *unsafe mut u32) { set *pointer = 42; } unsafe fn run() { let mut value: u32 = 0; store(&mut value as *unsafe mut u32); }"; |
| 7957 | try expectAnalyzeOk(program); |
| 7958 | } |
| 7959 | |
| 7960 | /// Dropping a reference to an unsafe pointer cannot add mutability. |
| 7961 | @test unsafe fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) { |
| 7962 | let mut testArena557 = testArena(); |
| 7963 | let testStorage557: 'test557 = &mut testArena557 in { |
| 7964 | let mut a = testResolver(testStorage557); |
| 7965 | let program = "record Marker: Once {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }"; |
| 7966 | let result = try resolveProgramStr(&mut a, program); |
| 7967 | let err = try expectError(&result); |
| 7968 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 7969 | else throw testing::TestError::Failed; |
| 7970 | } |
| 7971 | } |
| 7972 | |
| 7973 | /// Recursive cast validation cannot hide a checked-to-unsafe transition. |
| 7974 | @test unsafe fn testNestedUnsafePointerCastRejected() throws (testing::TestError) { |
| 7975 | let mut testArena558 = testArena(); |
| 7976 | let testStorage558: 'test558 = &mut testArena558 in { |
| 7977 | let mut a = testResolver(testStorage558); |
| 7978 | let program = "record Marker: Once {} fn run(value: **u32) { value as **unsafe u32; }"; |
| 7979 | let result = try resolveProgramStr(&mut a, program); |
| 7980 | let err = try expectError(&result); |
| 7981 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 7982 | else throw testing::TestError::Failed; |
| 7983 | } |
| 7984 | } |
| 7985 | |
| 7986 | /// Unsafe code may drop a checked slice reference to an unsafe slice. |
| 7987 | @test unsafe fn testUnsafeSliceFromReference() throws (testing::TestError) { |
| 7988 | let program = "record Marker: Once {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }"; |
| 7989 | try expectAnalyzeOk(program); |
| 7990 | } |
| 7991 | |
| 7992 | /// Slice casts cannot add mutability. |
| 7993 | @test unsafe fn testSliceCastCannotAddMutability() throws (testing::TestError) { |
| 7994 | let mut testArena559 = testArena(); |
| 7995 | let testStorage559: 'test559 = &mut testArena559 in { |
| 7996 | let mut a = testResolver(testStorage559); |
| 7997 | let program = "record Marker: Once {} fn run(values: &[u32]) { values as &mut [u32]; }"; |
| 7998 | let result = try resolveProgramStr(&mut a, program); |
| 7999 | let err = try expectError(&result); |
| 8000 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 8001 | else throw testing::TestError::Failed; |
| 8002 | } |
| 8003 | } |
| 8004 | |
| 8005 | /// Mutable unsafe receivers do not create checked exclusive loans. |
| 8006 | @test unsafe fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) { |
| 8007 | let program = "record Marker: Once {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }"; |
| 8008 | try expectAnalyzeOk(program); |
| 8009 | } |
| 8010 | |
| 8011 | /// Unsafe instance-method attributes enable unsafe operations in the body. |
| 8012 | @test unsafe fn testUnsafeInstanceMethodBody() throws (testing::TestError) { |
| 8013 | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (*unsafe Read) get() -> u32; } instance Read for Value { unsafe fn (value: *unsafe Value) get() -> u32 { return value.number; } }"; |
| 8014 | try expectAnalyzeOk(program); |
| 8015 | } |
| 8016 | |
| 8017 | /// Unsafe instance methods cannot implement safe trait contracts. |
| 8018 | @test unsafe fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) { |
| 8019 | let mut testArena560 = testArena(); |
| 8020 | let testStorage560: 'test560 = &mut testArena560 in { |
| 8021 | let mut a = testResolver(testStorage560); |
| 8022 | let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }"; |
| 8023 | let result = try resolveProgramStr(&mut a, program); |
| 8024 | try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch); |
| 8025 | } |
| 8026 | } |
| 8027 | |
| 8028 | /// Unsafe trait methods retain their call-site requirement through dispatch. |
| 8029 | @test unsafe fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) { |
| 8030 | let mut testArena561 = testArena(); |
| 8031 | let testStorage561: 'test561 = &mut testArena561 in { |
| 8032 | let mut a = testResolver(testStorage561); |
| 8033 | let program = "record Marker: Once {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }"; |
| 8034 | let result = try resolveProgramStr(&mut a, program); |
| 8035 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 8036 | } |
| 8037 | } |
| 8038 | |
| 8039 | /// An unsafe callback retains its requirement at an indirect call. |
| 8040 | @test unsafe fn testUnsafeCallbackCallRejected() throws (testing::TestError) { |
| 8041 | let mut testArena562 = testArena(); |
| 8042 | let testStorage562: 'test562 = &mut testArena562 in { |
| 8043 | let mut a = testResolver(testStorage562); |
| 8044 | let result = try resolveProgramStr(&mut a, |
| 8045 | "fn run(callback: unsafe fn() -> u32) -> u32 { return callback(); }"); |
| 8046 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
| 8047 | } |
| 8048 | } |
| 8049 | |
| 8050 | /// An unsafe function cannot enter a safe callback slot. |
| 8051 | @test unsafe fn testUnsafeCallbackAssignmentRejected() throws (testing::TestError) { |
| 8052 | let mut testArena563 = testArena(); |
| 8053 | let testStorage563: 'test563 = &mut testArena563 in { |
| 8054 | let mut a = testResolver(testStorage563); |
| 8055 | let result = try resolveProgramStr(&mut a, |
| 8056 | "unsafe fn load() -> u32 { return 1; } fn run() { let callback: fn() -> u32 = load; }"); |
| 8057 | let err = try expectError(&result); |
| 8058 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 8059 | else throw testing::TestError::Failed; |
| 8060 | } |
| 8061 | } |
| 8062 | |
| 8063 | /// A safe function can enter an unsafe callback slot. |
| 8064 | @test unsafe fn testSafeCallbackIntoUnsafeSlot() throws (testing::TestError) { |
| 8065 | try expectAnalyzeOk( |
| 8066 | "fn load() -> u32 { return 1; } unsafe fn run() -> u32 { let callback: unsafe fn() -> u32 = load; return callback(); }"); |
| 8067 | } |
| 8068 | |
| 8069 | /// Borrowed callback storage must preserve its function safety type. |
| 8070 | @test unsafe fn testBorrowedCallbackSafetyInvariant() throws (testing::TestError) { |
| 8071 | let mut testArena564 = testArena(); |
| 8072 | let testStorage564: 'test564 = &mut testArena564 in { |
| 8073 | let mut a = testResolver(testStorage564); |
| 8074 | let result = try resolveProgramStr(&mut a, |
| 8075 | "fn replace(slot: &mut unsafe fn()) {} fn load() {} fn run() { let mut callback: fn() = load; replace(&mut callback); }"); |
| 8076 | let err = try expectError(&result); |
| 8077 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 8078 | else throw testing::TestError::Failed; |
| 8079 | } |
| 8080 | } |
| 8081 | |
| 8082 | /// Referenced storage must preserve its exact element type in every context. |
| 8083 | @test unsafe fn testPointerStorageCoercionsRejected() throws (testing::TestError) { |
| 8084 | let programs = &[ |
| 8085 | "fn replace(p: &mut ?*u8) { set *p = nil; } static DATA: u8 = 7; fn run() { let mut p = &DATA; replace(&mut p); }", |
| 8086 | "fn replace(p: &mut ?*u8) { set *p = nil; } static DATA: u8 = 7; unsafe fn run() { let mut p = &DATA; replace(&mut p); }", |
| 8087 | "fn replace(p: &mut ?*u8) { set *p = nil; } static DATA: u8 = 7; fn run() { let mut p = &DATA; unsafe { replace(&mut p); } }", |
| 8088 | "fn take(p: *?u32) {} fn run(p: *u32) { take(p); }", |
| 8089 | "unsafe fn take(p: *unsafe mut ?*u8) {} unsafe fn run(p: *unsafe mut *u8) { take(p); }", |
| 8090 | "fn take(p: *[?*u8]) {} fn run(p: *[*u8]) { take(p); }", |
| 8091 | "unsafe fn take(p: *mut [?*u8]) {} unsafe fn run(p: *mut [*u8]) { take(p); }", |
| 8092 | "fn take(p: &mut **u8) {} fn run(p: &mut *mut *u8) { take(p); }", |
| 8093 | ]; |
| 8094 | for program in programs { |
| 8095 | let mut testArena565 = testArena(); |
| 8096 | let testStorage565: 'test565 = &mut testArena565 in { |
| 8097 | let mut a = testResolver(testStorage565); |
| 8098 | let result = try resolveProgramStr(&mut a, program); |
| 8099 | let err = try expectError(&result); |
| 8100 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
| 8101 | else throw testing::TestError::Failed; |
| 8102 | } |
| 8103 | } |
| 8104 | try expectAnalyzeOk("fn take(p: &mut ?*u8) {} fn run() { let mut p: ?*u8 = nil; take(&mut p); }"); |
| 8105 | try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p; }"); |
| 8106 | try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p; }"); |
| 8107 | } |
| 8108 | |
| 8109 | /// Casts cannot add or remove optionality in referenced storage. |
| 8110 | @test unsafe fn testPointerOptionalStorageCastsRejected() throws (testing::TestError) { |
| 8111 | let programs = &[ |
| 8112 | "fn run(p: **u8) { p as *?*u8; }", |
| 8113 | "unsafe fn run(p: *mut *u8) { p as *mut ?*u8; }", |
| 8114 | "fn run(p: *mut *u8) { unsafe { p as *mut ?*u8; } }", |
| 8115 | "unsafe fn run(p: *mut ?*u8) { p as *mut *u8; }", |
| 8116 | "unsafe fn run(p: *unsafe mut *u8) { p as *unsafe mut ?*u8; }", |
| 8117 | "unsafe fn run(p: *mut [*u8]) { p as *mut [?*u8]; }", |
| 8118 | ]; |
| 8119 | for program in programs { |
| 8120 | let mut testArena566 = testArena(); |
| 8121 | let testStorage566: 'test566 = &mut testArena566 in { |
| 8122 | let mut a = testResolver(testStorage566); |
| 8123 | let result = try resolveProgramStr(&mut a, program); |
| 8124 | let err = try expectError(&result); |
| 8125 | let case super::ErrorKind::InvalidAsCast(_) = err.kind |
| 8126 | else throw testing::TestError::Failed; |
| 8127 | } |
| 8128 | } |
| 8129 | } |
| 8130 | |
| 8131 | /// Pattern references prevent replacement of their source in every context. |
| 8132 | @test unsafe fn testPatternLoanMutationRejected() throws (testing::TestError) { |
| 8133 | let programs = &[ |
| 8134 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &u { case U::A(p) => { set u = U::B(1); *p; } else => {} } }", |
| 8135 | "union U: Copy { A(u64), B(u64) } unsafe fn run() { let mut u = U::A(7); match &u { case U::A(p) => { set u = U::B(1); *p; } else => {} } }", |
| 8136 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &mut u { case U::A(p) => { unsafe { set u = U::B(1); } *p; } else => {} } }", |
| 8137 | "union U: Copy { A(u64), B(u64) } fn change(u: &mut U) { set *u = U::B(1); } fn run() { let mut u = U::A(7); match &u { case U::A(p) => { change(&mut u); *p; } else => {} } }", |
| 8138 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); if let case U::A(p) = &u { set u = U::B(1); *p; } }", |
| 8139 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); while let case U::A(p) = &u { set u = U::B(1); *p; } }", |
| 8140 | "union U: Copy { A(u64), B(u64) } fn run(u: *mut U) { match u { case U::A(p) => { let moved = u; *p; } else => {} } }", |
| 8141 | "record R: 'r + Copy { p: &'r u32 } fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }", |
| 8142 | "record R: 'r + Copy { p: &'r u32 } unsafe fn run 'r (p: &'r u32) { let mut u = R 'r { p }; match &u { case R { p: q } => { set u = R 'r { p }; **q; } else => {} } }", |
| 8143 | ]; |
| 8144 | for program in programs { |
| 8145 | let mut testArena567 = testArena(); |
| 8146 | let testStorage567: 'test567 = &mut testArena567 in { |
| 8147 | let mut a = testResolver(testStorage567); |
| 8148 | let result = try resolveProgramStr(&mut a, program); |
| 8149 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
| 8150 | } |
| 8151 | } |
| 8152 | } |
| 8153 | |
| 8154 | /// Pattern loans end at their scope and permit writes through mutable payload references. |
| 8155 | @test unsafe fn testPatternLoanScopeAllowed() throws (testing::TestError) { |
| 8156 | try expectAnalyzeOk("union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &u { case U::A(p) => { *p; } else => {} } set u = U::B(1); }"); |
| 8157 | try expectAnalyzeOk("union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); match &mut u { case U::A(p) => { set *p = 8; } else => {} } set u = U::B(1); }"); |
| 8158 | try expectAnalyzeOk("union U: Copy { A(u64), B(u64) } fn run() { let mut u = U::A(7); if let case U::A(p) = &u { *p; } else { set u = U::A(1); } set u = U::B(1); }"); |
| 8159 | } |
| 8160 | |
| 8161 | /// Replacing a borrowed pointer payload cannot forge an address. |
| 8162 | @test unsafe fn testBorrowedPointerPayloadReplacementRejected() throws (testing::TestError) { |
| 8163 | let programs = &[ |
| 8164 | "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }", |
| 8165 | "static DATA: u8 = 7; union U: Copy { A(*u8), B(u64) } unsafe fn run() -> u8 { let mut u = U::A(&DATA); match &u { case U::A(p) => { set u = U::B(1); return **p; } else => return 0, } }", |
| 8166 | ]; |
| 8167 | for program in programs { |
| 8168 | let mut testArena568 = testArena(); |
| 8169 | let testStorage568: 'test568 = &mut testArena568 in { |
| 8170 | let mut a = testResolver(testStorage568); |
| 8171 | let result = try resolveProgramStr(&mut a, program); |
| 8172 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
| 8173 | } |
| 8174 | } |
| 8175 | } |
| 8176 | |
| 8177 | /// Guards, nested patterns, and unsafe calls must preserve pattern source storage. |
| 8178 | @test unsafe fn testPatternLoanIndirectMutationRejected() throws (testing::TestError) { |
| 8179 | let programs = &[ |
| 8180 | "fn run() { let mut u: ?u64 = 7; match &u { p => { set u = nil; *p; } else => {} } }", |
| 8181 | "union U: Copy { A(u64), B(u64) } fn change(u: &mut U) -> bool { set *u = U::B(1); return true; } fn run() { let mut u = U::A(7); match &u { case U::A(p) if change(&mut u) => { *p; } else => {} } }", |
| 8182 | "union U: Copy { A(u64), B(u64) } unsafe fn change(u: *unsafe mut U) { set *u = U::B(1); } unsafe fn run(u: *unsafe mut U) { match u { case U::A(p) => { change(u); *p; } else => {} } }", |
| 8183 | "union U: Copy { A(u64), B(u64) } record R: Copy { value: U } fn run() { let mut u = R { value: U::A(7) }; match &u.value { case U::A(p) => { set u.value = U::B(1); *p; } else => {} } }", |
| 8184 | "union U: Copy { A(u64), B(u64) } fn run() { let mut u = [U::A(7)]; match &u[0] { case U::A(p) => { set u[0] = U::B(1); *p; } else => {} } }", |
| 8185 | "union U: Copy { A(u64), B(u64) } unsafe fn (u: *unsafe mut U) change() { set *u = U::B(1); } unsafe fn run(u: *unsafe mut U) { match u { case U::A(p) => { u.change(); *p; } else => {} } }", |
| 8186 | ]; |
| 8187 | for program in programs { |
| 8188 | let mut testArena569 = testArena(); |
| 8189 | let testStorage569: 'test569 = &mut testArena569 in { |
| 8190 | let mut a = testResolver(testStorage569); |
| 8191 | let result = try resolveProgramStr(&mut a, program); |
| 8192 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("u")); |
| 8193 | } |
| 8194 | } |
| 8195 | } |
| 8196 | |
| 8197 | /// Binding mutability does not permit writes through immutable pointers. |
| 8198 | @test unsafe fn testMutableBindingImmutableTargetRejected() throws (testing::TestError) { |
| 8199 | let programs = &[ |
| 8200 | "record R: Copy { n: u8 } fn run(input: *R) { let mut p = input; set p.n = 1; }", |
| 8201 | "record R: Copy { n: u8 } fn run(input: *R) { let mut p = input; let q = &mut p.n; }", |
| 8202 | "static DATA: [u8; 1] = [0]; fn run() { let mut p = &DATA; set p[0] = 1; }", |
| 8203 | "static DATA: [u8; 1] = [0]; fn run() { let mut p = &DATA; set p[..] = 1; }", |
| 8204 | "static DATA: [u8; 1] = [0]; fn run() { let mut p = &DATA; let q = &mut p[0]; }", |
| 8205 | "record R: Copy { n: u8 } fn (r: &mut R) change() { set r.n = 1; } fn run(input: *R) { let mut p = input; p.change(); }", |
| 8206 | "fn run(p: *mut u8, q: *mut u8) { set p = q; }", |
| 8207 | ]; |
| 8208 | for program in programs { |
| 8209 | let mut testArena570 = testArena(); |
| 8210 | let testStorage570: 'test570 = &mut testArena570 in { |
| 8211 | let mut a = testResolver(testStorage570); |
| 8212 | let result = try resolveProgramStr(&mut a, program); |
| 8213 | try expectErrorKind(&result, super::ErrorKind::ImmutableBinding); |
| 8214 | } |
| 8215 | } |
| 8216 | } |
| 8217 | |
| 8218 | /// Mutable targets support access through fixed and mutable pointer bindings. |
| 8219 | @test unsafe fn testMutablePointerTargetsAllowed() throws (testing::TestError) { |
| 8220 | try expectAnalyzeOk("record R: Copy { n: u8 } fn (r: &mut R) change() { set r.n = 2; } fn run(p: *mut R) { set p.n = 1; p.change(); let q = &mut p.n; set *q = 3; }"); |
| 8221 | try expectAnalyzeOk("static DATA: [u8; 2] = [0, 0]; fn run() { let p = &mut DATA; set p[0] = 1; set p[..] = 2; let q = &mut p[0]; set *q = 3; }"); |
| 8222 | try expectAnalyzeOk("fn run(first: *u8, second: *u8) { let mut p = first; set p = second; }"); |
| 8223 | } |
| 8224 | |
| 8225 | /// Pattern access through raw pointers requires an unsafe context. |
| 8226 | @test unsafe fn testRawPointerPatternRejected() throws (testing::TestError) { |
| 8227 | let programs = &[ |
| 8228 | "union U: Copy { A(u8), B } fn run(p: *unsafe U) { match p { case U::A(n) => { *n; } else => {} } }", |
| 8229 | "union U: Copy { A(u8), B } fn run(p: *unsafe U) { if let case U::A(n) = p { *n; } }", |
| 8230 | "union U: Copy { A(u8), B } fn run(p: *unsafe U) { while let case U::A(n) = p { *n; break; } }", |
| 8231 | "record R: Copy { n: u8 } fn run(p: *unsafe R) { let case R { n } = p else return; }", |
| 8232 | ]; |
| 8233 | for program in programs { |
| 8234 | let mut testArena571 = testArena(); |
| 8235 | let testStorage571: 'test571 = &mut testArena571 in { |
| 8236 | let mut a = testResolver(testStorage571); |
| 8237 | let result = try resolveProgramStr(&mut a, program); |
| 8238 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
| 8239 | } |
| 8240 | } |
| 8241 | } |
| 8242 | |
| 8243 | /// Unsafe contexts permit pattern access through raw pointers. |
| 8244 | @test unsafe fn testRawPointerPatternsAllowed() throws (testing::TestError) { |
| 8245 | try expectAnalyzeOk("union U: Copy { A(u8), B } unsafe fn run(p: *unsafe U) { match p { case U::A(n) => { *n; } else => {} } if let case U::A(n) = p { *n; } while let case U::A(n) = p { *n; break; } }"); |
| 8246 | try expectAnalyzeOk("record R: Copy { n: u8 } unsafe fn run(p: *unsafe R) { let case R { n } = p else return; }"); |
| 8247 | } |
| 8248 | |
| 8249 | /// Slice append requires the allocator layout and callback signature. |
| 8250 | @test unsafe fn testSliceAppendAllocatorRejected() throws (testing::TestError) { |
| 8251 | let programs = &[ |
| 8252 | "record A {} fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8253 | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8254 | "record A(fn(*mut opaque, u32, u32) -> *mut opaque, *mut opaque); fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8255 | "fn run(s: *mut [u8]) { s.append(1, 0); }", |
| 8256 | "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8257 | "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8258 | "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8259 | "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8260 | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8261 | "union E: Copy { Bad } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
| 8262 | ]; |
| 8263 | for program in programs { |
| 8264 | let mut testArena572 = testArena(); |
| 8265 | let testStorage572: 'test572 = &mut testArena572 in { |
| 8266 | let mut a = testResolver(testStorage572); |
| 8267 | let result = try resolveProgramStr(&mut a, program); |
| 8268 | try expectErrorKind(&result, super::ErrorKind::InvalidSliceAllocator); |
| 8269 | } |
| 8270 | } |
| 8271 | } |
| 8272 | |
| 8273 | /// Slice allocators can use raw context pointers. |
| 8274 | @test unsafe fn testSliceAppendRawContextAllowed() throws (testing::TestError) { |
| 8275 | try expectAnalyzeOk("record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }"); |
| 8276 | } |
| 8277 | |
| 8278 | /// A live pattern reference protects its payload from writes through copied pointers. |
| 8279 | @test unsafe fn testPatternPayloadPointerAliasesRejected() throws (testing::TestError) { |
| 8280 | let programs = &[ |
| 8281 | "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; match p { case U::A(n) => { set *q = U::B; *n; } else => {} } }", |
| 8282 | "union U: Copy { A(*u8), B(u64) } fn run(p: *mut U) -> u8 { let q = p; match p { case U::A(n) => { set *q = U::B(1); return **n; } else => return 0, } }", |
| 8283 | "union U: Copy { A(u8), B } fn change(p: &mut U) { set *p = U::B; } fn run(p: *mut U) { let q = p; if let case U::A(n) = p { change(q); *n; } }", |
| 8284 | "union U: Copy { A(u8), B } fn run(p: *mut U) { let q = p; let r = q; while let case U::A(n) = p { set *r = U::B; *n; break; } }", |
| 8285 | ]; |
| 8286 | for program in programs { |
| 8287 | let mut testArena573 = testArena(); |
| 8288 | let testStorage573: 'test573 = &mut testArena573 in { |
| 8289 | let mut a = testResolver(testStorage573); |
| 8290 | let result = try resolveProgramStr(&mut a, program); |
| 8291 | let _ = try expectError(&result); |
| 8292 | } |
| 8293 | } |
| 8294 | } |
| 8295 | |
| 8296 | /// Every access to a moved safe mutable pointer is rejected. |
| 8297 | @test unsafe fn testMutablePointerMovesRejected() throws (testing::TestError) { |
| 8298 | let programs = &[ |
| 8299 | "fn run(p: *mut u8) { let q = p; *p; }", |
| 8300 | "fn run(p: *mut u8) { let q = p; set *p = 1; }", |
| 8301 | "fn run(p: *mut u8) { let q = p; let r = &*p; }", |
| 8302 | "fn read(p: &u8) {} fn run(p: *mut u8) { let q = p; read(p); }", |
| 8303 | "fn run(p: &mut u8) { let q = p; *p; }", |
| 8304 | "fn run(p: *mut [u8]) { let q = p; p.len; }", |
| 8305 | "fn run(p: &mut [u8]) { let q = p; p[0]; }", |
| 8306 | "record R { value: u8 } fn run(p: *mut R) { let q = p; p.value; }", |
| 8307 | "trait T { fn (&T) read(); } fn run(p: *mut opaque T) { let q = p; p.read(); }", |
| 8308 | "fn run(p: *mut u8, flag: bool) { if flag { let q = p; } *p; }", |
| 8309 | "fn run(p: *mut u8) { loop { let q = p; } }", |
| 8310 | ]; |
| 8311 | for program in programs { |
| 8312 | let mut testArena574 = testArena(); |
| 8313 | let testStorage574: 'test574 = &mut testArena574 in { |
| 8314 | let mut a = testResolver(testStorage574); |
| 8315 | let result = try resolveProgramStr(&mut a, program); |
| 8316 | let _ = try expectError(&result); |
| 8317 | } |
| 8318 | } |
| 8319 | } |
| 8320 | |
| 8321 | /// Copy composites cannot contain safe mutable owners or their containers. |
| 8322 | @test unsafe fn testCopyMutablePointerFieldsRejected() throws (testing::TestError) { |
| 8323 | let programs = &[ |
| 8324 | "record R: Copy { p: *mut u8 }", |
| 8325 | "union U: Copy { A(*mut u8), B }", |
| 8326 | "record R: Copy { p: ?*mut u8 }", |
| 8327 | "record R: Copy { p: [*mut u8; 2] }", |
| 8328 | "record R: Copy { p: *mut [u8] }", |
| 8329 | "trait T {} record R: Copy { p: *mut opaque T }", |
| 8330 | "record Inner { p: *mut u8 } record Outer: Copy { inner: Inner }", |
| 8331 | ]; |
| 8332 | for program in programs { |
| 8333 | let mut testArena575 = testArena(); |
| 8334 | let testStorage575: 'test575 = &mut testArena575 in { |
| 8335 | let mut a = testResolver(testStorage575); |
| 8336 | let result = try resolveProgramStr(&mut a, program); |
| 8337 | try expectErrorKind(&result, super::ErrorKind::CopyContainsNonCopy); |
| 8338 | } |
| 8339 | } |
| 8340 | } |
| 8341 | |
| 8342 | /// Moves transfer safe mutable pointers, and calls can borrow them temporarily. |
| 8343 | @test unsafe fn testMutablePointerMovesAllowed() throws (testing::TestError) { |
| 8344 | try expectAnalyzeOk("fn run(p: *mut u8) -> u8 { let q = p; set *q = 1; return *q; }"); |
| 8345 | try expectAnalyzeOk("fn setValue(p: &mut u8) { set *p = 1; } fn run(p: *mut u8) { setValue(p); setValue(p); *p; }"); |
| 8346 | try expectAnalyzeOk("fn run(p: *u8) { let q = p; *p; *q; }"); |
| 8347 | try expectAnalyzeOk("fn run(p: *mut u8, q: *mut u8) { unsafe { p == q; p as u64; } set *p = 1; set *q = 2; }"); |
| 8348 | try expectAnalyzeOk("record R: Copy { p: *unsafe mut u8 } unsafe fn run(p: *unsafe mut u8) { let q = p; set *p = 1; set *q = 2; }"); |
| 8349 | try expectAnalyzeOk("record R: Copy { p: *unsafe mut [u8] } unsafe fn run(p: *unsafe mut [u8]) { let q = p; set p[0] = 1; set q[0] = 2; }"); |
| 8350 | } |
| 8351 | |
| 8352 | /// Function signature inspection uses immutable descriptors in safe code. |
| 8353 | fn expectImmutableFunctionSignatures 'arena (res: &super::Resolver 'arena, root: *ast::Node) throws (testing::TestError) { |
| 8354 | let first = try typeOf(res, try getBlockStmt(root, 0)); |
| 8355 | let equivalent = try typeOf(res, try getBlockStmt(root, 1)); |
| 8356 | let throwing = try typeOf(res, try getBlockStmt(root, 2)); |
| 8357 | let unsafeCall = try typeOf(res, try getBlockStmt(root, 3)); |
| 8358 | try testing::expect(super::typesEqual(first, equivalent)); |
| 8359 | try testing::expect(not super::typesEqual(first, throwing)); |
| 8360 | try testing::expect(not super::typesEqual(first, unsafeCall)); |
| 8361 | } |
| 8362 | |
| 8363 | /// Local analysis state does not change function signature identity. |
| 8364 | @test unsafe fn testImmutableFunctionSignatures() throws (testing::TestError) { |
| 8365 | let mut testArena576 = testArena(); |
| 8366 | let testStorage576: 'test576 = &mut testArena576 in { |
| 8367 | let mut res = testResolver(testStorage576); |
| 8368 | let result = try resolveProgramStr(&mut res, |
| 8369 | "fn first(x: u8) -> u32 { return 0; } fn equivalent(y: u8) -> u32 { let a: u32 = 1; return a; } fn throwing(x: u8) -> u32 throws (u8) { throw x; } unsafe fn unsafeCall(x: u8) -> u32 { return 0; }"); |
| 8370 | try expectNoErrors(&result); |
| 8371 | try expectImmutableFunctionSignatures(&res, result.root); |
| 8372 | } |
| 8373 | } |
| 8374 | |
| 8375 | /// A diagnostic snapshot keeps its contents when the resolver buffer changes. |
| 8376 | @test unsafe fn testDiagnosticSnapshotOwnsItsErrors() throws (testing::TestError) { |
| 8377 | let mut testArena577 = testArena(); |
| 8378 | let testStorage577: 'test577 = &mut testArena577 in { |
| 8379 | let mut res = testResolver(testStorage577); |
| 8380 | let result = try resolveProgramStr(&mut res, "fn run() { missing; }"); |
| 8381 | let snapshot = result.diagnostics; |
| 8382 | let original = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
| 8383 | set res.errors.entries[0] = super::Error { |
| 8384 | kind: super::ErrorKind::Internal, node: nil, moduleId: 0, |
| 8385 | }; |
| 8386 | let later = super::diagnostics(&mut res); |
| 8387 | try testing::expect(snapshot.errors.len == 1); |
| 8388 | try testing::expect(later.errors.len == 1); |
| 8389 | let first = super::errorAt(snapshot.errors, 0) else throw testing::TestError::Failed; |
| 8390 | try testing::expect(first.kind == original.kind); |
| 8391 | try testing::expect(later.errors[0].kind == super::ErrorKind::Internal); |
| 8392 | try testing::expect(super::errorAt(snapshot.errors, 1) == nil); |
| 8393 | } |
| 8394 | } |
| 8395 | |
| 8396 | /// Prefixes, equal fields, and uncertain element locations must conflict. |
| 8397 | @test unsafe fn testOverlappingFieldBorrows() throws (testing::TestError) { |
| 8398 | let programs = &[ |
| 8399 | "record R: Copy { a: u32, b: u32 } fn take(a: &mut u32, b: &u32) {} fn run(r: &mut R) { take(&mut r.a, &r.a); }", |
| 8400 | "record R: Copy { a: u32, b: u32 } fn take(a: &mut R, b: &u32) {} fn run(r: &mut R) { take(r, &r.b); }", |
| 8401 | "record R: Copy { a: [u8; 2], b: [u8; 2] } fn take(a: &mut u8, b: &u8) {} fn run(r: &mut R) { take(&mut r.a[0], &r.a[1]); }", |
| 8402 | "record R: Copy { a: u32, b: u32 } fn take(a: &mut u32, b: u32) {} fn run(r: &mut R) { take(&mut r.a, r.a); }", |
| 8403 | ]; |
| 8404 | for program in programs { |
| 8405 | let mut testArena578 = testArena(); |
| 8406 | let testStorage578: 'test578 = &mut testArena578 in { |
| 8407 | let mut a = testResolver(testStorage578); |
| 8408 | let result = try resolveProgramStr(&mut a, program); |
| 8409 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
| 8410 | } |
| 8411 | } |
| 8412 | } |
| 8413 | |
| 8414 | /// Local loans exclude writes, competing references, and source reads. |
| 8415 | @test unsafe fn testLocalReferenceConflicts() throws (testing::TestError) { |
| 8416 | let programs = &[ |
| 8417 | "fn run() { let mut n: u32 = 4; let p = &n; set n = 1; }", |
| 8418 | "fn run() { let mut n: u32 = 4; let p = &mut n; n; }", |
| 8419 | "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &n; }", |
| 8420 | "fn run() { let mut n: u32 = 4; let p = &n; let q = &mut n; }", |
| 8421 | "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &mut *p; set *p = 1; }", |
| 8422 | "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &*p; set *p = 1; }", |
| 8423 | "fn take(p: &mut u32) {} fn run() { let mut n: u32 = 4; let p = &n; take(&mut n); }", |
| 8424 | "fn run() { let mut n: u32 = 4; let p = &n; unsafe { set n = 1; } }", |
| 8425 | "fn run() { let mut n: u32 = 4; let p = &mut n; let q = &*p; let moved = p; }", |
| 8426 | ]; |
| 8427 | for program in programs { |
| 8428 | let mut testArena579 = testArena(); |
| 8429 | let testStorage579: 'test579 = &mut testArena579 in { |
| 8430 | let mut a = testResolver(testStorage579); |
| 8431 | let result = try resolveProgramStr(&mut a, program); |
| 8432 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("n")); |
| 8433 | } |
| 8434 | } |
| 8435 | } |
| 8436 | |
| 8437 | /// Reference bindings cannot outlive temporary storage or change their source. |
| 8438 | @test unsafe fn testLocalReferenceStorage() throws (testing::TestError) { |
| 8439 | let programs = &[ |
| 8440 | "fn f 'r (p: &'r mut u32) { let mut cursor = p; }", |
| 8441 | "unsafe fn f 'r (p: &'r mut u32) { let mut cursor = p; }", |
| 8442 | "fn run() { let n: u32 = 4; let mut p: &u32 = &n; }", |
| 8443 | "record R: Copy { n: u32 } fn make() -> R { return R { n: 4 }; } fn run() { let p: &u32 = &make().n; }", |
| 8444 | ]; |
| 8445 | for program in programs { |
| 8446 | let mut testArena580 = testArena(); |
| 8447 | let testStorage580: 'test580 = &mut testArena580 in { |
| 8448 | let mut a = testResolver(testStorage580); |
| 8449 | let result = try resolveProgramStr(&mut a, program); |
| 8450 | try expectErrorKind(&result, super::ErrorKind::RefBinding); |
| 8451 | } |
| 8452 | } |
| 8453 | } |
| 8454 | |
| 8455 | /// Stored and returned values cannot contain a local reference. |
| 8456 | @test unsafe fn testLocalReferenceEscapeRejected() throws (testing::TestError) { |
| 8457 | let programs = &[ |
| 8458 | "fn run() -> *u32 { let n: u32 = 1; let p = &n; return p; }", |
| 8459 | "fn run() -> *u32 { let n: u32 = 1; let p = &n; return p as *u32; }", |
| 8460 | "static DATA: u32 = 0; static P: *u32 = &DATA; fn run() { let n: u32 = 1; let p = &n; set P = p; }", |
| 8461 | "record R: Copy { p: *u32 } fn run() { let n: u32 = 1; let p = &n; let r = R { p }; }", |
| 8462 | ]; |
| 8463 | for program in programs { |
| 8464 | let mut testArena581 = testArena(); |
| 8465 | let testStorage581: 'test581 = &mut testArena581 in { |
| 8466 | let mut a = testResolver(testStorage581); |
| 8467 | let result = try resolveProgramStr(&mut a, program); |
| 8468 | let _ = try expectError(&result); |
| 8469 | } |
| 8470 | } |
| 8471 | } |
| 8472 | |
| 8473 | /// Pointer indirection cannot prove that sibling pointees are disjoint. |
| 8474 | @test unsafe fn testIndirectFieldBorrowConflict() throws (testing::TestError) { |
| 8475 | let programs = &[ |
| 8476 | "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } fn take(a: &mut u32, b: &mut u32) {} unsafe fn run(r: &mut R) { take(&mut *r.a, &mut *r.b); }", |
| 8477 | "record R: Copy { a: *unsafe mut u32, b: *unsafe mut u32 } unsafe fn run(r: &mut R) { let a: &mut u32 = &mut *r.a; let b: &mut u32 = &mut *r.b; }", |
| 8478 | ]; |
| 8479 | for program in programs { |
| 8480 | let mut testArena582 = testArena(); |
| 8481 | let testStorage582: 'test582 = &mut testArena582 in { |
| 8482 | let mut a = testResolver(testStorage582); |
| 8483 | let result = try resolveProgramStr(&mut a, program); |
| 8484 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
| 8485 | } |
| 8486 | } |
| 8487 | } |
| 8488 | |
| 8489 | /// Pattern references protect their field and permit writes to disjoint fields. |
| 8490 | @test unsafe fn testDisjointPatternFieldBorrow() throws (testing::TestError) { |
| 8491 | let mut testArena583 = testArena(); |
| 8492 | let testStorage583: 'test583 = &mut testArena583 in { |
| 8493 | let mut a = testResolver(testStorage583); |
| 8494 | let result = try resolveProgramStr(&mut a, |
| 8495 | "union U: Copy { A(u32), B } record R: Copy { u: U, n: u32 } fn run(r: &mut R) { let alias = &mut r.u; match alias { case U::A(p) => { set *alias = U::B; *p; } else => {} } }"); |
| 8496 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("r")); |
| 8497 | } |
| 8498 | } |
| 8499 | |
| 8500 | /// Slice mutations must preserve storage held by a local reference. |
| 8501 | @test unsafe fn testLocalReferenceSliceMutationRejected() throws (testing::TestError) { |
| 8502 | let programs = &[ |
| 8503 | "fn run(s: *mut [u8]) { let p: &u8 = &s[0]; s.delete(0); }", |
| 8504 | "record A: Copy { func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque, ctx: *unsafe mut opaque } fn run(s: *mut [u8], a: A) { let p: &u8 = &s[0]; s.append(1, a); }", |
| 8505 | ]; |
| 8506 | for program in programs { |
| 8507 | let mut testArena584 = testArena(); |
| 8508 | let testStorage584: 'test584 = &mut testArena584 in { |
| 8509 | let mut a = testResolver(testStorage584); |
| 8510 | let result = try resolveProgramStr(&mut a, program); |
| 8511 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("s")); |
| 8512 | } |
| 8513 | } |
| 8514 | } |
| 8515 | |
| 8516 | /// Owner moves and mutable methods cannot invalidate a local loan. |
| 8517 | @test unsafe fn testLocalReferenceOwnerMutationRejected() throws (testing::TestError) { |
| 8518 | let programs = &[ |
| 8519 | "fn run(p: *mut u32) { let r: &u32 = &*p; let moved = p; }", |
| 8520 | "record R: Copy { a: u32 } fn (p: &mut R) change() { set p.a = 1; } fn run(p: &mut R) { let r = &p.a; p.change(); }", |
| 8521 | ]; |
| 8522 | for program in programs { |
| 8523 | let mut testArena585 = testArena(); |
| 8524 | let testStorage585: 'test585 = &mut testArena585 in { |
| 8525 | let mut a = testResolver(testStorage585); |
| 8526 | let result = try resolveProgramStr(&mut a, program); |
| 8527 | try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p")); |
| 8528 | } |
| 8529 | } |
| 8530 | } |
| 8531 | |
| 8532 | /// Reference locals require a function scope and a reachable initializer result. |
| 8533 | @test unsafe fn testLocalReferenceDeclarationContext() throws (testing::TestError) { |
| 8534 | let mut testArena586 = testArena(); |
| 8535 | let testStorage586: 'test586 = &mut testArena586 in { |
| 8536 | let mut a = testResolver(testStorage586); |
| 8537 | let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;"); |
| 8538 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
| 8539 | } |
| 8540 | } |
| 8541 | |
| 8542 | /// Test-only imports do not require their modules in a normal build. |
| 8543 | @test unsafe fn testConditionalUse() throws (testing::TestError) { |
| 8544 | let mut testArena587 = testArena(); |
| 8545 | let testStorage587: 'test587 = &mut testArena587 in { |
| 8546 | let mut a = testResolver(testStorage587); |
| 8547 | set a.config.buildTest = false; |
| 8548 | let source = "@test use missing::testing; fn value() -> u32 { return 1; }"; |
| 8549 | let result = try resolveProgramStr(&mut a, source); |
| 8550 | try expectNoErrors(&result); |
| 8551 | set a.config.buildTest = true; |
| 8552 | let enabled = try resolveProgramStr(&mut a, source); |
| 8553 | try testing::expect(enabled.diagnostics.errors.len > 0); |
| 8554 | } |
| 8555 | } |
| 8556 | |
| 8557 | /// Retained module identities are accessible through safe lookup. |
| 8558 | fn checkModuleQueries 'arena ( |
| 8559 | res: &super::Resolver 'arena, root: u16, child: u16 |
| 8560 | ) throws (testing::TestError) { |
| 8561 | let rootEntry = super::moduleFor(res, root) else throw testing::TestError::Failed; |
| 8562 | let childEntry = super::moduleFor(res, child) else throw testing::TestError::Failed; |
| 8563 | assert rootEntry.id == root; |
| 8564 | assert childEntry.id == child; |
| 8565 | let parent = childEntry.parent else throw testing::TestError::Failed; |
| 8566 | assert parent == root; |
| 8567 | try testing::expectBytesEq(rootEntry.name, "root"); |
| 8568 | try testing::expectBytesEq(childEntry.name, "child"); |
| 8569 | if let _ = super::moduleFor(res, 0xffff) { |
| 8570 | throw testing::TestError::Failed; |
| 8571 | } |
| 8572 | } |
| 8573 | |
| 8574 | /// Module identities remain valid independently of the graph container. |
| 8575 | @test unsafe fn testSafeModuleQueries() throws (testing::TestError) { |
| 8576 | let mut arena = testArena(); |
| 8577 | let storage: 'test = &mut arena in { |
| 8578 | let mut res = testResolver(storage); |
| 8579 | if let _ = super::moduleFor(&res, 0) { |
| 8580 | throw testing::TestError::Failed; |
| 8581 | } |
| 8582 | let root = try! module::registerRootWithName(&mut MODULE_GRAPH, &mut STRING_POOL, 0, "root", "/root.rad"); |
| 8583 | let child = try! module::registerChild(&mut MODULE_GRAPH, &mut STRING_POOL, root, "child", "/child.rad"); |
| 8584 | let diagnostics = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
| 8585 | assert super::success(&diagnostics); |
| 8586 | set MODULE_GRAPH.entriesLen = 0; |
| 8587 | set MODULE_ENTRIES[root as u32] = nil; |
| 8588 | set MODULE_ENTRIES[child as u32] = nil; |
| 8589 | try checkModuleQueries(&res, root, child); |
| 8590 | let empty = try! super::resolve(&mut res, &MODULE_GRAPH, &[]); |
| 8591 | assert super::success(&empty); |
| 8592 | if let _ = super::moduleFor(&res, root) { |
| 8593 | throw testing::TestError::Failed; |
| 8594 | } |
| 8595 | } |
| 8596 | } |
| 8597 | |
| 8598 | /// Resolver metadata retains its caller's arena when the context moves. |
| 8599 | unsafe fn relocateResolver 'arena (context: super::Resolver 'arena) -> super::Resolver 'arena { |
| 8600 | return context; |
| 8601 | } |
| 8602 | |
| 8603 | @test unsafe fn testResolverRelocation() throws (testing::TestError) { |
| 8604 | static DATA: [u8; 65536] = [0; 65536]; |
| 8605 | static AST_DATA: [u8; 8192] = [0; 8192]; |
| 8606 | static NODES: [super::NodeData; 128] = undefined; |
| 8607 | static ERRORS: [super::Error; 8] = undefined; |
| 8608 | static SCOPE: super::Scope = undefined; |
| 8609 | static POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
| 8610 | let mut arena = alloc::new(&mut DATA[..]); |
| 8611 | let mut astArena = ast::nodeArena(&mut AST_DATA[..]); |
| 8612 | let root = try! parser::parse(scanner::SourceLoc::String, |
| 8613 | "fn answer() -> u32 { return 42; }", &mut astArena, &mut POOL); |
| 8614 | let storage = super::ResolverStorage { |
| 8615 | nodeData: &mut NODES[..], pkgScope: &mut SCOPE, errors: &mut ERRORS[..], |
| 8616 | }; |
| 8617 | let arenaRef: 'arena = &mut arena in { |
| 8618 | let context = super::resolver(arenaRef, storage, super::Config { buildTest: false }); |
| 8619 | let initialized = alloc::used(context.arena); |
| 8620 | try testing::expect(initialized > 0); |
| 8621 | let mut moved = relocateResolver(context); |
| 8622 | let diagnostics = try! super::resolveModuleRoot(&mut moved, root); |
| 8623 | try testing::expect(super::success(&diagnostics)); |
| 8624 | try testing::expect(alloc::used(moved.arena) > initialized); |
| 8625 | try testing::expect(moved.arena.offset == alloc::used(moved.arena)); |
| 8626 | let output = try! alloc::alloc(&mut *moved.arena, @sizeOf(u32), @alignOf(u32)) as *mut u32; |
| 8627 | set *output = 42; |
| 8628 | try testing::expect(*output == 42); |
| 8629 | } |
| 8630 | } |