lib/std/lang/resolver/tests.rad 275.2 KiB raw
1
//! Resolver tests.
2
3
use std::mem;
4
use std::testing;
5
use std::lang::alloc;
6
use std::lang::ast;
7
use std::lang::types;
8
use std::lang::parser;
9
use std::lang::scanner;
10
use std::lang::module;
11
use std::lang::strings;
12
13
/// Synthetic file path used for resolver tests.
14
unsafe constant MODULE_PATH: *[u8] = "/dev/test.rad";
15
16
/// AST arena storage used by resolver tests.
17
unsafe static AST_ARENA: [u8; 2097152] = undefined;
18
19
/// Resolver arena storage used by resolver tests.
20
unsafe static ARENA_STORAGE: [u8; 2097152] = undefined;
21
22
/// Node metadata storage used by resolver tests.
23
unsafe static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined;
24
25
/// Diagnostic storage used by resolver tests.
26
unsafe static ERROR_STORAGE: [super::Error; 16] = undefined;
27
28
/// Package scope used by resolver tests.
29
unsafe static PKG_SCOPE: super::Scope = undefined;
30
31
/// Module entries used by resolver tests.
32
unsafe static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined;
33
34
/// Module graph used by resolver tests.
35
unsafe static MODULE_GRAPH: module::ModuleGraph = undefined;
36
37
/// Module AST arena storage used by resolver tests.
38
unsafe static MODULE_ARENA_STORAGE: [u8; 4096] = undefined;
39
40
/// Module AST arena used by resolver tests.
41
unsafe static MODULE_ARENA: ast::NodeArena = undefined;
42
43
/// Interned string pool used by resolver tests.
44
unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
45
46
/// String literals used in tests.
47
unsafe constant LITERALS: [*[u8]; 15] = [
48
    "Ok", "Error", "R", "S",
49
    "f", "Status", "Pending",
50
    "Some", "None", "First",
51
    "Second", "Opt", "x",
52
    "value", "idx"
53
];
54
55
/// Resolver result with AST, used by test helpers.
56
record TestResult {
57
    diagnostics: super::Diagnostics,
58
    root: *ast::Node,
59
}
60
61
/// Create isolated storage for tests to avoid conflicts with global resolver storage.
62
fn testStorage() -> super::ResolverStorage {
63
    return super::ResolverStorage {
64
        arena: alloc::new(&mut ARENA_STORAGE[..]),
65
        nodeData: &mut NODE_DATA_STORAGE[..],
66
        pkgScope: &mut PKG_SCOPE,
67
        errors: &mut ERROR_STORAGE[..],
68
    };
69
}
70
71
/// Construct a resolver backed by test storage and a synthetic module graph.
72
fn testResolver() -> super::Resolver {
73
    // TODO: This should be initialized only once.
74
    for i in 0..LITERALS.len {
75
        strings::intern(&mut STRING_POOL, LITERALS[i]);
76
    }
77
    // TODO: Use local static for this.
78
    // Reset the module graph for each test.
79
    set MODULE_ARENA = ast::nodeArena(&mut MODULE_ARENA_STORAGE[..]);
80
    set MODULE_GRAPH = module::moduleGraph(&mut MODULE_ENTRIES[..], &mut STRING_POOL, &mut MODULE_ARENA);
81
    let config = super::Config { buildTest: true };
82
    let res = super::resolver(testStorage(), config);
83
84
    return res;
85
}
86
87
/// Resolve a block of statements by wrapping them in a synthetic function.
88
fn resolveStatements(
89
    self: *mut super::Resolver, block: ast::Block, arena: *mut ast::NodeArena
90
) -> TestResult throws (super::ResolveError) {
91
    let module = ast::synthFnModule(arena, super::ANALYZE_BLOCK_FN_NAME, block.statements);
92
    let diagnostics = try super::resolveModuleRoot(self, module.modBody) catch {
93
        return TestResult { diagnostics: super::Diagnostics { errors: self.errors }, root: module.modBody };
94
    };
95
    return TestResult { diagnostics, root: module.fnBody };
96
}
97
98
/// Parse and analyze an expression string for testing.
99
fn resolveExprStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
100
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
101
    let mut p = parser::mkParser(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL);
102
    parser::advance(&mut p);
103
104
    let expr = try parser::parseExpr(&mut p) catch {
105
        panic "resolveExprStr: parsing failed";
106
    };
107
    let diagnostics = try super::resolveExpr(self, expr, &mut arena) catch {
108
        throw testing::TestError::Failed;
109
    };
110
    return TestResult { diagnostics, root: expr };
111
}
112
113
/// Parse and analyze a module string for testing.
114
/// Use this for code with `fn`, `record`, `union`, etc. at the top level.
115
fn resolveProgramStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
116
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
117
    let stmt = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
118
        panic "resolveProgramStr: parsing failed";
119
    };
120
    let diagnostics = try super::resolveModuleRoot(self, stmt) catch {
121
        throw testing::TestError::Failed;
122
    };
123
    return TestResult { diagnostics, root: stmt };
124
}
125
126
/// Parse and analyze a block of statements (eg. inside a function body) for testing.
127
/// Use this for code with `let` bindings and expressions, not module-level declarations.
128
fn resolveBlockStr(self: *mut super::Resolver, stmt: *[u8]) -> TestResult throws (testing::TestError) {
129
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
130
    let parsed = try parser::parse(scanner::SourceLoc::String, stmt, &mut arena, &mut STRING_POOL) catch {
131
        panic "resolveBlockStr: parsing failed";
132
    };
133
    let case ast::NodeValue::Block(block) = parsed.value
134
        else panic "resolveBlockStr: expected block root";
135
136
    let analysis = try resolveStatements(self, block, &mut arena) catch {
137
        throw testing::TestError::Failed;
138
    };
139
    return TestResult {
140
        diagnostics: analysis.diagnostics,
141
        root: analysis.root,
142
    };
143
}
144
145
/// Resolve a module with the full resolution process.
146
fn resolveModuleTree(
147
    res: *mut super::Resolver,
148
    rootId: u16
149
) -> TestResult throws (testing::TestError) {
150
    let root = module::get(&MODULE_GRAPH, rootId)
151
        else throw testing::TestError::Failed;
152
    let rootAst = root.ast
153
        else throw testing::TestError::Failed;
154
    let packages: *[super::Pkg] = &[super::Pkg {
155
        rootEntry: root,
156
        rootAst,
157
    }];
158
    let diagnostics = try super::resolve(res, &MODULE_GRAPH, packages) catch {
159
        throw testing::TestError::Failed;
160
    };
161
    return TestResult { diagnostics, root: rootAst };
162
}
163
164
/// Register a module in the graph and attach a parsed AST to it.
165
/// If parentId is nil, registers as a root module.
166
fn registerModule(
167
    graph: *mut module::ModuleGraph,
168
    parentId: ?u16,
169
    name: *[u8],
170
    code: *[u8],
171
    arena: *mut ast::NodeArena
172
) -> u16 throws (testing::TestError) {
173
    let filePath = "<test>";
174
    let mut modId: u16 = undefined;
175
    if let parent = parentId {
176
        set modId = try module::registerChild(graph, parent, name, filePath) catch {
177
            throw testing::TestError::Failed;
178
        };
179
    } else {
180
        set modId = try module::registerRootWithName(graph, 0, name, filePath) catch {
181
            throw testing::TestError::Failed;
182
        };
183
    }
184
    let root = try parser::parse(scanner::SourceLoc::String, code, arena, &mut STRING_POOL) catch {
185
        panic "registerModule: parsing failed";
186
    };
187
    try module::setAst(graph, modId, root) catch {
188
        panic "registerModule: module not found";
189
    };
190
    return modId;
191
}
192
193
/// Ensure an expression statement produces the expected type and return the expression node.
194
fn expectExprStmtType(self: *super::Resolver, node: *ast::Node, expected: super::Type) -> *ast::Node
195
    throws (testing::TestError)
196
{
197
    let case ast::NodeValue::ExprStmt(expr) = node.value
198
        else throw testing::TestError::Failed;
199
    try expectType(self, expr, expected);
200
201
    return expr;
202
}
203
204
/// Assert that the test result contains no diagnostic errors.
205
fn expectNoErrors(r: *TestResult) throws (testing::TestError) {
206
    try testing::expect(super::success(&r.diagnostics));
207
}
208
209
/// Extract the first error from a test result, failing if none exists.
210
fn expectError(result: *TestResult) -> *super::Error throws (testing::TestError) {
211
    let err = super::errorAt(&result.diagnostics.errors[..], 0)
212
        else throw testing::TestError::Failed;
213
    return err;
214
}
215
216
/// Check if two error kinds match.
217
fn errorKindMatches(actual: *super::ErrorKind, expected: super::ErrorKind) -> bool {
218
    if let case super::ErrorKind::DuplicateBinding(expectedName) = expected {
219
        if let case super::ErrorKind::DuplicateBinding(actualName) = *actual {
220
            return mem::eq(actualName, expectedName);
221
        }
222
        return false;
223
    }
224
    if let case super::ErrorKind::UnresolvedSymbol(expectedName) = expected {
225
        if let case super::ErrorKind::UnresolvedSymbol(actualName) = *actual {
226
            return mem::eq(actualName, expectedName);
227
        }
228
        return false;
229
    }
230
    if let case super::ErrorKind::RecordFieldMissing(expectedName) = expected {
231
        if let case super::ErrorKind::RecordFieldMissing(actualName) = *actual {
232
            return mem::eq(actualName, expectedName);
233
        }
234
        return false;
235
    }
236
    if let case super::ErrorKind::RecordFieldUnknown(expectedName) = expected {
237
        if let case super::ErrorKind::RecordFieldUnknown(actualName) = *actual {
238
            return mem::eq(actualName, expectedName);
239
        }
240
        return false;
241
    }
242
    if let case super::ErrorKind::ArrayFieldUnknown(expectedName) = expected {
243
        if let case super::ErrorKind::ArrayFieldUnknown(actualName) = *actual {
244
            return mem::eq(actualName, expectedName);
245
        }
246
        return false;
247
    }
248
    if let case super::ErrorKind::SliceFieldUnknown(expectedName) = expected {
249
        if let case super::ErrorKind::SliceFieldUnknown(actualName) = *actual {
250
            return mem::eq(actualName, expectedName);
251
        }
252
        return false;
253
    }
254
    if let case super::ErrorKind::UnionVariantPayloadMissing(expectedName) = expected {
255
        if let case super::ErrorKind::UnionVariantPayloadMissing(actualName) = *actual {
256
            return mem::eq(actualName, expectedName);
257
        }
258
        return false;
259
    }
260
    if let case super::ErrorKind::UnionVariantPayloadUnexpected(expectedName) = expected {
261
        if let case super::ErrorKind::UnionVariantPayloadUnexpected(actualName) = *actual {
262
            return mem::eq(actualName, expectedName);
263
        }
264
        return false;
265
    }
266
    if let case super::ErrorKind::UnionMatchNonExhaustive(expectedName) = expected {
267
        if let case super::ErrorKind::UnionMatchNonExhaustive(actualName) = *actual {
268
            return mem::eq(actualName, expectedName);
269
        }
270
        return false;
271
    }
272
    if let case super::ErrorKind::MissingTraitMethod(expectedName) = expected {
273
        if let case super::ErrorKind::MissingTraitMethod(actualName) = *actual {
274
            return mem::eq(actualName, expectedName);
275
        }
276
        return false;
277
    }
278
    if let case super::ErrorKind::MissingSupertraitInstance(expectedName) = expected {
279
        if let case super::ErrorKind::MissingSupertraitInstance(actualName) = *actual {
280
            return mem::eq(actualName, expectedName);
281
        }
282
        return false;
283
    }
284
    if let case super::ErrorKind::LinearUseAfterConsume(expectedName) = expected {
285
        if let case super::ErrorKind::LinearUseAfterConsume(actualName) = *actual {
286
            return mem::eq(actualName, expectedName);
287
        }
288
        return false;
289
    }
290
    if let case super::ErrorKind::LinearNotConsumed(expectedName) = expected {
291
        if let case super::ErrorKind::LinearNotConsumed(actualName) = *actual {
292
            return mem::eq(actualName, expectedName);
293
        }
294
        return false;
295
    }
296
    if let case super::ErrorKind::LinearBranchMismatch(expectedName) = expected {
297
        if let case super::ErrorKind::LinearBranchMismatch(actualName) = *actual {
298
            return mem::eq(actualName, expectedName);
299
        }
300
        return false;
301
    }
302
    if let case super::ErrorKind::BorrowConflict(expectedName) = expected {
303
        if let case super::ErrorKind::BorrowConflict(actualName) = *actual {
304
            return mem::eq(actualName, expectedName);
305
        }
306
        return false;
307
    }
308
    return *actual == expected;
309
}
310
311
/// Extract the first error and ensure it has the expected kind.
312
fn expectErrorKind(result: *TestResult, kind: super::ErrorKind) -> *super::Error
313
    throws (testing::TestError)
314
{
315
    let err = try expectError(result);
316
    try testing::expect(errorKindMatches(&err.kind, kind));
317
    return err;
318
}
319
320
/// Ensure an expression resolves to the expected type annotation.
321
fn expectType(self: *super::Resolver, expr: *ast::Node, expected: super::Type)
322
    throws (testing::TestError)
323
{
324
    let actual = super::typeFor(self, expr)
325
        else throw testing::TestError::Failed;
326
327
    if actual <> expected {
328
        throw testing::TestError::Failed;
329
    }
330
}
331
332
/// Verify that an error represents a specific type mismatch.
333
fn expectTypeMismatch(err: *super::Error, expected: super::Type, actual: super::Type)
334
    throws (testing::TestError)
335
{
336
    let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
337
        else throw testing::TestError::Failed;
338
    try testing::expect(mismatch.expected == expected);
339
    try testing::expect(mismatch.actual == actual);
340
}
341
342
/// Resolve a program and require successful analysis.
343
fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) {
344
    let mut a = testResolver();
345
    let result = try resolveProgramStr(&mut a, program);
346
    try expectNoErrors(&result);
347
}
348
349
/// Require a linear use-after-consume error at the later identifier use.
350
fn expectLinearUseAfterConsume(
351
    program: *[u8],
352
    name: *[u8],
353
    offset: u32,
354
) throws (testing::TestError) {
355
    let mut a = testResolver();
356
    let result = try resolveProgramStr(&mut a, program);
357
    let err = try expectErrorKind(
358
        &result,
359
        super::ErrorKind::LinearUseAfterConsume(name),
360
    );
361
    let node = err.node else throw testing::TestError::Failed;
362
    try testing::expect(node.span.offset == offset);
363
}
364
365
/// Require an inferred integer type mismatch.
366
fn expectIntMismatch(program: *[u8], expected: super::Type)
367
    throws (testing::TestError)
368
{
369
    let mut a = testResolver();
370
    let result = try resolveProgramStr(&mut a, program);
371
    let err = try expectError(&result);
372
    try expectTypeMismatch(err, expected, super::Type::Int);
373
}
374
375
/// Retrieve the nth statement from a block node.
376
fn getBlockStmt(block: *ast::Node, index: u32) -> *ast::Node
377
    throws (testing::TestError)
378
{
379
    let case ast::NodeValue::Block(body) = block.value
380
        else throw testing::TestError::Failed;
381
382
    if index >= body.statements.len {
383
        throw testing::TestError::Failed;
384
    }
385
    return body.statements[index];
386
}
387
388
/// Retrieve a declared function body by its statement index.
389
fn getFnDeclBody(root: *ast::Node, index: u32) -> ast::Block
390
    throws (testing::TestError)
391
{
392
    let stmt = try getBlockStmt(root, index);
393
    let case ast::NodeValue::FnDecl(decl) = stmt.value
394
        else throw testing::TestError::Failed;
395
    let body = decl.body
396
        else throw testing::TestError::Failed;
397
    let case ast::NodeValue::Block(block) = body.value
398
        else throw testing::TestError::Failed;
399
    return block;
400
}
401
402
/// Retrieve a function body block by function name from the program scope.
403
fn getFnBody(a: *super::Resolver, root: *ast::Node, name: *[u8]) -> ast::Block
404
    throws (testing::TestError)
405
{
406
    let scope = super::scopeFor(a, root)
407
        else throw testing::TestError::Failed;
408
    let sym = super::findSymbolInScope(scope, name)
409
        else throw testing::TestError::Failed;
410
    // Verify it's a value symbol by pattern matching.
411
    let case super::SymbolData::Value { .. } = sym.data
412
        else throw testing::TestError::Failed;
413
414
    let case ast::NodeValue::FnDecl(fnDecl) = sym.node.value
415
        else throw testing::TestError::Failed;
416
417
    let body = fnDecl.body
418
        else throw testing::TestError::Failed;
419
    let case ast::NodeValue::Block(blk) = body.value
420
        else throw testing::TestError::Failed;
421
422
    return blk;
423
}
424
425
/// Get the payload type of a union variant, if it has one.
426
/// For single-field unlabeled variants like `Variant(i32)`, unwraps to return the inner type.
427
fn getUnionVariantPayload(nominalTy: *super::NominalType, variantName: *[u8]) -> super::Type {
428
    let case super::NominalType::Union(unionType) = *nominalTy
429
        else panic "getUnionVariantPayload: not a union";
430
    for i in 0..unionType.variants.len {
431
        if mem::eq(unionType.variants[i].name, variantName) {
432
            let payloadType = unionType.variants[i].valueType;
433
            // Unwrap single-field unlabeled records to get the inner type.
434
            if let case super::Type::Nominal(super::NominalType::Record(recInfo)) = payloadType {
435
                if not recInfo.labeled and recInfo.fields.len == 1 {
436
                    return recInfo.fields[0].fieldType;
437
                }
438
            }
439
            return payloadType;
440
        }
441
    }
442
    panic "getUnionVariantPayload: variant not found";
443
}
444
445
/// Get a nominal type by name, in the scope of the given block node.
446
fn getTypeInScopeOf(a: *super::Resolver, blk: *ast::Node, name: *[u8]) -> *super::NominalType
447
    throws (testing::TestError)
448
{
449
    let scope = super::scopeFor(a, blk)
450
        else throw testing::TestError::Failed;
451
    let sym = super::findSymbolInScope(scope, name)
452
        else throw testing::TestError::Failed;
453
    let case super::SymbolData::Type(ty) = sym.data
454
        else throw testing::TestError::Failed;
455
    return ty;
456
}
457
458
/// Return the resolved type of a syntax node.
459
fn typeOf(a: *super::Resolver, node: *ast::Node) -> super::Type
460
    throws (testing::TestError)
461
{
462
    let ty = super::typeFor(a, node)
463
        else throw testing::TestError::Failed;
464
    return ty;
465
}
466
467
/// Require an array type and return its element type.
468
fn expectArrayType(ty: super::Type, length: u32) -> super::Type
469
    throws (testing::TestError)
470
{
471
    let case super::Type::Array(info) = ty
472
        else throw testing::TestError::Failed;
473
    try testing::expect(info.length == length);
474
475
    return *info.item;
476
}
477
478
/// Require a slice type and return its element type.
479
fn expectSliceType(ty: super::Type, mutable: bool) -> super::Type
480
    throws (testing::TestError)
481
{
482
    let case super::Type::Slice { item, mutable: sliceMut, .. } = ty
483
        else throw testing::TestError::Failed;
484
    try testing::expect(sliceMut == mutable);
485
486
    return *item;
487
}
488
489
/// Require a pointer type and return its target type.
490
fn expectPointerType(ty: super::Type, mutable: bool) -> super::Type
491
    throws (testing::TestError)
492
{
493
    let case super::Type::Pointer { target, mutable: ptrMut, .. } = ty
494
        else throw testing::TestError::Failed;
495
    try testing::expect(ptrMut == mutable);
496
497
    return *target;
498
}
499
500
/// Verify that a node has a constant integer value with the expected magnitude.
501
fn expectConstInt(a: *super::Resolver, node: *ast::Node, expected: u32)
502
    throws (testing::TestError)
503
{
504
    let constVal = super::constValueEntry(a, node)
505
        else throw testing::TestError::Failed;
506
507
    let case super::ConstValue::Int(int) = constVal
508
        else throw testing::TestError::Failed;
509
510
    try testing::expect(int.magnitude == expected);
511
}
512
513
/// Resolve an expression that should evaluate to a constant, and verify it equals the expected value.
514
fn resolveAndExpectConstExpr(expr: *[u8], expected: u32)
515
    throws (testing::TestError)
516
{
517
    let mut a = testResolver();
518
    let result = try resolveExprStr(&mut a, expr);
519
    try expectNoErrors(&result);
520
    try expectType(&a, result.root, super::Type::U32);
521
    try expectConstInt(&a, result.root, expected);
522
}
523
524
/// Resolve a statement that should evaluate to a constant, and verify it equals the expected value.
525
fn resolveAndExpectConstStmt(expr: *[u8], expected: u32)
526
    throws (testing::TestError)
527
{
528
    let mut a = testResolver();
529
    let result = try resolveProgramStr(&mut a, expr);
530
    try expectNoErrors(&result);
531
    let stmt = try getBlockStmt(result.root, 1);
532
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
533
    try expectConstInt(&a, expr, expected);
534
}
535
536
// Tests ///////////////////////////////////////////////////////////////////////
537
538
@test fn testResolveLit() throws (testing::TestError) {
539
    let mut a = testResolver();
540
    let result = try resolveExprStr(&mut a, "true");
541
542
    try expectNoErrors(&result);
543
    try expectType(&a, result.root, super::Type::Bool);
544
}
545
546
@test fn testResolveStringLiteralType() throws (testing::TestError) {
547
    let mut a = testResolver();
548
    let result = try resolveBlockStr(&mut a, "panic \"hello\";");
549
550
    try expectNoErrors(&result);
551
    let stmt = try getBlockStmt(result.root, 0);
552
    let case ast::NodeValue::Panic { message } = stmt.value
553
        else throw testing::TestError::Failed;
554
    let literal = message
555
        else throw testing::TestError::Failed;
556
    let ty = try typeOf(&a, literal);
557
    let elemTy = try expectSliceType(ty, false);
558
    try testing::expect(elemTy == super::Type::U8);
559
}
560
561
@test fn testResolveAsNumeric() throws (testing::TestError) {
562
    {
563
        let mut a = testResolver();
564
        let result = try resolveExprStr(&mut a, "1 as u32");
565
        try expectNoErrors(&result);
566
        try expectType(&a, result.root, super::Type::U32);
567
    } {
568
        let mut a = testResolver();
569
        let result = try resolveBlockStr(&mut a, "let x: u32 = 913; x as u8;");
570
        try expectNoErrors(&result);
571
572
        let x = try getBlockStmt(result.root, 1);
573
        try expectExprStmtType(&a, x, super::Type::U8);
574
    }
575
}
576
577
@test fn testResolveAsInvalid() throws (testing::TestError) {
578
    let mut a = testResolver();
579
    let result = try resolveProgramStr(&mut a, "true as u32");
580
581
    try expectErrorKind(
582
        &result,
583
        super::ErrorKind::InvalidAsCast(super::InvalidAsCast {
584
            from: super::Type::Bool,
585
            to: super::Type::U32,
586
        })
587
    );
588
}
589
590
@test fn testResolveAsUnionToInt() throws (testing::TestError) {
591
    let mut a = testResolver();
592
    let program = "union Color { Red } Color::Red as u32;";
593
    let result = try resolveProgramStr(&mut a, program);
594
    try expectNoErrors(&result);
595
596
    let red = try getBlockStmt(result.root, 1);
597
    try expectExprStmtType(&a, red, super::Type::U32);
598
}
599
600
@test fn testResolveBinding() throws (testing::TestError) {
601
    let mut a = testResolver();
602
    let result = try resolveBlockStr(&mut a, "let x: bool = true; x;");
603
    let stmt = try parser::tests::getBlockLastStmt(result.root);
604
605
    try expectNoErrors(&result);
606
    try expectType(&a, stmt, super::Type::Void);
607
    try expectExprStmtType(&a, stmt, super::Type::Bool);
608
609
    let case ast::NodeValue::ExprStmt(x) = stmt.value
610
        else throw testing::TestError::Failed;
611
612
    let sym = super::symbolFor(&a, x)
613
        else throw testing::TestError::Failed;
614
    let case super::SymbolData::Value { type: valType, .. } = sym.data
615
        else throw testing::TestError::Failed;
616
    try testing::expect(valType == super::Type::Bool);
617
}
618
619
@test fn testResolveBindingInvalid() throws (testing::TestError) {
620
    let mut a = testResolver();
621
    let result = try resolveBlockStr(&mut a, "let x: i32 = true;");
622
    let err = try expectError(&result);
623
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
624
}
625
626
@test fn testResolveDuplicateBinding() throws (testing::TestError) {
627
    let mut a = testResolver();
628
    let result = try resolveBlockStr(&mut a, "let x: bool = true; let x: u8 = 1;");
629
    let stmt = try parser::tests::getBlockLastStmt(result.root);
630
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("x"));
631
}
632
633
@test fn testResolveConstLiteralValue() throws (testing::TestError) {
634
    let mut a = testResolver();
635
    let program = "constant ANSWER: i32 = 42;";
636
    let result = try resolveProgramStr(&mut a, program);
637
    try expectNoErrors(&result);
638
639
    let constNode = try getBlockStmt(result.root, 0);
640
    let sym = super::symbolFor(&a, constNode)
641
        else throw testing::TestError::Failed;
642
    let case super::SymbolData::Constant { type: constType, .. } = sym.data
643
        else throw testing::TestError::Failed;
644
    try testing::expect(constType == super::Type::I32);
645
}
646
647
@test fn testResolveConstRequiresConstantExpr() throws (testing::TestError) {
648
    let mut a = testResolver();
649
    let program = "fn value() -> i32 { return 1 } fn main() { constant ANSWER: i32 = value(); }";
650
    let result = try resolveProgramStr(&mut a, program);
651
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
652
653
    let errNode = err.node
654
        else throw testing::TestError::Failed;
655
    let case ast::NodeValue::Call(_) = errNode.value
656
        else throw testing::TestError::Failed;
657
}
658
659
@test fn testResolveStaticLiteralValue() throws (testing::TestError) {
660
    let mut a = testResolver();
661
    let program = "static COUNTER: i32 = 0;";
662
    let result = try resolveProgramStr(&mut a, program);
663
    try expectNoErrors(&result);
664
665
    let staticNode = try getBlockStmt(result.root, 0);
666
    let sym = super::symbolFor(&a, staticNode)
667
        else throw testing::TestError::Failed;
668
    let case super::SymbolData::Value { type: valType, .. } = sym.data
669
        else throw testing::TestError::Failed;
670
    try testing::expect(valType == super::Type::I32);
671
}
672
673
@test fn testResolveStaticRequiresConstantExpr() throws (testing::TestError) {
674
    let mut a = testResolver();
675
    let program = "fn seed() -> i32 { return 1; } static COUNTER: i32 = seed();";
676
    let result = try resolveProgramStr(&mut a, program);
677
    let err = try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
678
679
    let errNode = err.node
680
        else throw testing::TestError::Failed;
681
    let case ast::NodeValue::Call(_) = errNode.value
682
        else throw testing::TestError::Failed;
683
}
684
685
@test fn testSymbolStoresFnAttributes() throws (testing::TestError) {
686
    let mut a = testResolver();
687
    let program = "@default export fn f() { return; }";
688
    let result = try resolveProgramStr(&mut a, program);
689
    try expectNoErrors(&result);
690
691
    let scope = super::scopeFor(&a, result.root)
692
        else throw testing::TestError::Failed;
693
    let sym = super::findSymbolInScope(scope, "f")
694
        else throw testing::TestError::Failed;
695
696
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
697
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
698
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern));
699
}
700
701
/// The canonical unsafe ecall intrinsic declaration is accepted.
702
@test fn testResolveEcallIntrinsicCanonicalSignature() throws (testing::TestError) {
703
    let mut a = testResolver();
704
    let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64;";
705
    let result = try resolveProgramStr(&mut a, program);
706
    try expectNoErrors(&result);
707
}
708
709
/// The ecall intrinsic cannot be exposed as a safe function.
710
@test fn testResolveEcallIntrinsicRequiresUnsafe() throws (testing::TestError) {
711
    let mut a = testResolver();
712
    let program = "@intrinsic fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64;";
713
    let result = try resolveProgramStr(&mut a, program);
714
    try expectErrorKind(
715
        &result,
716
        super::ErrorKind::InvalidEcallIntrinsicSignature,
717
    );
718
}
719
720
/// The ecall intrinsic declaration must use its canonical ABI.
721
@test fn testResolveEcallIntrinsicRequiresCanonicalAbi() throws (testing::TestError) {
722
    {
723
        let mut a = testResolver();
724
        let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64) -> i64;";
725
        let result = try resolveProgramStr(&mut a, program);
726
        try expectErrorKind(
727
            &result,
728
            super::ErrorKind::InvalidEcallIntrinsicSignature,
729
        );
730
    } {
731
        let mut a = testResolver();
732
        let program = "@intrinsic unsafe fn ecall(number: u32, arg1: u64, arg2: i64, arg3: i64, arg4: i64) -> i64;";
733
        let result = try resolveProgramStr(&mut a, program);
734
        try expectErrorKind(
735
            &result,
736
            super::ErrorKind::InvalidEcallIntrinsicSignature,
737
        );
738
    } {
739
        let mut a = testResolver();
740
        let program = "@intrinsic unsafe fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i32;";
741
        let result = try resolveProgramStr(&mut a, program);
742
        try expectErrorKind(
743
            &result,
744
            super::ErrorKind::InvalidEcallIntrinsicSignature,
745
        );
746
    }
747
}
748
749
@test fn testSymbolStoresRecordAttributes() throws (testing::TestError) {
750
    let mut a = testResolver();
751
    let program = "export record S { value: i32 }";
752
    let result = try resolveProgramStr(&mut a, program);
753
    try expectNoErrors(&result);
754
755
    let scope = super::scopeFor(&a, result.root)
756
        else throw testing::TestError::Failed;
757
    let sym = super::findSymbolInScope(scope, "S")
758
        else throw testing::TestError::Failed;
759
760
    try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export));
761
    try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default));
762
}
763
764
@test fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) {
765
    let mut a = testResolver();
766
    let program = "@default record T { value: i32 }";
767
    let result = try resolveProgramStr(&mut a, program);
768
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
769
}
770
771
@test fn testDefaultAttributeRejectedOnUnion() throws (testing::TestError) {
772
    let mut a = testResolver();
773
    let program = "@default union Result { Ok, Err }";
774
    let result = try resolveProgramStr(&mut a, program);
775
    try expectErrorKind(&result, super::ErrorKind::DefaultAttrOnlyOnFn);
776
}
777
778
@test fn testResolveArrayLiteralTyped() throws (testing::TestError) {
779
    let mut a = testResolver();
780
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2];");
781
    try expectNoErrors(&result);
782
783
    let stmt = try getBlockStmt(result.root, 0);
784
    let case ast::NodeValue::Let(decl) = stmt.value
785
        else throw testing::TestError::Failed;
786
    let arrayTy = try typeOf(&a, decl.value);
787
    let elemTy = try expectArrayType(arrayTy, 2);
788
    try testing::expect(elemTy == super::Type::I32);
789
}
790
791
@test fn testResolveArrayLiteralElementMismatch() throws (testing::TestError) {
792
    let mut a = testResolver();
793
    let result = try resolveProgramStr(&mut a, "let xs: [bool; 2] = [true, 1];");
794
    let err = try expectError(&result);
795
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
796
}
797
798
@test fn testResolveArrayLiteralCannotInfer() throws (testing::TestError) {
799
    let mut a = testResolver();
800
    let result = try resolveProgramStr(&mut a, "let xs = [1, 2];");
801
    try expectErrorKind(&result, super::ErrorKind::CannotInferType);
802
}
803
804
@test fn testResolveArrayLiteralOverflow() throws (testing::TestError) {
805
    let mut a = testResolver();
806
    let result = try resolveProgramStr(&mut a, "let xs: [u8; 2] = [1, 256];");
807
    let err = try expectError(&result);
808
    let case super::ErrorKind::TypeMismatch(_) = err.kind
809
        else throw testing::TestError::Failed;
810
}
811
812
@test fn testResolveArrayLiteralTooFewElements() throws (testing::TestError) {
813
    let mut a = testResolver();
814
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1];");
815
    let err = try expectError(&result);
816
    let case super::ErrorKind::TypeMismatch(_) = err.kind
817
        else throw testing::TestError::Failed;
818
}
819
820
@test fn testResolveArrayLiteralTooManyElements() throws (testing::TestError) {
821
    let mut a = testResolver();
822
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 2] = [1, 2, 3];");
823
    let err = try expectError(&result);
824
    let case super::ErrorKind::TypeMismatch(_) = err.kind
825
        else throw testing::TestError::Failed;
826
}
827
828
@test fn testResolveArrayLiteralEmptyWithAnnotation() throws (testing::TestError) {
829
    let mut a = testResolver();
830
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 0] = [];");
831
    try expectNoErrors(&result);
832
}
833
834
@test fn testResolveNestedArrayLiteralTyped() throws (testing::TestError) {
835
    let mut a = testResolver();
836
    let result = try resolveProgramStr(&mut a, "let grid: [[i32; 2]; 2] = [[1, 2], [3, 4]];");
837
    try expectNoErrors(&result);
838
839
    let stmt = try getBlockStmt(result.root, 0);
840
    let case ast::NodeValue::Let(decl) = stmt.value
841
        else throw testing::TestError::Failed;
842
    let gridTy = try typeOf(&a, decl.value);
843
    let rowTy = try expectArrayType(gridTy, 2);
844
    let elemTy = try expectArrayType(rowTy, 2);
845
    try testing::expect(elemTy == super::Type::I32);
846
}
847
848
@test fn testResolveArrayLiteralWithOptionalElems() throws (testing::TestError) {
849
    let mut a = testResolver();
850
    let result = try resolveProgramStr(&mut a, "let xs: [?i32; 2] = [1, 2];");
851
    try expectNoErrors(&result);
852
853
    let stmt = try getBlockStmt(result.root, 0);
854
    let case ast::NodeValue::Let(decl) = stmt.value
855
        else throw testing::TestError::Failed;
856
    let arrayTy = try typeOf(&a, decl.value);
857
    let elemTy = try expectArrayType(arrayTy, 2);
858
    let case super::Type::Optional(inner) = elemTy
859
        else throw testing::TestError::Failed;
860
    try testing::expect(*inner == super::Type::I32);
861
}
862
863
@test fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) {
864
    let mut a = testResolver();
865
    let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];");
866
    let err = try expectError(&result);
867
    let case super::ErrorKind::TypeMismatch(_) = err.kind
868
        else throw testing::TestError::Failed;
869
}
870
871
@test fn testResolveArrayRepeatBasic() throws (testing::TestError) {
872
    let mut a = testResolver();
873
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3];");
874
    try expectNoErrors(&result);
875
876
    let stmt = try getBlockStmt(result.root, 0);
877
    let case ast::NodeValue::Let(decl) = stmt.value
878
        else throw testing::TestError::Failed;
879
    let arrayTy = try typeOf(&a, decl.value);
880
    let elemTy = try expectArrayType(arrayTy, 3);
881
    try testing::expect(elemTy == super::Type::I32);
882
}
883
884
@test fn testResolveArrayRepeatWithExpression() throws (testing::TestError) {
885
    let mut a = testResolver();
886
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 5] = [3 + 2; 5];");
887
    try expectNoErrors(&result);
888
889
    let stmt = try getBlockStmt(result.root, 0);
890
    let case ast::NodeValue::Let(decl) = stmt.value
891
        else throw testing::TestError::Failed;
892
    let arrayTy = try typeOf(&a, decl.value);
893
    let elemTy = try expectArrayType(arrayTy, 5);
894
    try testing::expect(elemTy == super::Type::I32);
895
}
896
897
@test fn testResolveArrayRepeatLiteralArithmetic() throws (testing::TestError) {
898
    let mut a = testResolver();
899
    // `3 * 1` folds to a compile-time constant, so the repeat count is valid.
900
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 3] = [42; 3 * 1];");
901
    try expectNoErrors(&result);
902
}
903
904
@test fn testResolveArrayRepeatNonConstCount() throws (testing::TestError) {
905
    let mut a = testResolver();
906
    // A function call is not a constant expression.
907
    let result = try resolveProgramStr(&mut a, "fn f() -> u32 { return 3; } let xs: [i32; 3] = [42; f()];");
908
    try expectErrorKind(&result, super::ErrorKind::ConstExprRequired);
909
}
910
911
@test fn testResolveArrayRepeatCountMismatch() throws (testing::TestError) {
912
    let mut a = testResolver();
913
    let result = try resolveProgramStr(&mut a, "let xs: [i32; 4] = [1; 3];");
914
    let err = try expectError(&result);
915
    let case super::ErrorKind::TypeMismatch(_) = err.kind
916
        else throw testing::TestError::Failed;
917
}
918
919
@test fn testResolveArrayIndex() throws (testing::TestError) {
920
    let mut a = testResolver();
921
    let program = "let xs: [i32; 3] = [1, 2, 3]; xs[1];";
922
    let result = try resolveProgramStr(&mut a, program);
923
    try expectNoErrors(&result);
924
925
    let stmt = try getBlockStmt(result.root, 1);
926
    try expectExprStmtType(&a, stmt, super::Type::I32);
927
}
928
929
@test fn testResolveSliceIndex() throws (testing::TestError) {
930
    let mut a = testResolver();
931
    let program = "fn run(slice: &[i32]) { slice[1]; }";
932
    let result = try resolveProgramStr(&mut a, program);
933
    try expectNoErrors(&result);
934
935
    let body = try getFnDeclBody(result.root, 0);
936
    try testing::expect(body.statements.len == 1);
937
    try expectExprStmtType(&a, body.statements[0], super::Type::I32);
938
}
939
940
@test fn testResolveSliceFields() throws (testing::TestError) {
941
    let mut a = testResolver();
942
    let program = "fn run(slice: &[i32]) { slice.len; slice.ptr; }";
943
    let result = try resolveProgramStr(&mut a, program);
944
    try expectNoErrors(&result);
945
946
    let body = try getFnDeclBody(result.root, 0);
947
    try testing::expect(body.statements.len == 2);
948
    let case ast::NodeValue::ExprStmt(lenExpr) = body.statements[0].value
949
        else throw testing::TestError::Failed;
950
    let lenTy = try typeOf(&a, lenExpr);
951
    try testing::expect(lenTy == super::Type::U32);
952
953
    let case ast::NodeValue::ExprStmt(ptrExpr) = body.statements[1].value
954
        else throw testing::TestError::Failed;
955
    let ptrTy = try typeOf(&a, ptrExpr);
956
    let case super::Type::Pointer { class, target, mutable } = ptrTy
957
        else throw testing::TestError::Failed;
958
    try testing::expect(class == types::PointerClass::Ref);
959
    try testing::expect(not mutable);
960
    try testing::expect(*target == super::Type::I32);
961
}
962
963
@test fn testResolveSliceLiteralImmutable() throws (testing::TestError) {
964
    let mut a = testResolver();
965
    let program = "unsafe fn run() { let slice: *[i32] = &[1, 2, 3]; }";
966
    let result = try resolveProgramStr(&mut a, program);
967
    try expectNoErrors(&result);
968
}
969
970
/// Empty array literal infers element type from slice annotation.
971
@test fn testResolveSliceLiteralEmpty() throws (testing::TestError) {
972
    let mut a = testResolver();
973
    let program = "unsafe fn run() { let slice: *[i32] = &[]; }";
974
    let result = try resolveProgramStr(&mut a, program);
975
    try expectNoErrors(&result);
976
}
977
978
/// Nested array literal should infer inner element type from slice annotation.
979
@test fn testResolveSliceLiteralNestedArray() throws (testing::TestError) {
980
    let mut a = testResolver();
981
    let program = "unsafe fn run() { let slice: *[[i32; 2]] = &[[1, 2], [3, 4]]; }";
982
    let result = try resolveProgramStr(&mut a, program);
983
    try expectNoErrors(&result);
984
}
985
986
@test fn testResolveSliceFromArray() throws (testing::TestError) {
987
    {
988
        let mut a = testResolver();
989
        let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..]; }";
990
        let result = try resolveProgramStr(&mut a, program);
991
        try expectNoErrors(&result);
992
    } {
993
        let mut a = testResolver();
994
        let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[0..3]; }";
995
        let result = try resolveProgramStr(&mut a, program);
996
        try expectNoErrors(&result);
997
    } {
998
        let mut a = testResolver();
999
        let program = "unsafe fn run() { let xs: [i32; 3] = [1, 2, 3]; let slice: *[i32] = &xs[..3]; }";
1000
        let result = try resolveProgramStr(&mut a, program);
1001
        try expectNoErrors(&result);
1002
    } {
1003
        let mut a = testResolver();
1004
        let program = "unsafe fn run() { let xs: [u8; 2] = [1, 2]; let slice = &xs[1..1]; }";
1005
        let result = try resolveProgramStr(&mut a, program);
1006
        try expectNoErrors(&result);
1007
    }
1008
}
1009
1010
@test fn testResolveSliceLiteralMutableRequiresMut() throws (testing::TestError) {
1011
    let mut a = testResolver();
1012
    let program = "let slice: *mut [i32] = &[1, 2, 3];";
1013
    let result = try resolveProgramStr(&mut a, program);
1014
    let err = try expectError(&result);
1015
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1016
        else throw testing::TestError::Failed;
1017
}
1018
1019
@test fn testResolveSliceLiteralMutable() throws (testing::TestError) {
1020
    let mut a = testResolver();
1021
    let program = "unsafe fn run() { let slice: *mut [i32] = &mut [1, 2, 3]; }";
1022
    let result = try resolveProgramStr(&mut a, program);
1023
    try expectNoErrors(&result);
1024
}
1025
1026
@test fn testResolvePointerMutableAssignmentRequiresMut() throws (testing::TestError) {
1027
    let mut a = testResolver();
1028
    let program = "let x: i32 = 0; let ptr: *mut i32 = &x;";
1029
    let result = try resolveProgramStr(&mut a, program);
1030
    let err = try expectError(&result);
1031
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1032
        else throw testing::TestError::Failed;
1033
}
1034
1035
@test fn testResolvePointerMutableToImmutableAssignment() throws (testing::TestError) {
1036
    let mut a = testResolver();
1037
    let program = "fn f(mptr: *mut i32) -> *i32 { return mptr; }";
1038
    let result = try resolveProgramStr(&mut a, program);
1039
    try expectNoErrors(&result);
1040
}
1041
1042
@test fn testResolveAddressOfRequiresMutableBinding() throws (testing::TestError) {
1043
    {
1044
        let mut a = testResolver();
1045
        let program = "fn borrow(ptr: &mut i32) {} fn run() { let x: i32 = 0; borrow(&mut x); }";
1046
        let result = try resolveProgramStr(&mut a, program);
1047
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1048
    } {
1049
        let mut a = testResolver();
1050
        let program = "fn borrow(ptr: &mut i32) {} fn run() { let mut x: i32 = 0; borrow(&mut x); }";
1051
        let result = try resolveProgramStr(&mut a, program);
1052
        try expectNoErrors(&result);
1053
    }
1054
}
1055
1056
@test fn testResolveAddressOfSliceRequiresMutableBinding() throws (testing::TestError) {
1057
    {
1058
        let mut a = testResolver();
1059
        let program = "fn borrow(slice: &mut [i32]) {} fn run() { let xs: [i32; 3] = [1, 2, 3]; borrow(&mut xs[..]); }";
1060
        let result = try resolveProgramStr(&mut a, program);
1061
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1062
    } {
1063
        let mut a = testResolver();
1064
        let program = "fn borrow(slice: &mut [i32]) {} fn run() { let mut xs: [i32; 3] = [1, 2, 3]; borrow(&mut xs[..]); }";
1065
        let result = try resolveProgramStr(&mut a, program);
1066
        try expectNoErrors(&result);
1067
    }
1068
}
1069
1070
@test fn testResolveSliceCannotAssignToArray() throws (testing::TestError) {
1071
    let mut a = testResolver();
1072
    let program = "let xs: *[u8] = &[1, 2]; let ys: [u8; 2] = xs;";
1073
    let result = try resolveProgramStr(&mut a, program);
1074
    let err = try expectError(&result);
1075
    let case super::ErrorKind::TypeMismatch(_) = err.kind
1076
        else throw testing::TestError::Failed;
1077
}
1078
1079
@test fn testResolveSliceSyntaxRequiresAddressOf() throws (testing::TestError) {
1080
    let mut a = testResolver();
1081
    let program = "let xs: [u8; 2] = [1, 2]; xs[..];";
1082
    let result = try resolveProgramStr(&mut a, program);
1083
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1084
}
1085
1086
@test fn testResolveSliceResliceRequiresAddressOf() throws (testing::TestError) {
1087
    let mut a = testResolver();
1088
    let program = "fn f(s: *[u8]) -> *[u8] { return s[..]; }";
1089
    let result = try resolveProgramStr(&mut a, program);
1090
    try expectErrorKind(&result, super::ErrorKind::SliceRequiresAddress);
1091
}
1092
1093
@test fn testResolveSliceRangeOutOfBounds() throws (testing::TestError) {
1094
    {
1095
        let mut a = testResolver();
1096
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[..3];";
1097
        let result = try resolveProgramStr(&mut a, program);
1098
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1099
    } {
1100
        let mut a = testResolver();
1101
        let program = "let xs: [u8; 2] = [1, 2]; let slice = &xs[3..];";
1102
        let result = try resolveProgramStr(&mut a, program);
1103
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1104
    } {
1105
        let mut a = testResolver();
1106
        let program = "let xs: [u8; 4] = [1, 2, 3, 4]; let slice = &xs[3..2];";
1107
        let result = try resolveProgramStr(&mut a, program);
1108
        try expectErrorKind(&result, super::ErrorKind::SliceRangeOutOfBounds);
1109
    }
1110
}
1111
1112
@test fn testResolveArrayLenConstValue() throws (testing::TestError) {
1113
    let mut a = testResolver();
1114
    let program = "let xs: [i32; 3] = [1, 2, 3]; constant LEN: u32 = xs.len;";
1115
    let result = try resolveBlockStr(&mut a, program);
1116
    try expectNoErrors(&result);
1117
1118
    let constStmt = try getBlockStmt(result.root, 1);
1119
    let case ast::NodeValue::ConstDecl(decl) = constStmt.value
1120
        else throw testing::TestError::Failed;
1121
    let valueConst = super::constValueEntry(&a, decl.value)
1122
        else throw testing::TestError::Failed;
1123
    let case super::ConstValue::Int(lenVal) = valueConst
1124
        else throw testing::TestError::Failed;
1125
    try testing::expect(lenVal.magnitude == 3);
1126
    try testing::expect(not lenVal.negative);
1127
}
1128
1129
@test fn testResolveIndexNonIndexable() throws (testing::TestError) {
1130
    let mut a = testResolver();
1131
    let program = "let flag: bool = true; flag[0];";
1132
    let result = try resolveProgramStr(&mut a, program);
1133
    try expectErrorKind(&result, super::ErrorKind::ExpectedIndexable);
1134
}
1135
1136
@test fn testResolveSliceFieldUnknown() throws (testing::TestError) {
1137
    let mut a = testResolver();
1138
    let program = "let xs: [i32; 2] = [1, 2]; (&xs[0..]).unknown;";
1139
    let result = try resolveProgramStr(&mut a, program);
1140
    try expectErrorKind(&result, super::ErrorKind::SliceFieldUnknown("unknown"));
1141
}
1142
1143
@test fn testResolveArrayFieldUnknown() throws (testing::TestError) {
1144
    let mut a = testResolver();
1145
    let program = "let xs: [i32; 2] = [1, 2]; xs.field;";
1146
    let result = try resolveProgramStr(&mut a, program);
1147
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
1148
}
1149
1150
@test fn testResolveIfConditionRequiresBool() throws (testing::TestError) {
1151
    {
1152
        let mut a = testResolver();
1153
        let result = try resolveProgramStr(&mut a, "if 42 {}");
1154
        let err = try expectError(&result);
1155
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1156
    } {
1157
        let mut a = testResolver();
1158
        let result = try resolveProgramStr(&mut a, "if true {}");
1159
        try expectNoErrors(&result);
1160
    }
1161
}
1162
1163
@test fn testResolveIfLetScopeBinding() throws (testing::TestError) {
1164
    let mut a = testResolver();
1165
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x }");
1166
    try expectNoErrors(&result);
1167
1168
    // Get the if-let statement and verify `x` has type `i32`.
1169
    let ifLetStmt = try parser::tests::getBlockLastStmt(result.root);
1170
    let case ast::NodeValue::IfLet(ifLet) = ifLetStmt.value
1171
        else throw testing::TestError::Failed;
1172
1173
    let thenStmt = try parser::tests::getBlockLastStmt(ifLet.thenBranch);
1174
    let case ast::NodeValue::ExprStmt(xExpr) = thenStmt.value
1175
        else throw testing::TestError::Failed;
1176
1177
    try expectType(&a, xExpr, super::Type::I32);
1178
1179
    let scope = super::scopeFor(&a, ifLetStmt)
1180
        else throw testing::TestError::Failed;
1181
    let xSym = super::findSymbolInScope(scope, "x")
1182
        else throw testing::TestError::Failed;
1183
    let case super::SymbolData::Value { type: valType, .. } = xSym.data
1184
        else throw testing::TestError::Failed;
1185
1186
    try testing::expect(valType == super::Type::I32);
1187
}
1188
1189
@test fn testResolveIfLetScopeBindingError() throws (testing::TestError) {
1190
    let mut a = testResolver();
1191
    let result = try resolveProgramStr(&mut a, "let opt: ?i32 = 42; if let x = opt { x } else { x }");
1192
    let err = try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1193
1194
    // Verify the error comes from the else branch (offset 48).
1195
    let errNode = err.node
1196
        else throw testing::TestError::Failed;
1197
    try testing::expect(errNode.span.offset == 48);
1198
}
1199
1200
/// Tests that `if let` with a condition expression binds the variable in scope.
1201
@test fn testResolveIfLetConditionBindsVariable() throws (testing::TestError) {
1202
    let mut a = testResolver();
1203
    let program = "let opt: ?i32 = 42; if let x = opt; x == 1 { x }";
1204
    let result = try resolveProgramStr(&mut a, program);
1205
    try expectNoErrors(&result);
1206
}
1207
1208
@test fn testResolveWhileConditionRequiresBool() throws (testing::TestError) {
1209
    {
1210
        let mut a = testResolver();
1211
        let result = try resolveProgramStr(&mut a, "while 1 {}");
1212
        let err = try expectError(&result);
1213
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1214
    } {
1215
        let mut a = testResolver();
1216
        let result = try resolveProgramStr(&mut a, "while true {}");
1217
        try expectNoErrors(&result);
1218
    }
1219
}
1220
1221
@test fn testResolveWhileLetBindingScope() throws (testing::TestError) {
1222
    {
1223
        let mut a = testResolver();
1224
        let program = "let mut opt: ?i32 = 42; while let x = opt; x > 0 { x; opt; }";
1225
        let result = try resolveProgramStr(&mut a, program);
1226
        try expectNoErrors(&result);
1227
1228
        let whileStmt = try parser::tests::getBlockLastStmt(result.root);
1229
        let case ast::NodeValue::WhileLet(loopNode) = whileStmt.value
1230
            else throw testing::TestError::Failed;
1231
1232
        let bodyStmt = try parser::tests::getBlockFirstStmt(loopNode.body);
1233
        try expectExprStmtType(&a, bodyStmt, super::Type::I32);
1234
1235
        let scope = super::scopeFor(&a, whileStmt)
1236
            else throw testing::TestError::Failed;
1237
        let xSym = super::findSymbolInScope(scope, "x")
1238
            else throw testing::TestError::Failed;
1239
        let case super::SymbolData::Value { type: valType, .. } = xSym.data
1240
            else throw testing::TestError::Failed;
1241
        try testing::expect(valType == super::Type::I32);
1242
    } {
1243
        let mut a = testResolver();
1244
        let program = "let opt: ?i32 = nil; while let x = opt; true { break } else { x }";
1245
        let result = try resolveProgramStr(&mut a, program);
1246
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1247
    }
1248
}
1249
1250
@test fn testResolveForArrayBindsElementType() throws (testing::TestError) {
1251
    let mut a = testResolver();
1252
    let program = "let xs: [i32; 2] = [1, 2]; for x in xs { x; }";
1253
    let result = try resolveProgramStr(&mut a, program);
1254
    try expectNoErrors(&result);
1255
1256
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1257
    let case ast::NodeValue::For(loopNode) = forStmt.value
1258
        else throw testing::TestError::Failed;
1259
1260
    let scope = super::scopeFor(&a, forStmt)
1261
        else throw testing::TestError::Failed;
1262
    let sym = super::findSymbolInScope(scope, "x")
1263
        else throw testing::TestError::Failed;
1264
    let case super::SymbolData::Value { type: valType, .. } = sym.data
1265
        else throw testing::TestError::Failed;
1266
    try testing::expect(valType == super::Type::I32);
1267
1268
    let bindingTy = super::typeFor(&a, loopNode.binding)
1269
        else throw testing::TestError::Failed;
1270
    try testing::expect(bindingTy == super::Type::I32);
1271
}
1272
1273
@test fn testResolveForIndexedLoopBindsIndex() throws (testing::TestError) {
1274
    let mut a = testResolver();
1275
    let program = "let xs: [bool; 3] = [true; 3]; for value, idx in xs { value; idx; }";
1276
    let result = try resolveProgramStr(&mut a, program);
1277
    try expectNoErrors(&result);
1278
1279
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1280
    let case ast::NodeValue::For(loopNode) = forStmt.value
1281
        else throw testing::TestError::Failed;
1282
1283
    let scope = super::scopeFor(&a, forStmt)
1284
        else throw testing::TestError::Failed;
1285
    let valueSym = super::findSymbolInScope(scope, "value")
1286
        else throw testing::TestError::Failed;
1287
    let case super::SymbolData::Value { type: valueValType, .. } = valueSym.data
1288
        else throw testing::TestError::Failed;
1289
    try testing::expect(valueValType == super::Type::Bool);
1290
    let indexSym = super::findSymbolInScope(scope, "idx")
1291
        else throw testing::TestError::Failed;
1292
    let case super::SymbolData::Value { type: indexValType, .. } = indexSym.data
1293
        else throw testing::TestError::Failed;
1294
    try testing::expect(indexValType == super::Type::U32);
1295
1296
    let indexNode = loopNode.index
1297
        else throw testing::TestError::Failed;
1298
    let indexTy = super::typeFor(&a, indexNode)
1299
        else throw testing::TestError::Failed;
1300
    try testing::expect(indexTy == super::Type::U32);
1301
}
1302
1303
@test fn testResolveForSliceIterable() throws (testing::TestError) {
1304
    let mut a = testResolver();
1305
    let program = "let xs: [i32; 3] = [1, 2, 3]; for x in &xs[..] { x; }";
1306
    let result = try resolveProgramStr(&mut a, program);
1307
    try expectNoErrors(&result);
1308
1309
    let forStmt = try parser::tests::getBlockLastStmt(result.root);
1310
    let case ast::NodeValue::For(loopNode) = forStmt.value
1311
        else throw testing::TestError::Failed;
1312
1313
    let bindingTy = super::typeFor(&a, loopNode.binding)
1314
        else throw testing::TestError::Failed;
1315
    try testing::expect(bindingTy == super::Type::I32);
1316
}
1317
1318
@test fn testResolveForRequiresIterable() throws (testing::TestError) {
1319
    let mut a = testResolver();
1320
    let result = try resolveProgramStr(&mut a, "for x in true { x; }");
1321
    try expectErrorKind(&result, super::ErrorKind::ExpectedIterable);
1322
}
1323
1324
@test fn testResolveForRangeBoundsMustNumeric() throws (testing::TestError) {
1325
    let mut a = testResolver();
1326
    let result = try resolveBlockStr(&mut a, "for i in 0..true { i; }");
1327
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
1328
}
1329
1330
@test fn testResolveMatchPatternTypeMismatch() throws (testing::TestError) {
1331
    let mut a = testResolver();
1332
    let program = "let val: i32 = 0; match val { case true => {} }";
1333
    let result = try resolveProgramStr(&mut a, program);
1334
    let err = try expectError(&result);
1335
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1336
}
1337
1338
@test fn testResolveMatchUnionVariantTypeMismatch() throws (testing::TestError) {
1339
    let mut a = testResolver();
1340
    let program = "union First { A }  union Second { B } fn run(val: First) { match val { case Second::B => {} } }";
1341
    let result = try resolveProgramStr(&mut a, program);
1342
    let err = try expectError(&result);
1343
1344
    let firstTy = try getTypeInScopeOf(&a, result.root, "First");
1345
    let secondTy = try getTypeInScopeOf(&a, result.root, "Second");
1346
    try expectTypeMismatch(err, super::Type::Nominal(firstTy), super::Type::Nominal(secondTy));
1347
}
1348
1349
@test fn testResolveMatchUnionPayloadMissing() throws (testing::TestError) {
1350
    let mut a = testResolver();
1351
    let program = "union Opt { Some(i32) } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1352
    let result = try resolveProgramStr(&mut a, program);
1353
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadMissing("Some"));
1354
}
1355
1356
@test fn testResolveMatchUnionVoidVariantExplicitDiscriminant() throws (testing::TestError) {
1357
    let mut a = testResolver();
1358
    let program = "union Opt { Some = 5 } fn run(val: Opt) { match val { case Opt::Some => {} } }";
1359
    let result = try resolveProgramStr(&mut a, program);
1360
    try expectNoErrors(&result);
1361
}
1362
1363
@test fn testResolveMatchUnionPayloadUnexpected() throws (testing::TestError) {
1364
    let mut a = testResolver();
1365
    let program = "union Opt { None } fn run(val: Opt) { match val { case Opt::None(x) => {} } }";
1366
    let result = try resolveProgramStr(&mut a, program);
1367
    try expectErrorKind(&result, super::ErrorKind::UnionVariantPayloadUnexpected("None"));
1368
}
1369
1370
@test fn testResolveMatchUnionUnknownVariant() throws (testing::TestError) {
1371
    let mut a = testResolver();
1372
    let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Unknown => {} } }";
1373
    let result = try resolveProgramStr(&mut a, program);
1374
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
1375
}
1376
1377
@test fn testResolveMatchUnionNonExhaustive() throws (testing::TestError) {
1378
    {
1379
        let mut a = testResolver();
1380
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { case Opt::Some => {} } }";
1381
        let result = try resolveProgramStr(&mut a, program);
1382
        try expectErrorKind(&result, super::ErrorKind::UnionMatchNonExhaustive("None"));
1383
    } {
1384
        let mut a = testResolver();
1385
        let program = "union Opt { Some, None } fn run(value: Opt) { match value { else => {} } }";
1386
        let result = try resolveProgramStr(&mut a, program);
1387
        try expectNoErrors(&result);
1388
    }
1389
}
1390
1391
@test fn testResolveMatchUnionNonExhaustiveExplicitDiscriminants() throws (testing::TestError) {
1392
    let mut a = testResolver();
1393
    let program = "union U { A = 3, B = 9 } fn run(value: U) { match value { case U::A => {}, case U::B => {} } }";
1394
    let result = try resolveProgramStr(&mut a, program);
1395
    try expectNoErrors(&result);
1396
}
1397
1398
@test fn testResolveMatchUnionBindingScope() throws (testing::TestError) {
1399
    let mut a = testResolver();
1400
    let program = "union Opt { Some(i32), None } fn f(value: Opt) { match value { case Opt::Some(x) if x > 0 => { x; } else => {} } }";
1401
    let result = try resolveProgramStr(&mut a, program);
1402
    try expectNoErrors(&result);
1403
1404
    let fnBlock = try getFnBody(&a, result.root, "f");
1405
    try testing::expect(fnBlock.statements.len > 0);
1406
1407
    let matchNode = fnBlock.statements[0];
1408
    let case ast::NodeValue::Match(sw) = matchNode.value
1409
        else throw testing::TestError::Failed;
1410
    let caseNode = sw.prongs[0];
1411
1412
    let scope = super::scopeFor(&a, caseNode)
1413
        else throw testing::TestError::Failed;
1414
    let payloadSym = super::findSymbolInScope(scope, "x")
1415
        else throw testing::TestError::Failed;
1416
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
1417
        else throw testing::TestError::Failed;
1418
    try testing::expect(payloadValType == super::Type::I32);
1419
}
1420
1421
@test fn testResolveMatchUnionPatternNonUnionType() throws (testing::TestError) {
1422
    let mut a = testResolver();
1423
    let program = "union Opt { Some, None } fn f(value: Opt) { match value { case true => {} } }";
1424
    let result = try resolveProgramStr(&mut a, program);
1425
    let err = try expectError(&result);
1426
    let optionTy = try getTypeInScopeOf(&a, result.root, "Opt");
1427
    try expectTypeMismatch(err, super::Type::Nominal(optionTy), super::Type::Bool);
1428
}
1429
1430
@test fn testResolveMatchGuardForms() throws (testing::TestError) {
1431
    let mut a = testResolver();
1432
    let program = "fn first(value: i32) { match value { case _ if true => {}, else => {} } }";
1433
    let result = try resolveProgramStr(&mut a, program);
1434
    try expectNoErrors(&result);
1435
}
1436
1437
/// Test that a binding prong binds the subject to the identifier.
1438
@test fn testResolveMatchBindingProng() throws (testing::TestError) {
1439
    let mut a = testResolver();
1440
    let program = "fn f(value: i32) -> i32 { match value { x => return x } }";
1441
    let result = try resolveProgramStr(&mut a, program);
1442
    try expectNoErrors(&result);
1443
}
1444
1445
/// Test that a binding prong with guard can use the bound variable.
1446
@test fn testResolveMatchBindingProngGuard() throws (testing::TestError) {
1447
    let mut a = testResolver();
1448
    let program = "fn f(value: i32) -> i32 { match value { x if x > 0 => return x, _ => return 0 } }";
1449
    let result = try resolveProgramStr(&mut a, program);
1450
    try expectNoErrors(&result);
1451
}
1452
1453
/// Test that a binding prong covers all union variants for exhaustiveness.
1454
@test fn testResolveMatchBindingProngExhaustive() throws (testing::TestError) {
1455
    let mut a = testResolver();
1456
    let program = "union U { A, B, C } fn f(u: U) -> i32 { match u { x => return 0 } }";
1457
    let result = try resolveProgramStr(&mut a, program);
1458
    try expectNoErrors(&result);
1459
}
1460
1461
/// Test that `case x =>` fails if `x` is not in scope, since bare identifiers
1462
/// in case patterns are values to compare against, not bindings.
1463
@test fn testResolveMatchCaseUndefinedIdent() throws (testing::TestError) {
1464
    let mut a = testResolver();
1465
    let program = "fn f(n: i32) -> i32 { match n { case x => return 0 } }";
1466
    let result = try resolveProgramStr(&mut a, program);
1467
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1468
}
1469
1470
/// Test matching on optionals: exhaustiveness and type unwrapping.
1471
@test fn testResolveMatchOptional() throws (testing::TestError) {
1472
    {
1473
        // Exhaustive: binding + nil case.
1474
        let mut a = testResolver();
1475
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {} } }";
1476
        let result = try resolveProgramStr(&mut a, program);
1477
        try expectNoErrors(&result);
1478
    } {
1479
        // Missing nil case.
1480
        let mut a = testResolver();
1481
        let program = "fn f(opt: ?i32) { match opt { v => {} } }";
1482
        let result = try resolveProgramStr(&mut a, program);
1483
        try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingNil);
1484
    } {
1485
        // Missing value case.
1486
        let mut a = testResolver();
1487
        let program = "fn f(opt: ?i32) { match opt { case nil => {} } }";
1488
        let result = try resolveProgramStr(&mut a, program);
1489
        try expectErrorKind(&result, super::ErrorKind::OptionalMatchMissingValue);
1490
    } {
1491
        // Else covers both cases.
1492
        let mut a = testResolver();
1493
        let program = "fn f(opt: ?i32) { match opt { else => {} } }";
1494
        let result = try resolveProgramStr(&mut a, program);
1495
        try expectNoErrors(&result);
1496
    } {
1497
        // Binding unwraps the inner type.
1498
        let mut a = testResolver();
1499
        let program = "fn f(opt: ?i32) -> i32 { match opt { v => return v + 1, case nil => return 0 } }";
1500
        let result = try resolveProgramStr(&mut a, program);
1501
        try expectNoErrors(&result);
1502
    }
1503
}
1504
1505
/// Test that match on non-union types requires exhaustiveness.
1506
@test fn testResolveMatchGenericExhaustive() throws (testing::TestError) {
1507
    {
1508
        // Match on i32 without catch-all should error.
1509
        let mut a = testResolver();
1510
        let program = "fn f(x: i32) { match x { case 1 => {} } }";
1511
        let result = try resolveProgramStr(&mut a, program);
1512
        try expectErrorKind(&result, super::ErrorKind::MatchNonExhaustive);
1513
    } {
1514
        // Match on i32 with else is fine.
1515
        let mut a = testResolver();
1516
        let program = "fn f(x: i32) { match x { case 1 => {}, else => {} } }";
1517
        let result = try resolveProgramStr(&mut a, program);
1518
        try expectNoErrors(&result);
1519
    } {
1520
        // Match on i32 with binding catch-all is fine.
1521
        let mut a = testResolver();
1522
        let program = "fn f(x: i32) { match x { y => {} } }";
1523
        let result = try resolveProgramStr(&mut a, program);
1524
        try expectNoErrors(&result);
1525
    } {
1526
        // Match on i32 with wildcard catch-all is fine.
1527
        let mut a = testResolver();
1528
        let program = "fn f(x: i32) { match x { case _ => {} } }";
1529
        let result = try resolveProgramStr(&mut a, program);
1530
        try expectNoErrors(&result);
1531
    }
1532
}
1533
1534
/// Test that match on bool requires both true and false cases.
1535
@test fn testResolveMatchBoolExhaustive() throws (testing::TestError) {
1536
    {
1537
        // Match on bool with both cases is fine.
1538
        let mut a = testResolver();
1539
        let program = "fn f(x: bool) { match x { case true => {}, case false => {} } }";
1540
        let result = try resolveProgramStr(&mut a, program);
1541
        try expectNoErrors(&result);
1542
    } {
1543
        // Match on bool missing true should error.
1544
        let mut a = testResolver();
1545
        let program = "fn f(x: bool) { match x { case false => {} } }";
1546
        let result = try resolveProgramStr(&mut a, program);
1547
        try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(true));
1548
    } {
1549
        // Match on bool missing false should error.
1550
        let mut a = testResolver();
1551
        let program = "fn f(x: bool) { match x { case true => {} } }";
1552
        let result = try resolveProgramStr(&mut a, program);
1553
        try expectErrorKind(&result, super::ErrorKind::BoolMatchMissing(false));
1554
    } {
1555
        // Match on bool with else is fine.
1556
        let mut a = testResolver();
1557
        let program = "fn f(x: bool) { match x { else => {} } }";
1558
        let result = try resolveProgramStr(&mut a, program);
1559
        try expectNoErrors(&result);
1560
    } {
1561
        // Match on bool with binding catch-all is fine.
1562
        let mut a = testResolver();
1563
        let program = "fn f(x: bool) { match x { b => {} } }";
1564
        let result = try resolveProgramStr(&mut a, program);
1565
        try expectNoErrors(&result);
1566
    }
1567
}
1568
1569
@test fn testResolveBreakRequiresLoop() throws (testing::TestError) {
1570
    {
1571
        let mut a = testResolver();
1572
        let result = try resolveProgramStr(&mut a, "break;");
1573
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1574
    } {
1575
        let mut a = testResolver();
1576
        let result = try resolveProgramStr(&mut a, "loop { break }");
1577
        try expectNoErrors(&result);
1578
    }
1579
}
1580
1581
@test fn testResolveContinueRequiresLoop() throws (testing::TestError) {
1582
    {
1583
        let mut a = testResolver();
1584
        let result = try resolveProgramStr(&mut a, "continue;");
1585
        try expectErrorKind(&result, super::ErrorKind::InvalidLoopControl);
1586
    } {
1587
        let mut a = testResolver();
1588
        let result = try resolveProgramStr(&mut a, "while true { continue }");
1589
        try expectNoErrors(&result);
1590
    }
1591
}
1592
1593
@test fn testResolveFnTypeVoidNoParams() throws (testing::TestError) {
1594
    let mut a = testResolver();
1595
    let result = try resolveProgramStr(&mut a, "fn f() {} f();");
1596
    try expectNoErrors(&result);
1597
1598
    let blockNode = result.root;
1599
    let case ast::NodeValue::Block(block) = blockNode.value
1600
        else throw testing::TestError::Failed;
1601
    let fnNode = try getBlockStmt(blockNode, 0);
1602
    let callStmt = try getBlockStmt(blockNode, 1);
1603
1604
    { // Verify the function symbol captures an empty parameter list and void return.
1605
        let sym = super::symbolFor(&a, fnNode)
1606
            else throw testing::TestError::Failed;
1607
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1608
            else throw testing::TestError::Failed;
1609
        try testing::expect(fnTy.paramTypes.len == 0);
1610
        try testing::expect(*fnTy.returnType == super::Type::Void);
1611
    }
1612
    { // Checking that the type of the call matches the function return type.
1613
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1614
1615
        let fnSym = super::symbolFor(&a, fnNode)
1616
            else throw testing::TestError::Failed;
1617
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1618
            else throw testing::TestError::Failed;
1619
        try expectType(&a, callExpr, *fnTy.returnType);
1620
    }
1621
}
1622
1623
@test fn testResolveFnTypeReturnsValue() throws (testing::TestError) {
1624
    let mut a = testResolver();
1625
    let program = "fn f() -> i32 { return 1; } f();";
1626
    let result = try resolveProgramStr(&mut a, program);
1627
    try expectNoErrors(&result);
1628
1629
    let blockNode = result.root;
1630
    let case ast::NodeValue::Block(block) = blockNode.value
1631
        else throw testing::TestError::Failed;
1632
    let fnNode = try getBlockStmt(blockNode, 0);
1633
    let callStmt = try getBlockStmt(blockNode, 1);
1634
1635
    { // Function returns i32 with no parameters.
1636
        let sym = super::symbolFor(&a, fnNode)
1637
            else throw testing::TestError::Failed;
1638
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1639
            else throw testing::TestError::Failed;
1640
        try testing::expect(fnTy.paramTypes.len == 0);
1641
        try testing::expect(*fnTy.returnType == super::Type::I32);
1642
    }
1643
    { // Call expression should inherit the function's return type.
1644
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32);
1645
1646
        let fnSym = super::symbolFor(&a, fnNode)
1647
            else throw testing::TestError::Failed;
1648
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1649
            else throw testing::TestError::Failed;
1650
        try expectType(&a, callExpr, *fnTy.returnType);
1651
    }
1652
}
1653
1654
@test fn testResolveFnTypeSingleParam() throws (testing::TestError) {
1655
    let mut a = testResolver();
1656
    let program = "fn f(x: i8) {} let x: i8 = 1; f(x);";
1657
    let result = try resolveProgramStr(&mut a, program);
1658
    try expectNoErrors(&result);
1659
1660
    let blockNode = result.root;
1661
    let case ast::NodeValue::Block(block) = blockNode.value
1662
        else throw testing::TestError::Failed;
1663
    let fnNode = try getBlockStmt(blockNode, 0);
1664
    let callStmt = try getBlockStmt(blockNode, 2);
1665
1666
    { // Single parameter propagates nominal type onto the symbol and parameter node.
1667
        let sym = super::symbolFor(&a, fnNode)
1668
            else throw testing::TestError::Failed;
1669
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1670
            else throw testing::TestError::Failed;
1671
        try testing::expect(fnTy.paramTypes.len == 1);
1672
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1673
        try testing::expect(*fnTy.returnType == super::Type::Void);
1674
1675
        let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
1676
            else throw testing::TestError::Failed;
1677
        try testing::expect(fnDecl.sig.params.len == 1);
1678
1679
        let paramNode = fnDecl.sig.params[0];
1680
        try expectType(&a, paramNode, super::Type::I8);
1681
    }
1682
    { // Call should resolve to void, matching the function's return type.
1683
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1684
        let fnSym = super::symbolFor(&a, fnNode)
1685
            else throw testing::TestError::Failed;
1686
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1687
            else throw testing::TestError::Failed;
1688
        try expectType(&a, callExpr, *fnTy.returnType);
1689
    }
1690
}
1691
1692
@test fn testResolveFnTypeMultipleParams() throws (testing::TestError) {
1693
    let mut a = testResolver();
1694
    let program = "fn f(x: i8, y: i32) {} let x: i8 = 1; let y: i32 = 2; f(x, y);";
1695
    let result = try resolveProgramStr(&mut a, program);
1696
    try expectNoErrors(&result);
1697
1698
    let blockNode = result.root;
1699
    let case ast::NodeValue::Block(block) = blockNode.value
1700
        else throw testing::TestError::Failed;
1701
    let fnNode = try getBlockStmt(blockNode, 0);
1702
    let callStmt = try getBlockStmt(blockNode, 3);
1703
1704
    { // Ensure multi-parameter signatures record both argument types.
1705
        let sym = super::symbolFor(&a, fnNode)
1706
            else throw testing::TestError::Failed;
1707
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1708
            else throw testing::TestError::Failed;
1709
        try testing::expect(fnTy.paramTypes.len == 2);
1710
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1711
        try testing::expect(*fnTy.paramTypes[1] == super::Type::I32);
1712
        try testing::expect(*fnTy.returnType == super::Type::Void);
1713
1714
        let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value
1715
            else throw testing::TestError::Failed;
1716
        try testing::expect(fnDecl.sig.params.len == 2);
1717
1718
        let firstParam = fnDecl.sig.params[0];
1719
        let secondParam = fnDecl.sig.params[1];
1720
        try expectType(&a, firstParam, super::Type::I8);
1721
        try expectType(&a, secondParam, super::Type::I32);
1722
    }
1723
    { // Call expression should again mirror the function return type.
1724
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1725
        let fnSym = super::symbolFor(&a, fnNode)
1726
            else throw testing::TestError::Failed;
1727
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1728
            else throw testing::TestError::Failed;
1729
        try expectType(&a, callExpr, *fnTy.returnType);
1730
    }
1731
}
1732
1733
@test fn testResolveFnRecursiveCall() throws (testing::TestError) {
1734
    let mut a = testResolver();
1735
    let program = "fn flip(b: bool) -> bool { if b { return false; } return flip(false); }";
1736
    let result = try resolveProgramStr(&mut a, program);
1737
    try expectNoErrors(&result);
1738
1739
    let blockNode = result.root;
1740
    let case ast::NodeValue::Block(block) = blockNode.value
1741
        else throw testing::TestError::Failed;
1742
    let fnNode = try getBlockStmt(blockNode, 0);
1743
1744
    { // Function symbol should be visible for recursive calls within its own body.
1745
        let sym = super::symbolFor(&a, fnNode)
1746
            else throw testing::TestError::Failed;
1747
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1748
            else throw testing::TestError::Failed;
1749
        try testing::expect(fnTy.paramTypes.len == 1);
1750
        try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool);
1751
        try testing::expect(*fnTy.returnType == super::Type::Bool);
1752
    }
1753
}
1754
1755
@test fn testResolveFnCallMissingArgument() throws (testing::TestError) {
1756
    let mut a = testResolver();
1757
    let program = "fn f(x: i8) {} f();";
1758
    let result = try resolveProgramStr(&mut a, program);
1759
    // Expect an error when a required parameter is omitted.
1760
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1761
        expected: 1,
1762
        actual: 0,
1763
    }));
1764
}
1765
1766
@test fn testResolveFnCallExtraArgument() throws (testing::TestError) {
1767
    let mut a = testResolver();
1768
    let program = "fn f() {} f(1);";
1769
    let result = try resolveProgramStr(&mut a, program);
1770
    // Passing more arguments than declared should fail.
1771
    try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
1772
        expected: 0,
1773
        actual: 1,
1774
    }));
1775
}
1776
1777
@test fn testResolveFnCallArgumentTypeMismatch() throws (testing::TestError) {
1778
    let mut a = testResolver();
1779
    let program = "fn f(x: i8) {} f(true);";
1780
    let result = try resolveProgramStr(&mut a, program);
1781
    let err = try expectError(&result);
1782
    // The argument type (bool) should not match the parameter type (i8).
1783
    try expectTypeMismatch(err, super::Type::I8, super::Type::Bool);
1784
}
1785
1786
@test fn testResolveFnReturnTypeMismatch() throws (testing::TestError) {
1787
    let mut a = testResolver();
1788
    let program = "fn f() -> i32 { return true; }";
1789
    let result = try resolveProgramStr(&mut a, program);
1790
    let err = try expectError(&result);
1791
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
1792
}
1793
1794
@test fn testResolveFnReturnVoid() throws (testing::TestError) {
1795
    {
1796
        let mut a = testResolver();
1797
        let result = try resolveProgramStr(&mut a, "fn f() { return; }");
1798
        try expectNoErrors(&result);
1799
    } {
1800
        let mut a = testResolver();
1801
        let result = try resolveProgramStr(&mut a, "fn g() -> i32 { return; }");
1802
        let err = try expectError(&result);
1803
        try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
1804
    }
1805
}
1806
1807
@test fn testResolveFnMissingReturn() throws (testing::TestError) {
1808
    {
1809
        let mut a = testResolver();
1810
        let result = try resolveProgramStr(&mut a, "fn f() -> i32 {}");
1811
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1812
    } {
1813
        let mut a = testResolver();
1814
        let program = "fn g(flag: bool) -> i32 { if flag { return 1; } 2; }";
1815
        let result = try resolveProgramStr(&mut a, program);
1816
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1817
    }
1818
}
1819
1820
@test fn testResolveFnAllPathsReturn() throws (testing::TestError) {
1821
    let mut a = testResolver();
1822
    let program = "fn h(flag: bool) -> i32 { if flag { return 1; } else { return 2; } }";
1823
    let result = try resolveProgramStr(&mut a, program);
1824
    try expectNoErrors(&result);
1825
}
1826
1827
/// Test that match statements with returns in all branches don't require a
1828
/// return at the end of the function.
1829
@test fn testResolveFnMatchAllPathsReturn() throws (testing::TestError) {
1830
    {
1831
        // Union match with all variants returning.
1832
        let mut a = testResolver();
1833
        let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => return 2 } }";
1834
        let result = try resolveProgramStr(&mut a, program);
1835
        try expectNoErrors(&result);
1836
    } {
1837
        // Match with default case where all branches return.
1838
        let mut a = testResolver();
1839
        let program = "fn f(x: i32) -> i32 { match x { case 1 => return 1, else => return 0, } }";
1840
        let result = try resolveProgramStr(&mut a, program);
1841
        try expectNoErrors(&result);
1842
    } {
1843
        // Match where not all branches return should error.
1844
        let mut a = testResolver();
1845
        let program = "union E { A, B } fn f(e: E) -> i32 { match e { case E::A => return 1, case E::B => {} } }";
1846
        let result = try resolveProgramStr(&mut a, program);
1847
        try expectErrorKind(&result, super::ErrorKind::FnMissingReturn);
1848
    }
1849
}
1850
1851
@test fn testResolveAssign() throws (testing::TestError) {
1852
    {
1853
        let mut a = testResolver();
1854
        let result = try resolveProgramStr(&mut a, "let mut x: i32 = 0; set x = 1;");
1855
        try expectNoErrors(&result);
1856
    } {
1857
        let mut a = testResolver();
1858
        let result = try resolveProgramStr(&mut a, "let x: i32 = 0; set x = 1;");
1859
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1860
    } {
1861
        let mut a = testResolver();
1862
        let result = try resolveProgramStr(&mut a, "let mut x: bool = false; set x = 1;");
1863
        let err = try expectError(&result);
1864
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
1865
    } {
1866
        let mut a = testResolver();
1867
        let result = try resolveProgramStr(&mut a, "set x = 1;");
1868
        try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
1869
    } {
1870
        let mut a = testResolver();
1871
        let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = 1;");
1872
        try expectNoErrors(&result);
1873
    } {
1874
        let mut a = testResolver();
1875
        let result = try resolveProgramStr(&mut a, "let mut x: ?i32 = 0; set x = nil;");
1876
        try expectNoErrors(&result);
1877
    }
1878
}
1879
1880
@test fn testResolveAssignSubscript() throws (testing::TestError) {
1881
    {
1882
        let mut a = testResolver();
1883
        let program = "let mut xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
1884
        let result = try resolveProgramStr(&mut a, program);
1885
        try expectNoErrors(&result);
1886
    }
1887
    {
1888
        let mut a = testResolver();
1889
        let program = "fn assign(slice: &mut [u8]) { set slice[0] = 1; } fn run() { let mut xs: [u8; 2] = [0, 1]; assign(&mut xs[..]); }";
1890
        let result = try resolveProgramStr(&mut a, program);
1891
        try expectNoErrors(&result);
1892
    }
1893
    {
1894
        let mut a = testResolver();
1895
        let program = "fn f(input: *[u8]) { let mut slice: *[u8] = input; set slice[0] = 1; }";
1896
        let result = try resolveProgramStr(&mut a, program);
1897
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1898
    }
1899
    {
1900
        let mut a = testResolver();
1901
        let program = "let xs: [u8; 2] = [0, 1]; set xs[0] = 9;";
1902
        let result = try resolveProgramStr(&mut a, program);
1903
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1904
    }
1905
    {
1906
        let mut a = testResolver();
1907
        let program = "fn f(slice: &[u8]) { set slice[0] = 1; }";
1908
        let result = try resolveProgramStr(&mut a, program);
1909
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
1910
    }
1911
}
1912
1913
@test fn testResolveAssignIntegerLits() throws (testing::TestError) {
1914
    try expectAnalyzeOk("let x: i8 = 127;");
1915
    try expectAnalyzeOk("let x: i8 = 0x7F;");
1916
    try expectAnalyzeOk("let x: i8 = -128;");
1917
    try expectAnalyzeOk("let x: u8 = 255;");
1918
    try expectAnalyzeOk("let x: u8 = 0b11111111;");
1919
    try expectAnalyzeOk("let x: i16 = 0x7FFF;");
1920
    try expectAnalyzeOk("let x: i16 = -32768;");
1921
    try expectAnalyzeOk("let x: u16 = 0xFFFF;");
1922
    try expectAnalyzeOk("let x: i32 = 2147483647;");
1923
    try expectAnalyzeOk("let x: i32 = -2147483648;");
1924
    try expectAnalyzeOk("let x: u32 = 0xFFFFFFFF;");
1925
    try expectAnalyzeOk("let x: i64 = 9223372036854775807;");
1926
    try expectAnalyzeOk("let x: i64 = -9223372036854775808;");
1927
1928
    try expectAnalyzeOk("constant LIMIT: u8 = 0xFF;");
1929
1930
    try expectIntMismatch("let x: i8 = 128;", super::Type::I8);
1931
    try expectIntMismatch("let x: i8 = -129;", super::Type::I8);
1932
    try expectIntMismatch("let x: i8 = 0x80;", super::Type::I8);
1933
    try expectIntMismatch("let x: i8 = 0b10000000;", super::Type::I8);
1934
    try expectIntMismatch("let x: u8 = 256;", super::Type::U8);
1935
    try expectIntMismatch("let x: u8 = -1;", super::Type::U8);
1936
    try expectIntMismatch("let x: u8 = 0b100000000;", super::Type::U8);
1937
    try expectIntMismatch("let x: i16 = 32768;", super::Type::I16);
1938
    try expectIntMismatch("let x: i16 = -32769;", super::Type::I16);
1939
    try expectIntMismatch("let x: u16 = 65536;", super::Type::U16);
1940
    try expectIntMismatch("let x: u16 = -1;", super::Type::U16);
1941
    try expectIntMismatch("let x: i32 = 2147483648;", super::Type::I32);
1942
    try expectIntMismatch("let x: i32 = -2147483649;", super::Type::I32);
1943
    try expectIntMismatch("let x: i32 = 0xFFFFFFFF;", super::Type::I32);
1944
    try expectIntMismatch("let x: u32 = -1;", super::Type::U32);
1945
    try expectIntMismatch("let x: u32 = 0x100000000;", super::Type::U32);
1946
    try expectIntMismatch("let x: i64 = 9223372036854775808;", super::Type::I64);
1947
    try expectIntMismatch("let x: i64 = -9223372036854775809;", super::Type::I64);
1948
    try expectIntMismatch("constant LIMIT: u8 = 512;", super::Type::U8);
1949
    try expectIntMismatch("constant LIMIT: u8 = -5;", super::Type::U8);
1950
}
1951
1952
@test fn testNilCoercions() throws (testing::TestError) {
1953
    {
1954
        let mut a = testResolver();
1955
        let result = try resolveBlockStr(&mut a, "let opt: ?i32 = nil;");
1956
        try expectNoErrors(&result);
1957
    } {
1958
        let mut a = testResolver();
1959
        let program = "fn g(opt: ?i32) {} fn f() { g(nil); }";
1960
        let result = try resolveProgramStr(&mut a, program);
1961
        try expectNoErrors(&result);
1962
    } {
1963
        let mut a = testResolver();
1964
        let program = "fn make(flag: bool) -> ?i32 { if flag { return 1; } return nil; }";
1965
        let result = try resolveProgramStr(&mut a, program);
1966
        try expectNoErrors(&result);
1967
    }
1968
}
1969
1970
@test fn testOptionalComparedWithNil() throws (testing::TestError) {
1971
    let mut a = testResolver();
1972
    let program = "let opt: ?i32 = nil; opt == nil; nil == opt; opt == 1; 1 == opt; opt == opt; nil == nil;";
1973
    let result = try resolveBlockStr(&mut a, program);
1974
    try expectNoErrors(&result);
1975
1976
    for i in 1..7 {
1977
        let stmt = try getBlockStmt(result.root, i);
1978
        try expectExprStmtType(&a, stmt, super::Type::Bool);
1979
    }
1980
}
1981
1982
@test fn testResolveRecordLiteralAllFieldsSet() throws (testing::TestError) {
1983
    let mut a = testResolver();
1984
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, y: 2 };";
1985
    let result = try resolveProgramStr(&mut a, program);
1986
    try expectNoErrors(&result);
1987
}
1988
1989
@test fn testResolveRecordLiteralMissingField() throws (testing::TestError) {
1990
    let mut a = testResolver();
1991
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1 };";
1992
    let result = try resolveProgramStr(&mut a, program);
1993
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
1994
}
1995
1996
@test fn testResolveRecordLiteralFieldTypeMismatch() throws (testing::TestError) {
1997
    let mut a = testResolver();
1998
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: true, y: 2 };";
1999
    let result = try resolveProgramStr(&mut a, program);
2000
    let err = try expectError(&result);
2001
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2002
2003
    let errNode = err.node
2004
        else throw testing::TestError::Failed;
2005
    let case ast::NodeValue::Bool(_) = errNode.value
2006
        else throw testing::TestError::Failed;
2007
}
2008
2009
@test fn testResolveRecordLiteralExtraField() throws (testing::TestError) {
2010
    let mut a = testResolver();
2011
    let program = "record Pt { x: i32, y: i32 } let p = Pt { x: 1, z: 3, y: 2 };";
2012
    let result = try resolveProgramStr(&mut a, program);
2013
    let err = try expectError(&result);
2014
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2015
        else throw testing::TestError::Failed;
2016
}
2017
2018
/// Test that anonymous record literals with labels can be passed to functions expecting named records.
2019
@test fn testResolveAnonRecordLabeledToNamedRecord() throws (testing::TestError) {
2020
    let mut a = testResolver();
2021
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) -> i32 { return p.x; } foo({ x: 1, y: 2 });";
2022
    let result = try resolveProgramStr(&mut a, program);
2023
    try expectNoErrors(&result);
2024
}
2025
2026
/// Test that anonymous record with wrong field name causes out of order error.
2027
@test fn testResolveAnonRecordWrongFieldName() throws (testing::TestError) {
2028
    let mut a = testResolver();
2029
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, z: 2 });";
2030
    let result = try resolveProgramStr(&mut a, program);
2031
    let err = try expectError(&result);
2032
    let case super::ErrorKind::RecordFieldOutOfOrder { field: _, prev: _ } = err.kind
2033
        else throw testing::TestError::Failed;
2034
}
2035
2036
/// Test that anonymous record with wrong field type causes type mismatch.
2037
@test fn testResolveAnonRecordWrongFieldType() throws (testing::TestError) {
2038
    let mut a = testResolver();
2039
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: true, y: 2 });";
2040
    let result = try resolveProgramStr(&mut a, program);
2041
    let err = try expectError(&result);
2042
    let case super::ErrorKind::TypeMismatch(_) = err.kind
2043
        else throw testing::TestError::Failed;
2044
}
2045
2046
/// Test that anonymous record with missing field causes a missing field error.
2047
@test fn testResolveAnonRecordMissingField() throws (testing::TestError) {
2048
    let mut a = testResolver();
2049
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1 });";
2050
    let result = try resolveProgramStr(&mut a, program);
2051
    try expectErrorKind(&result, super::ErrorKind::RecordFieldMissing("y"));
2052
}
2053
2054
/// Test that anonymous record with extra field causes a count mismatch error.
2055
@test fn testResolveAnonRecordExtraField() throws (testing::TestError) {
2056
    let mut a = testResolver();
2057
    let program = "record Pt { x: i32, y: i32 } fn foo(p: Pt) {} foo({ x: 1, y: 2, z: 3 });";
2058
    let result = try resolveProgramStr(&mut a, program);
2059
    let err = try expectError(&result);
2060
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2061
        else throw testing::TestError::Failed;
2062
}
2063
2064
/// Test that anonymous record fields can be coerced (e.g., i32 to optional).
2065
@test fn testResolveAnonRecordFieldCoercion() throws (testing::TestError) {
2066
    let mut a = testResolver();
2067
    let program = "record Opt { x: ?i32 } fn foo(p: Opt) {} foo({ x: 42 });";
2068
    let result = try resolveProgramStr(&mut a, program);
2069
    try expectNoErrors(&result);
2070
}
2071
2072
/// Test that arrays of anonymous records with labeled fields are allowed.
2073
@test fn testResolveAnonRecordArray() throws (testing::TestError) {
2074
    let mut a = testResolver();
2075
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4 }];";
2076
    let result = try resolveProgramStr(&mut a, program);
2077
    try expectNoErrors(&result);
2078
}
2079
2080
/// Test that arrays of anonymous records with extra fields cause count mismatch.
2081
@test fn testResolveAnonRecordArrayMismatch() throws (testing::TestError) {
2082
    let mut a = testResolver();
2083
    let program = "record Pt { x: i32, y: i32 } constant ARR: [Pt; 2] = [{ x: 1, y: 2 }, { x: 3, y: 4, z: 5 }];";
2084
    let result = try resolveProgramStr(&mut a, program);
2085
    let err = try expectError(&result);
2086
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
2087
        else throw testing::TestError::Failed;
2088
}
2089
2090
/// Test that unlabeled record declarations are analyzed correctly.
2091
@test fn testResolveUnlabeledRecordDecl() throws (testing::TestError) {
2092
    let mut a = testResolver();
2093
    let program = "record R(i32, bool);";
2094
    let result = try resolveProgramStr(&mut a, program);
2095
    try expectNoErrors(&result);
2096
2097
    // Verify the type symbol was created with labeled=false.
2098
    let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2099
    let case super::NominalType::Record(recordType) = *nominalTy
2100
        else throw testing::TestError::Failed;
2101
    try testing::expect(not recordType.labeled);
2102
    try testing::expect(recordType.fields.len == 2);
2103
    try testing::expect(recordType.fields[0].name == nil);
2104
    try testing::expect(recordType.fields[1].name == nil);
2105
}
2106
2107
@test fn testResolveLabeledRecordDecl() throws (testing::TestError) {
2108
    let mut a = testResolver();
2109
    let program = "record R { x: i32, y: i32 }";
2110
    let result = try resolveProgramStr(&mut a, program);
2111
    try expectNoErrors(&result);
2112
2113
    let nominalTy = try getTypeInScopeOf(&a, result.root, "R");
2114
    let case super::NominalType::Record(recordType) = *nominalTy
2115
        else throw testing::TestError::Failed;
2116
    try testing::expect(recordType.labeled);
2117
    try testing::expect(recordType.fields.len == 2);
2118
    try testing::expect(recordType.fields[0].name <> nil);
2119
    try testing::expect(recordType.fields[1].name <> nil);
2120
}
2121
2122
@test fn testResolveRecordFieldAccessValid() throws (testing::TestError) {
2123
    let mut a = testResolver();
2124
    let program = "record Pt { x: i32, y: u8 } let p = Pt { x: 1, y: 2 }; p.y;";
2125
    let result = try resolveProgramStr(&mut a, program);
2126
    try expectNoErrors(&result);
2127
2128
    let fieldStmt = try getBlockStmt(result.root, 2);
2129
    try expectExprStmtType(&a, fieldStmt, super::Type::U8);
2130
}
2131
2132
@test fn testResolveRecordFieldAccessUnknownField() throws (testing::TestError) {
2133
    let mut a = testResolver();
2134
    let program = "record Pt { x: i32 } let p = Pt { x: 1 }; p.y;";
2135
    let result = try resolveProgramStr(&mut a, program);
2136
    try expectErrorKind(&result, super::ErrorKind::RecordFieldUnknown("y"));
2137
}
2138
2139
@test fn testResolveRecordFieldAccessOnFunctionReturn() throws (testing::TestError) {
2140
    let mut a = testResolver();
2141
    let program = "record Pt { x: i32, y: i32 } fn make() -> Pt { return Pt { x: 5, y: 10 }; } make().x;";
2142
    let result = try resolveProgramStr(&mut a, program);
2143
    try expectNoErrors(&result);
2144
2145
    let stmt = try getBlockStmt(result.root, 2);
2146
    try expectExprStmtType(&a, stmt, super::Type::I32);
2147
}
2148
2149
@test fn testResolveRecordFieldAccessChained() throws (testing::TestError) {
2150
    let mut a = testResolver();
2151
    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;";
2152
    let result = try resolveProgramStr(&mut a, program);
2153
    try expectNoErrors(&result);
2154
2155
    let stmt = try getBlockStmt(result.root, 4);
2156
    try expectExprStmtType(&a, stmt, super::Type::I32);
2157
}
2158
2159
@test fn testResolveRecordFieldAccessOnInteger() throws (testing::TestError) {
2160
    let mut a = testResolver();
2161
    let program = "let x: i32 = 42; x.field;";
2162
    let result = try resolveBlockStr(&mut a, program);
2163
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2164
}
2165
2166
@test fn testResolveRecordFieldAccessOnArray() throws (testing::TestError) {
2167
    let mut a = testResolver();
2168
    let program = "let arr: [i32; 3] = [1, 2, 3]; arr.field;";
2169
    let result = try resolveProgramStr(&mut a, program);
2170
    try expectErrorKind(&result, super::ErrorKind::ArrayFieldUnknown("field"));
2171
}
2172
2173
@test fn testResolveRecordFieldAccessOnBool() throws (testing::TestError) {
2174
    let mut a = testResolver();
2175
    let program = "let b: bool = true; b.field;";
2176
    let result = try resolveProgramStr(&mut a, program);
2177
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2178
}
2179
2180
@test fn testResolveRecordFieldAccessOnOptional() throws (testing::TestError) {
2181
    let mut a = testResolver();
2182
    let program = "record Pt { x: i32 } let opt: ?Pt = Pt { x: 5 }; opt.x;";
2183
    let result = try resolveProgramStr(&mut a, program);
2184
    try expectErrorKind(&result, super::ErrorKind::ExpectedRecord);
2185
}
2186
2187
/// Records may reference themselves through pointers without causing resolution errors.
2188
@test fn testResolveRecordSelfReferentialPointer() throws (testing::TestError) {
2189
    let mut a = testResolver();
2190
    let program = "record A { next: *A }";
2191
    let result = try resolveProgramStr(&mut a, program);
2192
    try expectNoErrors(&result);
2193
}
2194
2195
/// Mutually recursive records should resolve without infinite loops.
2196
@test fn testResolveRecordMutuallyRecursive() throws (testing::TestError) {
2197
    let mut a = testResolver();
2198
    let program = "record A { b: *B } record B { a: *A }";
2199
    let result = try resolveProgramStr(&mut a, program);
2200
    try expectNoErrors(&result);
2201
}
2202
2203
/// Unions may reference themselves through pointers without causing resolution errors.
2204
@test fn testResolveUnionSelfReferentialPointerAllowed() throws (testing::TestError) {
2205
    let mut a = testResolver();
2206
    let program = "union List { Cons(*List), Nil }";
2207
    let result = try resolveProgramStr(&mut a, program);
2208
    try expectNoErrors(&result);
2209
}
2210
2211
/// Mutually recursive unions should resolve without infinite loops.
2212
@test fn testResolveUnionMutuallyRecursive() throws (testing::TestError) {
2213
    let mut a = testResolver();
2214
    let program = "union A { HasB(*B), None } union B { HasA(*A), None }";
2215
    let result = try resolveProgramStr(&mut a, program);
2216
    try expectNoErrors(&result);
2217
}
2218
2219
/// Unions with record payloads containing slice references to self should resolve.
2220
/// This matches the pattern in sexpr.rad: `List { tail: *[Expr] }`.
2221
@test fn testResolveUnionRecordPayloadWithSliceSelfRef() throws (testing::TestError) {
2222
    let mut a = testResolver();
2223
    let program = "union Expr { Null, List { head: *[u8], tail: *[Expr] } }";
2224
    let result = try resolveProgramStr(&mut a, program);
2225
    try expectNoErrors(&result);
2226
}
2227
2228
@test fn testUndefinedCoercions() throws (testing::TestError) {
2229
    {
2230
        let mut a = testResolver();
2231
        let result = try resolveBlockStr(&mut a, "unsafe { let count: i32 = undefined; }");
2232
        try expectNoErrors(&result);
2233
    } {
2234
        let mut a = testResolver();
2235
        let program = "let mut value: i32 = 0; unsafe { set value = undefined; }";
2236
        let result = try resolveProgramStr(&mut a, program);
2237
        try expectNoErrors(&result);
2238
    } {
2239
        let mut a = testResolver();
2240
        let program = "fn f(x: i32) {} fn g() { unsafe { f(undefined); } }";
2241
        let result = try resolveProgramStr(&mut a, program);
2242
        try expectNoErrors(&result);
2243
    } {
2244
        let mut a = testResolver();
2245
        let program = "fn fetch() -> i32 { unsafe { return undefined; } }";
2246
        let result = try resolveProgramStr(&mut a, program);
2247
        try expectNoErrors(&result);
2248
    }
2249
}
2250
2251
@test fn testResolveBlockVoid() throws (testing::TestError) {
2252
    let mut a = testResolver();
2253
    let result = try resolveProgramStr(&mut a, "{ 42; }");
2254
    try expectNoErrors(&result);
2255
2256
    let block = try getBlockStmt(result.root, 0);
2257
    try expectType(&a, block, super::Type::Void);
2258
}
2259
2260
@test fn testResolveBlockNever() throws (testing::TestError) {
2261
    let mut a = testResolver();
2262
    let result = try resolveProgramStr(&mut a, "{ panic; }");
2263
    try expectNoErrors(&result);
2264
2265
    let block = try getBlockStmt(result.root, 0);
2266
    try expectType(&a, block, super::Type::Never);
2267
}
2268
2269
@test fn testResolveIfAllBranchesNever() throws (testing::TestError) {
2270
    let mut a = testResolver();
2271
    let program = "if true { panic; } else { panic; }";
2272
    let result = try resolveProgramStr(&mut a, program);
2273
    try expectNoErrors(&result);
2274
2275
    let stmt = try getBlockStmt(result.root, 0);
2276
    try expectType(&a, stmt, super::Type::Never);
2277
}
2278
2279
@test fn testResolveIfMixedBranchesNotNever() throws (testing::TestError) {
2280
    let mut a = testResolver();
2281
    let program = "if true { panic; } else {}";
2282
    let result = try resolveProgramStr(&mut a, program);
2283
    try expectNoErrors(&result);
2284
2285
    let stmt = try getBlockStmt(result.root, 0);
2286
    try expectType(&a, stmt, super::Type::Void);
2287
}
2288
2289
@test fn testResolveLetElse() throws (testing::TestError) {
2290
    let mut a = testResolver();
2291
    let program = "let opt: ?i32 = 42; let value = opt else panic; value;";
2292
    let result = try resolveProgramStr(&mut a, program);
2293
    try expectNoErrors(&result);
2294
2295
    let blockNode = result.root;
2296
    let case ast::NodeValue::Block(block) = blockNode.value
2297
        else throw testing::TestError::Failed;
2298
    let letElseNode = try getBlockStmt(blockNode, 1);
2299
    let valueStmt = try getBlockStmt(blockNode, 2);
2300
2301
    { // Ensure the bound identifier receives the inner optional type.
2302
        let valueExpr = try expectExprStmtType(&a, valueStmt, super::Type::I32);
2303
2304
        let sym = super::symbolFor(&a, valueExpr)
2305
            else throw testing::TestError::Failed;
2306
        let case super::SymbolData::Value { type: valType, .. } = sym.data
2307
            else throw testing::TestError::Failed;
2308
        try testing::expect(valType == super::Type::I32);
2309
    }
2310
    // The let-else statement itself should be typed as void.
2311
    try expectType(&a, letElseNode, super::Type::Void);
2312
}
2313
2314
@test fn testResolveLetElseDefaultValue() throws (testing::TestError) {
2315
    let mut a = testResolver();
2316
    let program = "let opt: ?i32 = nil; let value = opt else 42; value;";
2317
    let result = try resolveProgramStr(&mut a, program);
2318
    try expectNoErrors(&result);
2319
}
2320
2321
@test fn testResolveLetElseRequiresDivergentElse() throws (testing::TestError) {
2322
    let mut a = testResolver();
2323
    let program = "let opt: ?i32 = nil; let value = opt else {}; value;";
2324
    let result = try resolveProgramStr(&mut a, program);
2325
    let err = try expectError(&result);
2326
    try expectTypeMismatch(err, super::Type::I32, super::Type::Void);
2327
}
2328
2329
@test fn testResolveLetElseRequiresOptional() throws (testing::TestError) {
2330
    let mut a = testResolver();
2331
    let program = "let x: i32 = 42; let value = x else panic;";
2332
    let result = try resolveProgramStr(&mut a, program);
2333
    try expectErrorKind(&result, super::ErrorKind::ExpectedOptional);
2334
}
2335
2336
/// Test that `if let mut` produces a mutable binding.
2337
@test fn testResolveIfLetMut() throws (testing::TestError) {
2338
    let mut a = testResolver();
2339
    let program = "let opt: ?i32 = 42; if let mut v = opt { set v = v + 1; }";
2340
    let result = try resolveProgramStr(&mut a, program);
2341
    try expectNoErrors(&result);
2342
}
2343
2344
/// Test that `if let` (without mut) rejects assignment.
2345
@test fn testResolveIfLetImmutable() throws (testing::TestError) {
2346
    let mut a = testResolver();
2347
    let program = "let opt: ?i32 = 42; if let v = opt { set v = 1; }";
2348
    let result = try resolveProgramStr(&mut a, program);
2349
    let err = try expectError(&result);
2350
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2351
}
2352
2353
/// Test that `let mut ... else` produces a mutable binding.
2354
@test fn testResolveLetMutElse() throws (testing::TestError) {
2355
    let mut a = testResolver();
2356
    let program = "let opt: ?i32 = 42; let mut v = opt else panic; set v = v + 1;";
2357
    let result = try resolveProgramStr(&mut a, program);
2358
    try expectNoErrors(&result);
2359
}
2360
2361
/// Test that `let ... else` (without mut) rejects assignment.
2362
@test fn testResolveLetElseImmutable() throws (testing::TestError) {
2363
    let mut a = testResolver();
2364
    let program = "let opt: ?i32 = 42; let v = opt else panic; set v = 1;";
2365
    let result = try resolveProgramStr(&mut a, program);
2366
    let err = try expectError(&result);
2367
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2368
}
2369
2370
@test fn testResolveLetCaseElse() throws (testing::TestError) {
2371
    {
2372
        let mut a = testResolver();
2373
        let program = "let case _ = 1 else panic;";
2374
        let result = try resolveProgramStr(&mut a, program);
2375
        try expectNoErrors(&result);
2376
    } {
2377
        let mut a = testResolver();
2378
        let program = "let case _ = true else false;";
2379
        let result = try resolveProgramStr(&mut a, program);
2380
        try expectNoErrors(&result);
2381
    }
2382
}
2383
2384
@test fn testResolveLetCaseElseRequiresDivergentElse() throws (testing::TestError) {
2385
    let mut a = testResolver();
2386
    let program = "let case _ = 1 else {};";
2387
    let result = try resolveProgramStr(&mut a, program);
2388
    let err = try expectError(&result);
2389
    try expectTypeMismatch(err, super::Type::Int, super::Type::Void);
2390
}
2391
2392
@test fn testResolveTryValidPropagation() throws (testing::TestError) {
2393
    let mut a = testResolver();
2394
    let program = "fn fallible() throws (i32) {} fn caller() throws (i32) { try fallible() }";
2395
    let result = try resolveProgramStr(&mut a, program);
2396
    try expectNoErrors(&result);
2397
}
2398
2399
@test fn testResolveTryRequiresThrowsClause() throws (testing::TestError) {
2400
    let mut a = testResolver();
2401
    let program = "fn fallible() throws (i32) {} fn caller() { try fallible() }";
2402
    let result = try resolveProgramStr(&mut a, program);
2403
    try expectErrorKind(&result, super::ErrorKind::TryRequiresThrows);
2404
}
2405
2406
@test fn testResolveTryIncompatibleError() throws (testing::TestError) {
2407
    let mut a = testResolver();
2408
    let program = "fn fallible() throws (i32) {} fn caller() throws (i8) { try fallible() }";
2409
    let result = try resolveProgramStr(&mut a, program);
2410
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
2411
}
2412
2413
@test fn testResolveTryNonThrowing() throws (testing::TestError) {
2414
    let mut a = testResolver();
2415
    let program = "fn safe() {} fn caller() throws (i32) { try safe() }";
2416
    let result = try resolveProgramStr(&mut a, program);
2417
    try expectErrorKind(&result, super::ErrorKind::TryNonThrowing);
2418
}
2419
2420
@test fn testResolveTryCatchBlockMatchesResult() throws (testing::TestError) {
2421
    let mut a = testResolver();
2422
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 42; }; }";
2423
    let result = try resolveProgramStr(&mut a, program);
2424
    try expectNoErrors(&result);
2425
}
2426
2427
@test fn testResolveTryCatchBlockDiverges() throws (testing::TestError) {
2428
    let mut a = testResolver();
2429
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { return 7; }; }";
2430
    let result = try resolveProgramStr(&mut a, program);
2431
    try expectNoErrors(&result);
2432
}
2433
2434
@test fn testResolveTryCatchBlockMustDiverge() throws (testing::TestError) {
2435
    let mut a = testResolver();
2436
    let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; return 0; } fn caller() -> u32 { return try fallible() catch { 7; }; }";
2437
    let result = try resolveProgramStr(&mut a, program);
2438
    let err = try expectError(&result);
2439
    try expectTypeMismatch(err, super::Type::U32, super::Type::Void);
2440
}
2441
2442
@test fn testResolveCallMissingTry() throws (testing::TestError) {
2443
    let mut a = testResolver();
2444
    let program = "fn fallible() throws (i32) {} fn caller() { fallible() }";
2445
    let result = try resolveProgramStr(&mut a, program);
2446
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
2447
}
2448
2449
/// Test that `try?` converts errors to optionals without requiring caller to throw.
2450
@test fn testResolveTryOptionalConvertsToOptional() throws (testing::TestError) {
2451
    // `try?` should wrap the return type in optional and not require caller to throw.
2452
    {
2453
        let mut a = testResolver();
2454
        let program = "record S {} fn fallible() -> *S throws (i32) { panic; } fn caller() -> ?*S { return try? fallible(); }";
2455
        let result = try resolveProgramStr(&mut a, program);
2456
        try expectNoErrors(&result);
2457
    }
2458
    // `try?` works in non-throwing function.
2459
    {
2460
        let mut a = testResolver();
2461
        let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> ?i32 { return try? fallible(); }";
2462
        let result = try resolveProgramStr(&mut a, program);
2463
        try expectNoErrors(&result);
2464
    }
2465
    // `try?` can be used in if-let patterns.
2466
    {
2467
        let mut a = testResolver();
2468
        let program = "fn fallible() -> i32 throws (i32) { panic; } fn caller() -> i32 { if let x = try? fallible() { return x; } return 0; }";
2469
        let result = try resolveProgramStr(&mut a, program);
2470
        try expectNoErrors(&result);
2471
    }
2472
}
2473
2474
@test fn testResolveThrowValid() throws (testing::TestError) {
2475
    let mut a = testResolver();
2476
    let program = "fn fail() throws (i32) { throw 1; }";
2477
    let result = try resolveProgramStr(&mut a, program);
2478
    try expectNoErrors(&result);
2479
}
2480
2481
@test fn testResolveThrowRequiresThrowsClause() throws (testing::TestError) {
2482
    let mut a = testResolver();
2483
    let program = "fn fail() { throw 1; }";
2484
    let result = try resolveProgramStr(&mut a, program);
2485
    try expectErrorKind(&result, super::ErrorKind::ThrowRequiresThrows);
2486
}
2487
2488
@test fn testResolveThrowIncompatibleError() throws (testing::TestError) {
2489
    let mut a = testResolver();
2490
    let program = "fn fail() throws (i32) { throw true; }";
2491
    let result = try resolveProgramStr(&mut a, program);
2492
    try expectErrorKind(&result, super::ErrorKind::ThrowIncompatibleError);
2493
}
2494
2495
// Binary operation tests //////////////////////////////////////////////////////
2496
2497
@test fn testResolveBinaryOpArithmetic() throws (testing::TestError) {
2498
    {
2499
        let mut a = testResolver();
2500
        let result = try resolveExprStr(&mut a, "4 + 4");
2501
        try expectNoErrors(&result);
2502
        try expectType(&a, result.root, super::Type::Int);
2503
    } {
2504
        let mut a = testResolver();
2505
        let result = try resolveExprStr(&mut a, "10 - 3");
2506
        try expectNoErrors(&result);
2507
        try expectType(&a, result.root, super::Type::Int);
2508
    } {
2509
        let mut a = testResolver();
2510
        let result = try resolveExprStr(&mut a, "5 * 6");
2511
        try expectNoErrors(&result);
2512
        try expectType(&a, result.root, super::Type::Int);
2513
    } {
2514
        let mut a = testResolver();
2515
        let result = try resolveExprStr(&mut a, "20 / 4");
2516
        try expectNoErrors(&result);
2517
        try expectType(&a, result.root, super::Type::Int);
2518
    } {
2519
        let mut a = testResolver();
2520
        let result = try resolveExprStr(&mut a, "17 % 5");
2521
        try expectNoErrors(&result);
2522
        try expectType(&a, result.root, super::Type::Int);
2523
    } {
2524
        let mut a = testResolver();
2525
        let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: i32 = 5; x + y;");
2526
        try expectNoErrors(&result);
2527
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2528
        try expectExprStmtType(&a, stmt, super::Type::I32);
2529
    } {
2530
        let mut a = testResolver();
2531
        let result = try resolveExprStr(&mut a, "1 + (2 * 3) - 4");
2532
        try expectNoErrors(&result);
2533
        try expectType(&a, result.root, super::Type::Int);
2534
    } {
2535
        let mut a = testResolver();
2536
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n * 2;");
2537
        try expectNoErrors(&result);
2538
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2539
        try expectExprStmtType(&a, stmt, super::Type::I32);
2540
    } {
2541
        let mut a = testResolver();
2542
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 2 * n;");
2543
        try expectNoErrors(&result);
2544
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2545
        try expectExprStmtType(&a, stmt, super::Type::I32);
2546
    } {
2547
        let mut a = testResolver();
2548
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n - 1;");
2549
        try expectNoErrors(&result);
2550
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2551
        try expectExprStmtType(&a, stmt, super::Type::I32);
2552
    }
2553
}
2554
2555
@test fn testResolveBinaryOpComparison() throws (testing::TestError) {
2556
    {
2557
        let mut a = testResolver();
2558
        let result = try resolveExprStr(&mut a, "5 == 5");
2559
        try expectNoErrors(&result);
2560
        try expectType(&a, result.root, super::Type::Bool);
2561
    } {
2562
        let mut a = testResolver();
2563
        let result = try resolveExprStr(&mut a, "5 <> 10");
2564
        try expectNoErrors(&result);
2565
        try expectType(&a, result.root, super::Type::Bool);
2566
    } {
2567
        let mut a = testResolver();
2568
        let result = try resolveExprStr(&mut a, "5 < 10");
2569
        try expectNoErrors(&result);
2570
        try expectType(&a, result.root, super::Type::Bool);
2571
    } {
2572
        let mut a = testResolver();
2573
        let result = try resolveExprStr(&mut a, "10 > 5");
2574
        try expectNoErrors(&result);
2575
        try expectType(&a, result.root, super::Type::Bool);
2576
    } {
2577
        let mut a = testResolver();
2578
        let result = try resolveExprStr(&mut a, "5 <= 5");
2579
        try expectNoErrors(&result);
2580
        try expectType(&a, result.root, super::Type::Bool);
2581
    } {
2582
        let mut a = testResolver();
2583
        let result = try resolveExprStr(&mut a, "10 >= 5");
2584
        try expectNoErrors(&result);
2585
        try expectType(&a, result.root, super::Type::Bool);
2586
    } {
2587
        let mut a = testResolver();
2588
        let result = try resolveExprStr(&mut a, "true == false");
2589
        try expectNoErrors(&result);
2590
        try expectType(&a, result.root, super::Type::Bool);
2591
    } {
2592
        let mut a = testResolver();
2593
        let result = try resolveExprStr(&mut a, "5 + 3 > 10 - 4");
2594
        try expectNoErrors(&result);
2595
        try expectType(&a, result.root, super::Type::Bool);
2596
    } {
2597
        let mut a = testResolver();
2598
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; n == 1;");
2599
        try expectNoErrors(&result);
2600
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2601
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2602
    } {
2603
        let mut a = testResolver();
2604
        let result = try resolveBlockStr(&mut a, "let n: i32 = 5; 1 == n;");
2605
        try expectNoErrors(&result);
2606
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2607
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2608
    }
2609
}
2610
2611
@test fn testResolveBinaryOpLogical() throws (testing::TestError) {
2612
    {
2613
        let mut a = testResolver();
2614
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x and y;");
2615
        try expectNoErrors(&result);
2616
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2617
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2618
    } {
2619
        let mut a = testResolver();
2620
        let result = try resolveBlockStr(&mut a, "let x: bool = true; let y: bool = false; x or y;");
2621
        try expectNoErrors(&result);
2622
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2623
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2624
    } {
2625
        let mut a = testResolver();
2626
        let result = try resolveExprStr(&mut a, "true and false");
2627
        try expectNoErrors(&result);
2628
        try expectType(&a, result.root, super::Type::Bool);
2629
    }
2630
}
2631
2632
@test fn testResolveBinaryOpArithmeticTypeMismatch() throws (testing::TestError) {
2633
    {
2634
        let mut a = testResolver();
2635
        let result = try resolveProgramStr(&mut a, "4 + true");
2636
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2637
    } {
2638
        let mut a = testResolver();
2639
        let result = try resolveBlockStr(&mut a, "let x: i32 = 4; let y: bool = false; x + y;");
2640
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2641
    } {
2642
        let mut a = testResolver();
2643
        let result = try resolveProgramStr(&mut a, "10 - false");
2644
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2645
    } {
2646
        let mut a = testResolver();
2647
        let result = try resolveProgramStr(&mut a, "5 * true");
2648
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2649
    } {
2650
        let mut a = testResolver();
2651
        let result = try resolveProgramStr(&mut a, "20 / false");
2652
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2653
    } {
2654
        let mut a = testResolver();
2655
        let result = try resolveProgramStr(&mut a, "17 % true");
2656
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2657
    } {
2658
        let mut a = testResolver();
2659
        let result = try resolveProgramStr(&mut a, "1 + (true * 3)");
2660
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2661
    }
2662
}
2663
2664
@test fn testResolveBinaryOpLogicalTypeMismatch() throws (testing::TestError) {
2665
    {
2666
        let mut a = testResolver();
2667
        let result = try resolveProgramStr(&mut a, "42 and true");
2668
        let err = try expectError(&result);
2669
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2670
    } {
2671
        let mut a = testResolver();
2672
        let result = try resolveProgramStr(&mut a, "true or 5");
2673
        let err = try expectError(&result);
2674
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2675
    } {
2676
        let mut a = testResolver();
2677
        let result = try resolveProgramStr(&mut a, "1 and 2");
2678
        let err = try expectError(&result);
2679
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2680
    }
2681
}
2682
2683
@test fn testResolveBinaryOpComparisonTypeMismatch() throws (testing::TestError) {
2684
    let mut a = testResolver();
2685
    let result = try resolveProgramStr(&mut a, "true < false");
2686
    try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2687
}
2688
2689
// Unary operation tests ///////////////////////////////////////////////////////
2690
2691
@test fn testResolveUnaryOpNot() throws (testing::TestError) {
2692
    {
2693
        let mut a = testResolver();
2694
        let result = try resolveExprStr(&mut a, "not true");
2695
        try expectNoErrors(&result);
2696
        try expectType(&a, result.root, super::Type::Bool);
2697
    } {
2698
        let mut a = testResolver();
2699
        let result = try resolveBlockStr(&mut a, "let x: bool = true; not x;");
2700
        try expectNoErrors(&result);
2701
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2702
        try expectExprStmtType(&a, stmt, super::Type::Bool);
2703
    } {
2704
        let mut a = testResolver();
2705
        let result = try resolveExprStr(&mut a, "not (true and false)");
2706
        try expectNoErrors(&result);
2707
        try expectType(&a, result.root, super::Type::Bool);
2708
    } {
2709
        let mut a = testResolver();
2710
        let result = try resolveProgramStr(&mut a, "not 42");
2711
        let err = try expectError(&result);
2712
        try expectTypeMismatch(err, super::Type::Bool, super::Type::Int);
2713
    } {
2714
        let mut a = testResolver();
2715
        let result = try resolveBlockStr(&mut a, "let x: i32 = 5; not x;");
2716
        let err = try expectError(&result);
2717
        try expectTypeMismatch(err, super::Type::Bool, super::Type::I32);
2718
    }
2719
}
2720
2721
@test fn testResolveUnaryOpNeg() throws (testing::TestError) {
2722
    {
2723
        let mut a = testResolver();
2724
        let result = try resolveExprStr(&mut a, "-42");
2725
        try expectNoErrors(&result);
2726
        try expectType(&a, result.root, super::Type::Int);
2727
    } {
2728
        let mut a = testResolver();
2729
        let result = try resolveBlockStr(&mut a, "let x: i32 = 10; -x;");
2730
        try expectNoErrors(&result);
2731
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2732
        try expectExprStmtType(&a, stmt, super::Type::I32);
2733
    } {
2734
        let mut a = testResolver();
2735
        let result = try resolveExprStr(&mut a, "-(5 + 3)");
2736
        try expectNoErrors(&result);
2737
        try expectType(&a, result.root, super::Type::Int);
2738
    } {
2739
        let mut a = testResolver();
2740
        let result = try resolveBlockStr(&mut a, "let x: i8 = 5; -x;");
2741
        try expectNoErrors(&result);
2742
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2743
        try expectExprStmtType(&a, stmt, super::Type::I8);
2744
    } {
2745
        let mut a = testResolver();
2746
        let result = try resolveProgramStr(&mut a, "-true");
2747
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2748
    } {
2749
        let mut a = testResolver();
2750
        let result = try resolveBlockStr(&mut a, "let x: bool = false; -x;");
2751
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2752
    }
2753
}
2754
2755
@test fn testResolveUnaryOpBitNot() throws (testing::TestError) {
2756
    {
2757
        let mut a = testResolver();
2758
        let result = try resolveExprStr(&mut a, "~42");
2759
        try expectNoErrors(&result);
2760
        try expectType(&a, result.root, super::Type::Int);
2761
    } {
2762
        let mut a = testResolver();
2763
        let result = try resolveBlockStr(&mut a, "let x: u32 = 255; ~x;");
2764
        try expectNoErrors(&result);
2765
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2766
        try expectExprStmtType(&a, stmt, super::Type::U32);
2767
    } {
2768
        let mut a = testResolver();
2769
        let result = try resolveExprStr(&mut a, "~(0xFF)");
2770
        try expectNoErrors(&result);
2771
        try expectType(&a, result.root, super::Type::Int);
2772
    } {
2773
        let mut a = testResolver();
2774
        let result = try resolveBlockStr(&mut a, "let x: i8 = 5; ~x;");
2775
        try expectNoErrors(&result);
2776
        let stmt = try parser::tests::getBlockLastStmt(result.root);
2777
        try expectExprStmtType(&a, stmt, super::Type::I8);
2778
    } {
2779
        let mut a = testResolver();
2780
        let result = try resolveProgramStr(&mut a, "~true");
2781
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2782
    } {
2783
        let mut a = testResolver();
2784
        let result = try resolveBlockStr(&mut a, "let x: bool = false; ~x;");
2785
        try expectErrorKind(&result, super::ErrorKind::ExpectedNumeric);
2786
    }
2787
}
2788
2789
@test fn testResolveUnaryOpNested() throws (testing::TestError) {
2790
    {
2791
        let mut a = testResolver();
2792
        let result = try resolveExprStr(&mut a, "not not true");
2793
        try expectNoErrors(&result);
2794
        try expectType(&a, result.root, super::Type::Bool);
2795
    } {
2796
        let mut a = testResolver();
2797
        let result = try resolveExprStr(&mut a, "--42");
2798
        try expectNoErrors(&result);
2799
        try expectType(&a, result.root, super::Type::Int);
2800
    } {
2801
        let mut a = testResolver();
2802
        let result = try resolveExprStr(&mut a, "~~0xFF");
2803
        try expectNoErrors(&result);
2804
        try expectType(&a, result.root, super::Type::Int);
2805
    } {
2806
        let mut a = testResolver();
2807
        let result = try resolveExprStr(&mut a, "-(~42)");
2808
        try expectNoErrors(&result);
2809
        try expectType(&a, result.root, super::Type::Int);
2810
    }
2811
}
2812
2813
// test fn testNormalPointerArithmetic() throws (testing::TestError) {
2814
//     mut a = testResolver();
2815
//     let result = try resolveProgramStr(&mut a, "fn test() { let ptr: *i32 = undefined; let x = ptr + 1; }");
2816
//     try expectNoErrors(&result);
2817
// }
2818
2819
// Dereference tests //////////////////////////////////////////////////////////
2820
2821
@test fn testResolveDeref() throws (testing::TestError) {
2822
    {
2823
        let mut a = testResolver();
2824
        let result = try resolveProgramStr(&mut a, "fn f(ptr: &i32) { *ptr; }");
2825
        try expectNoErrors(&result);
2826
        let body = try getFnBody(&a, result.root, "f");
2827
        if body.statements.len <> 1 {
2828
            throw testing::TestError::Failed;
2829
        }
2830
        let stmt = body.statements[0];
2831
        try expectExprStmtType(&a, stmt, super::Type::I32);
2832
    } {
2833
        let mut a = testResolver();
2834
        let result = try resolveExprStr(&mut a, "*42");
2835
        try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
2836
    } {
2837
        let mut a = testResolver();
2838
        let result = try resolveBlockStr(&mut a, "let x: i32 = 5; *x;");
2839
        try expectErrorKind(&result, super::ErrorKind::ExpectedPointer);
2840
    }
2841
}
2842
2843
@test fn testResolveAssignDeref() throws (testing::TestError) {
2844
    {
2845
        let mut a = testResolver();
2846
        let program = "fn f(ptr: &mut i32) { set *ptr = 42; }";
2847
        let result = try resolveProgramStr(&mut a, program);
2848
        try expectNoErrors(&result);
2849
    } {
2850
        let mut a = testResolver();
2851
        let program = "fn f(ptr: &i32) { set *ptr = 42; }";
2852
        let result = try resolveProgramStr(&mut a, program);
2853
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2854
    } {
2855
        let mut a = testResolver();
2856
        let program = "fn f(input: *i32) { let mut ptr: *i32 = input; set *ptr = 42; }";
2857
        let result = try resolveProgramStr(&mut a, program);
2858
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
2859
    } {
2860
        let mut a = testResolver();
2861
        let program = "fn f(ptr: &mut u8) { set *ptr = 255; }";
2862
        let result = try resolveProgramStr(&mut a, program);
2863
        try expectNoErrors(&result);
2864
    }
2865
}
2866
2867
// Type inference tests ///////////////////////////////////////////////////////
2868
2869
@test fn testResolveBasicTypeInference() throws (testing::TestError) {
2870
    {
2871
        // Boolean literals are unambiguous.
2872
        let mut a = testResolver();
2873
        let result = try resolveProgramStr(&mut a, "let x = true; x;");
2874
        try expectNoErrors(&result);
2875
2876
        let xStmt = try parser::tests::getBlockLastStmt(result.root);
2877
        try expectExprStmtType(&a, xStmt, super::Type::Bool);
2878
    } {
2879
        // Integer literals are ambiguous.
2880
        let mut a = testResolver();
2881
        let result = try resolveProgramStr(&mut a, "let x = 34;");
2882
        try expectErrorKind(&result, super::ErrorKind::CannotInferType);
2883
    }
2884
}
2885
2886
// Union tests /////////////////////////////////////////////////////////////////
2887
2888
@test fn testResolveUnionVariantWithoutPayload() throws (testing::TestError) {
2889
    let mut a = testResolver();
2890
    let program = "union Status { Ok, Error } Status::Ok;";
2891
    let result = try resolveProgramStr(&mut a, program);
2892
2893
    let ty = try getTypeInScopeOf(&a, result.root, "Status");
2894
    let case super::NominalType::Union(unionType) = *ty
2895
        else throw testing::TestError::Failed;
2896
    try testing::expect(unionType.variants.len == 2);
2897
    try testing::expect(mem::eq(unionType.variants[0].name, "Ok"));
2898
    try testing::expect(mem::eq(unionType.variants[1].name, "Error"));
2899
    if getUnionVariantPayload(ty, "Ok") <> super::Type::Void {
2900
        throw testing::TestError::Failed;
2901
    }
2902
    let stmt = try getBlockStmt(result.root, 1);
2903
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2904
    try expectNoErrors(&result);
2905
}
2906
2907
@test fn testResolveUnionVariantWithPayload() throws (testing::TestError) {
2908
    let mut a = testResolver();
2909
    let program = "union R { Ok(i32), Err(bool) } R::Ok(42);";
2910
    let result = try resolveProgramStr(&mut a, program);
2911
    try expectNoErrors(&result);
2912
2913
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2914
2915
    let okPayload = getUnionVariantPayload(ty, "Ok");
2916
    try testing::expect(okPayload == super::Type::I32);
2917
2918
    let errPayload = getUnionVariantPayload(ty, "Err");
2919
    try testing::expect(errPayload == super::Type::Bool);
2920
2921
    let stmt = try getBlockStmt(result.root, 1);
2922
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2923
2924
    // TODO: Test payload type.
2925
}
2926
2927
@test fn testResolveUnionVariantWithoutPayloadExplicitDiscriminant() throws (testing::TestError) {
2928
    let mut a = testResolver();
2929
    let program = "union R { Ok = 7, Err = 11 } R::Ok;";
2930
    let result = try resolveProgramStr(&mut a, program);
2931
    try expectNoErrors(&result);
2932
2933
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2934
    let stmt = try getBlockStmt(result.root, 1);
2935
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2936
}
2937
2938
@test fn testResolveUnionVariantPayloadTypeMismatch() throws (testing::TestError) {
2939
    let mut a = testResolver();
2940
    let program = "union R { Ok(i32), Error(bool) } R::Ok(true);";
2941
    let result = try resolveProgramStr(&mut a, program);
2942
    let err = try expectError(&result);
2943
    try expectTypeMismatch(err, super::Type::I32, super::Type::Bool);
2944
2945
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2946
    let payload = getUnionVariantPayload(ty, "Ok");
2947
    try testing::expect(payload == super::Type::I32);
2948
2949
    let errNode = err.node
2950
        else throw testing::TestError::Failed;
2951
    let case ast::NodeValue::Bool(_) = errNode.value
2952
        else throw testing::TestError::Failed;
2953
}
2954
2955
@test fn testResolveUnionVariantUnexpectedPayload() throws (testing::TestError) {
2956
    let mut a = testResolver();
2957
    let program = "union Status { Ok, Error } Status::Ok(42);";
2958
    let result = try resolveProgramStr(&mut a, program);
2959
    let err = try expectError(&result);
2960
2961
    let case super::ErrorKind::UnionVariantPayloadUnexpected(_) = err.kind
2962
        else throw testing::TestError::Failed;
2963
    let node = err.node
2964
        else throw testing::TestError::Failed;
2965
    let case ast::NodeValue::Call(_) = node.value
2966
        else throw testing::TestError::Failed;
2967
}
2968
2969
@test fn testResolveUnionVariantUnknown() throws (testing::TestError) {
2970
    let mut a = testResolver();
2971
    let program = "union Status { Ok, Error } Status::Unknown;";
2972
    let result = try resolveProgramStr(&mut a, program);
2973
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
2974
}
2975
2976
@test fn testResolveScopeAccessUndefinedType() throws (testing::TestError) {
2977
    let mut a = testResolver();
2978
    let result = try resolveProgramStr(&mut a, "Unknown::X;");
2979
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Unknown"));
2980
}
2981
2982
@test fn testResolveUnionVariantVoidPayload() throws (testing::TestError) {
2983
    let mut a = testResolver();
2984
    let program = "union R { Success(i32), Pending } R::Pending;";
2985
    let result = try resolveProgramStr(&mut a, program);
2986
    try expectNoErrors(&result);
2987
2988
    let ty = try getTypeInScopeOf(&a, result.root, "R");
2989
    let payload = getUnionVariantPayload(ty, "Pending");
2990
    try testing::expect(payload == super::Type::Void);
2991
2992
    let stmt = try getBlockStmt(result.root, 1);
2993
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
2994
}
2995
2996
@test fn testResolveUnionVariantRecordPayload() throws (testing::TestError) {
2997
    let mut a = testResolver();
2998
    let program = "record P { x: i32, y: i32 } union S { Point(P), Num(u32) } S::Point(P { x: 10, y: 20 });";
2999
    let result = try resolveProgramStr(&mut a, program);
3000
    try expectNoErrors(&result);
3001
3002
    let ty = try getTypeInScopeOf(&a, result.root, "S");
3003
    let stmt = try getBlockStmt(result.root, 2);
3004
    try expectExprStmtType(&a, stmt, super::Type::Nominal(ty));
3005
}
3006
3007
@test fn testResolveBuiltinSizeOf() throws (testing::TestError) {
3008
    try resolveAndExpectConstExpr("@sizeOf(u8)", 1);
3009
    try resolveAndExpectConstExpr("@sizeOf(u16)", 2);
3010
    try resolveAndExpectConstExpr("@sizeOf(u32)", 4);
3011
    try resolveAndExpectConstExpr("@sizeOf(i32)", 4);
3012
    try resolveAndExpectConstExpr("@sizeOf(bool)", 1);
3013
    try resolveAndExpectConstExpr("@sizeOf(*u32)", 8);
3014
    try resolveAndExpectConstExpr("@sizeOf([u8; 10])", 10);
3015
    try resolveAndExpectConstExpr("@sizeOf(*[u32])", 16);
3016
    try resolveAndExpectConstExpr("@sizeOf(?u8)", 2);
3017
    try resolveAndExpectConstExpr("@sizeOf(?u16)", 4);
3018
    try resolveAndExpectConstExpr("@sizeOf(?u32)", 8);
3019
    try resolveAndExpectConstExpr("@sizeOf(*opaque)", 8);
3020
    try resolveAndExpectConstStmt("record T { x: u8 } @sizeOf(T);", 1);
3021
    try resolveAndExpectConstStmt("record T { x: i32 } @sizeOf(T);", 4);
3022
    try resolveAndExpectConstStmt("record T { x: i32, y: i8 } @sizeOf(T);", 8);
3023
    try resolveAndExpectConstStmt("record T { x: i8, y: i32 } @sizeOf(T);", 8);
3024
    try resolveAndExpectConstStmt("record T { x: i8, y: i32 } @sizeOf(T);", 8);
3025
    try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @sizeOf(T);", 8);
3026
    try resolveAndExpectConstStmt("record T { x: u8, y: u32, z: u8 }; @sizeOf(T);", 12);
3027
    try resolveAndExpectConstStmt("union T { A, B, C }; @sizeOf(T);", 1);
3028
    try resolveAndExpectConstStmt("union T { A, B(u32), C }; @sizeOf(T);", 8);
3029
    try resolveAndExpectConstStmt("union T { A, B(u16), C }; @sizeOf(T);", 4);
3030
    try resolveAndExpectConstStmt("union T { A(u32), B(u16), C(u16) }; @sizeOf(T);", 8);
3031
    try resolveAndExpectConstStmt("union T { A(u32), B(u16), C([u8; 16]) }; @sizeOf(T);", 20);
3032
}
3033
3034
@test fn testResolveBuiltinAlignOf() throws (testing::TestError) {
3035
    try resolveAndExpectConstExpr("@alignOf(u8)", 1);
3036
    try resolveAndExpectConstExpr("@alignOf(u16)", 2);
3037
    try resolveAndExpectConstExpr("@alignOf(u32)", 4);
3038
    try resolveAndExpectConstExpr("@alignOf(i32)", 4);
3039
    try resolveAndExpectConstExpr("@alignOf(bool)", 1);
3040
    try resolveAndExpectConstExpr("@alignOf(*u8)", 8);
3041
    try resolveAndExpectConstExpr("@alignOf(*u16)", 8);
3042
    try resolveAndExpectConstExpr("@alignOf(*u32)", 8);
3043
    try resolveAndExpectConstExpr("@alignOf(*opaque)", 8);
3044
    try resolveAndExpectConstExpr("@alignOf([u8; 8])", 1);
3045
    try resolveAndExpectConstExpr("@alignOf([u16; 8])", 2);
3046
    try resolveAndExpectConstExpr("@alignOf([u32; 8])", 4);
3047
    try resolveAndExpectConstExpr("@alignOf(*[u32])", 8);
3048
    try resolveAndExpectConstExpr("@alignOf(?u8)", 1);
3049
    try resolveAndExpectConstExpr("@alignOf(?u16)", 2);
3050
    try resolveAndExpectConstExpr("@alignOf(?u32)", 4);
3051
    try resolveAndExpectConstStmt("record T { x: u8, y: u16 }; @alignOf(T);", 2);
3052
    try resolveAndExpectConstStmt("record T { x: u8, y: u32, z: u8 }; @alignOf(T);", 4);
3053
    try resolveAndExpectConstStmt("record T { x: u32, y: u8, z: u8 }; @alignOf(T);", 4);
3054
    try resolveAndExpectConstStmt("union T { A, B, C }; @alignOf(T);", 1);
3055
    try resolveAndExpectConstStmt("union T { A, B(u32), C }; @alignOf(T);", 4);
3056
}
3057
3058
@test fn testResolveBuiltinSizeOfRecord() throws (testing::TestError) {
3059
    let mut a = testResolver();
3060
    let program = "record T { x: u8, y: u32 } @sizeOf(T);";
3061
    let result = try resolveProgramStr(&mut a, program);
3062
    try expectNoErrors(&result);
3063
3064
    let stmt = try getBlockStmt(result.root, 1);
3065
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3066
    try expectConstInt(&a, expr, 8);
3067
}
3068
3069
@test fn testResolveBuiltinSizeOfUnion() throws (testing::TestError) {
3070
    let mut a = testResolver();
3071
    let program = "union Result { Ok(u32), Err(u8) } @sizeOf(Result);";
3072
    let result = try resolveProgramStr(&mut a, program);
3073
    try expectNoErrors(&result);
3074
3075
    let stmt = try getBlockStmt(result.root, 1);
3076
    let expr = try expectExprStmtType(&a, stmt, super::Type::U32);
3077
    try expectConstInt(&a, expr, 8);
3078
}
3079
3080
@test fn testResolveAlignAnnotation() throws (testing::TestError) {
3081
    {
3082
        let mut a = testResolver();
3083
        let result = try resolveBlockStr(&mut a, "let x: u8 align(8) = 0;");
3084
        try expectNoErrors(&result);
3085
3086
        let stmt = try getBlockStmt(result.root, 0);
3087
        let sym = super::symbolFor(&a, stmt)
3088
            else throw testing::TestError::Failed;
3089
        let case super::SymbolData::Value { type: valType, .. } = sym.data
3090
            else throw testing::TestError::Failed;
3091
        let layout = super::getLayout(&a, sym.node, valType);
3092
        try testing::expect(layout.alignment == 8);
3093
    } {
3094
        let mut a = testResolver();
3095
        let result = try resolveProgramStr(&mut a, "let x: u32 align(3) = 0;");
3096
        let err = try expectError(&result);
3097
        let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3098
            else throw testing::TestError::Failed;
3099
        try testing::expect(val == 3);
3100
    } {
3101
        let mut a = testResolver();
3102
        let result = try resolveProgramStr(&mut a, "let x: u32 align(7) = 0;");
3103
        let err = try expectError(&result);
3104
        let case super::ErrorKind::InvalidAlignmentValue(val) = err.kind
3105
            else throw testing::TestError::Failed;
3106
        try testing::expect(val == 7);
3107
    }
3108
}
3109
3110
@test fn testResolveVoidAssignmentError() throws (testing::TestError) {
3111
    {
3112
        let mut a = testResolver();
3113
        let program = "fn voidFn() {} let _ = voidFn();";
3114
        let result = try resolveProgramStr(&mut a, program);
3115
        try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3116
    } {
3117
        let mut a = testResolver();
3118
        let program = "fn voidFn() {} let x = voidFn();";
3119
        let result = try resolveProgramStr(&mut a, program);
3120
        try expectErrorKind(&result, super::ErrorKind::CannotAssignVoid);
3121
    }
3122
}
3123
3124
//
3125
// Module Declaration Tests
3126
//
3127
3128
@test fn testResolveEmptyMod() throws (testing::TestError) {
3129
    let mut a = testResolver();
3130
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3131
    let mut graph = &mut MODULE_GRAPH;
3132
3133
    let rootId = try registerModule(graph, nil, "root", "mod child;", &mut arena);
3134
    let childId = try registerModule(graph, rootId, "child", "{}", &mut arena);
3135
    let result = try resolveModuleTree(&mut a, rootId);
3136
    try expectNoErrors(&result);
3137
}
3138
3139
@test fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) {
3140
    let mut a = testResolver();
3141
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3142
3143
    // Register root and util modules.
3144
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena);
3145
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena);
3146
3147
    // Resolve should fail: the parent module is not in scope.
3148
    let result = try resolveModuleTree(&mut a, rootId);
3149
    let err = try expectError(&result);
3150
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3151
        else throw testing::TestError::Failed;
3152
    try testing::expect(mem::eq(name, "helper"));
3153
}
3154
3155
@test fn testResolveModuleAccessPrivateSubModule() throws (testing::TestError) {
3156
    let mut a = testResolver();
3157
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3158
3159
    // Register root and util modules.
3160
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena);
3161
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena);
3162
3163
    // Resolve should succeed: parent can access child.
3164
    let result = try resolveModuleTree(&mut a, rootId);
3165
    try expectNoErrors(&result);
3166
}
3167
3168
@test fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) {
3169
    let mut a = testResolver();
3170
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3171
3172
    // Register root with two sibling modules.
3173
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3174
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena);
3175
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
3176
3177
    // Resolve should fail: siblings can't access each other directly.
3178
    let result = try resolveModuleTree(&mut a, rootId);
3179
    let err = try expectError(&result);
3180
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3181
        else throw testing::TestError::Failed;
3182
    try testing::expect(mem::eq(name, "patrick"));
3183
}
3184
3185
@test fn testResolveSiblingModulesViaRoot() throws (testing::TestError) {
3186
    let mut a = testResolver();
3187
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3188
3189
    // Register root with two sibling modules.
3190
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena);
3191
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena);
3192
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena);
3193
3194
    // Resolve should succeed: siblings can access each other via root.
3195
    let result = try resolveModuleTree(&mut a, rootId);
3196
    try expectNoErrors(&result);
3197
}
3198
3199
@test fn testResolveModuleMutualRecursion() throws (testing::TestError) {
3200
    let mut a = testResolver();
3201
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3202
3203
    // Register root with two sibling modules that call each other.
3204
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena);
3205
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena);
3206
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena);
3207
3208
    // Resolve should succeed: cyclic use is allowed.
3209
    let result = try resolveModuleTree(&mut a, rootId);
3210
    try expectNoErrors(&result);
3211
}
3212
3213
@test fn testResolveAccessModuleType() throws (testing::TestError) {
3214
    let mut a = testResolver();
3215
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3216
3217
    // Register root with types module containing a record.
3218
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3219
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena);
3220
    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);
3221
3222
    // Resolve should succeed: types can be accessed.
3223
    let result = try resolveModuleTree(&mut a, rootId);
3224
    try expectNoErrors(&result);
3225
}
3226
3227
@test fn testResolveAccessModuleConstant() throws (testing::TestError) {
3228
    let mut a = testResolver();
3229
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3230
3231
    // Register root with constants module.
3232
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3233
    let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant MAX_SIZE: i32 = 100;", &mut arena);
3234
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena);
3235
3236
    // Resolve should succeed: constants can be accessed.
3237
    let result = try resolveModuleTree(&mut a, rootId);
3238
    try expectNoErrors(&result);
3239
}
3240
3241
@test fn testResolveRootSymbolMustBeImported() throws (testing::TestError) {
3242
    let mut a = testResolver();
3243
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3244
3245
    // Register deeply nested modules: `root::app::services::auth`.
3246
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena);
3247
    let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena);
3248
3249
    // Resolve should fail: the `root` module must be imported.
3250
    let result = try resolveModuleTree(&mut a, rootId);
3251
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root"));
3252
}
3253
3254
@test fn testResolveUseImportsNestedSymbol() throws (testing::TestError) {
3255
    let mut a = testResolver();
3256
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3257
3258
    // Register deeply nested modules: `root::app::services::auth`.
3259
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena);
3260
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena);
3261
    let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena);
3262
    let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena);
3263
    let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena);
3264
    let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena);
3265
3266
    // Resolve should succeed: use imports the module symbol.
3267
    let result = try resolveModuleTree(&mut a, rootId);
3268
    try expectNoErrors(&result);
3269
}
3270
3271
@test fn testResolveUseNonExistentModule() throws (testing::TestError) {
3272
    let mut a = testResolver();
3273
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3274
3275
    // Register root with app trying to use a non-existent module.
3276
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod app;", &mut arena);
3277
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::unknown;", &mut arena);
3278
3279
    // Resolve should fail: module doesn't exist.
3280
    let result = try resolveModuleTree(&mut a, rootId);
3281
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("unknown"));
3282
}
3283
3284
@test fn testResolveUsePrivateFn() throws (testing::TestError) {
3285
    let mut a = testResolver();
3286
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3287
3288
    // Register root with util module containing a private function.
3289
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena);
3290
    let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena);
3291
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena);
3292
3293
    // Resolve should fail: function is not public.
3294
    let result = try resolveModuleTree(&mut a, rootId);
3295
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3296
}
3297
3298
@test fn testResolveUsePrivateMod() throws (testing::TestError) {
3299
    let mut a = testResolver();
3300
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3301
3302
    // Register root with public and private child modules.
3303
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; mod private;", &mut arena);
3304
    let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "private", "{}", &mut arena);
3305
    let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::private;", &mut arena);
3306
3307
    // Resolve should fail: module is not public.
3308
    let result = try resolveModuleTree(&mut a, rootId);
3309
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3310
}
3311
3312
@test fn testResolveUsePublicMod() throws (testing::TestError) {
3313
    let mut a = testResolver();
3314
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3315
3316
    // Register root with public and private child modules.
3317
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena);
3318
    let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena);
3319
    let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena);
3320
3321
    // Resolve should succeed: module is public.
3322
    let result = try resolveModuleTree(&mut a, rootId);
3323
    try expectNoErrors(&result);
3324
}
3325
3326
@test fn testResolveUseNonPublicType() throws (testing::TestError) {
3327
    let mut a = testResolver();
3328
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3329
3330
    // Register root with types module containing a private record.
3331
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3332
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena);
3333
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena);
3334
3335
    // Resolve should fail: record is not public.
3336
    let result = try resolveModuleTree(&mut a, rootId);
3337
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("Priv"));
3338
}
3339
3340
@test fn testResolveImportPublicType() throws (testing::TestError) {
3341
    let mut a = testResolver();
3342
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3343
3344
    // Register root with types module containing a public record.
3345
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena);
3346
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena);
3347
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena);
3348
3349
    // Resolve should succeed: record is public.
3350
    let result = try resolveModuleTree(&mut a, rootId);
3351
    try expectNoErrors(&result);
3352
}
3353
3354
@test fn testResolveUseNonPublicStatic() throws (testing::TestError) {
3355
    let mut a = testResolver();
3356
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3357
3358
    // Register root with statics module containing a private static.
3359
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3360
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena);
3361
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena);
3362
3363
    // Resolve should fail: static is not public.
3364
    let result = try resolveModuleTree(&mut a, rootId);
3365
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("PRIVATE"));
3366
}
3367
3368
@test fn testResolveImportPublicStatic() throws (testing::TestError) {
3369
    let mut a = testResolver();
3370
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3371
3372
    // Register root with statics module containing a public static.
3373
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3374
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena);
3375
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena);
3376
3377
    // Resolve should succeed: static is public.
3378
    let result = try resolveModuleTree(&mut a, rootId);
3379
    try expectNoErrors(&result);
3380
}
3381
3382
@test fn testResolveAccessSuper() throws (testing::TestError) {
3383
    {
3384
        let mut a = testResolver();
3385
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3386
3387
        // Test function access.
3388
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena);
3389
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena);
3390
        let result = try resolveModuleTree(&mut a, rootId);
3391
        try expectNoErrors(&result);
3392
    } {
3393
        let mut a = testResolver();
3394
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3395
3396
        // Test type access.
3397
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena);
3398
        let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena);
3399
        let result = try resolveModuleTree(&mut a, rootId);
3400
        try expectNoErrors(&result);
3401
    }
3402
}
3403
3404
/// Test nested super access to union variants (e.g. `super::E::A`).
3405
@test fn testResolveSuperUnionVariant() throws (testing::TestError) {
3406
    let mut a = testResolver();
3407
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3408
3409
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena);
3410
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c",
3411
        "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }",
3412
        &mut arena);
3413
    let result = try resolveModuleTree(&mut a, rootId);
3414
    try expectNoErrors(&result);
3415
}
3416
3417
@test fn testResolveUseSuper() throws (testing::TestError) {
3418
    let mut a = testResolver();
3419
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3420
3421
    // Register root with a function, and a child module that uses super to access it.
3422
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena);
3423
    let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena);
3424
    let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena);
3425
3426
    // Resolve should succeed - super allows accessing parent module.
3427
    let result = try resolveModuleTree(&mut a, rootId);
3428
    try expectNoErrors(&result);
3429
}
3430
3431
@test fn testResolveModNotFound() throws (testing::TestError) {
3432
    let mut a = testResolver();
3433
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3434
3435
    // Register root that declares a module that doesn't exist.
3436
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod unknown;", &mut arena);
3437
3438
    // Resolve should fail: module doesn't exist.
3439
    let result = try resolveModuleTree(&mut a, rootId);
3440
    let err = try expectError(&result);
3441
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3442
        else throw testing::TestError::Failed;
3443
    try testing::expect(mem::eq(name, "unknown"));
3444
}
3445
3446
@test fn testResolveDuplicateSubModule() throws (testing::TestError) {
3447
    let mut a = testResolver();
3448
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3449
3450
    // Register root that declares a module twice.
3451
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; mod child;", &mut arena);
3452
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
3453
3454
    // Resolve should fail: can't declare the same module twice.
3455
    let result = try resolveModuleTree(&mut a, rootId);
3456
    let err = try expectError(&result);
3457
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
3458
}
3459
3460
@test fn testResolveUseSubModule() throws (testing::TestError) {
3461
    let mut a = testResolver();
3462
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3463
3464
    // Register root that declares and imports the same module.
3465
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; use child;", &mut arena);
3466
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "{}", &mut arena);
3467
3468
    // Resolve should fail: Both `mod` and `use` are trying to create the same binding.
3469
    let result = try resolveModuleTree(&mut a, rootId);
3470
    let err = try expectError(&result);
3471
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("child"));
3472
}
3473
3474
@test fn testResolveDuplicateUse() throws (testing::TestError) {
3475
    let mut a = testResolver();
3476
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3477
3478
    // Register a module that imports the same module twice.
3479
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child", &mut arena);
3480
    let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "use root; use root;", &mut arena);
3481
3482
    // Resolve should fail.
3483
    let result = try resolveModuleTree(&mut a, rootId);
3484
    let err = try expectError(&result);
3485
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("root"));
3486
}
3487
3488
/// Test that opaque pointers are allowed in record fields.
3489
@test fn testOpaquePointerInRecordField() throws (testing::TestError) {
3490
    let mut a = testResolver();
3491
    let result = try resolveProgramStr(&mut a, "record T { x: *opaque }");
3492
    try expectNoErrors(&result);
3493
}
3494
3495
/// You cannot use `@sizeOf` or `@alignOf` on opaque type.
3496
@test fn testOpaqueTypeNoSizeOfAlignOf() throws (testing::TestError) {
3497
    let mut a = testResolver();
3498
3499
    let result1 = try resolveExprStr(&mut a, "@sizeOf(opaque)");
3500
    let err1 = try expectError(&result1);
3501
    try expectErrorKind(&result1, super::ErrorKind::OpaqueTypeNotAllowed);
3502
3503
    let result2 = try resolveExprStr(&mut a, "@alignOf(opaque)");
3504
    let err2 = try expectError(&result2);
3505
    try expectErrorKind(&result2, super::ErrorKind::OpaqueTypeNotAllowed);
3506
}
3507
3508
/// Test that immutable slice/pointer parameters cannot be borrowed mutably.
3509
@test fn testMutableBorrowFromImmutablePointer() throws (testing::TestError) {
3510
    let mut a = testResolver();
3511
    let program = "fn f(p: *i32) { let x = &mut *p; }";
3512
    let result = try resolveProgramStr(&mut a, program);
3513
    let err = try expectError(&result);
3514
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3515
}
3516
3517
/// Test that immutable slice parameters cannot be borrowed mutably.
3518
@test fn testMutableBorrowFromImmutableSlice() throws (testing::TestError) {
3519
    let mut a = testResolver();
3520
    let program = "fn f(s: *[i32]) { let x: *mut i32 = &mut s[0]; }";
3521
    let result = try resolveProgramStr(&mut a, program);
3522
    let err = try expectError(&result);
3523
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3524
}
3525
3526
/// Test that mutable pointer parameters can be borrowed mutably.
3527
@test fn testMutableBorrowFromMutablePointer() throws (testing::TestError) {
3528
    let mut a = testResolver();
3529
    let program = "fn borrow(value: &mut i32) {} fn f(p: &mut i32) { borrow(&mut *p); }";
3530
    let result = try resolveProgramStr(&mut a, program);
3531
    try expectNoErrors(&result);
3532
}
3533
3534
/// Test that mutable slice parameters can be borrowed mutably.
3535
@test fn testMutableBorrowFromMutableSlice() throws (testing::TestError) {
3536
    let mut a = testResolver();
3537
    let program = "fn borrow(value: &mut i32) {} fn f(s: &mut [i32]) { borrow(&mut s[0]); }";
3538
    let result = try resolveProgramStr(&mut a, program);
3539
    try expectNoErrors(&result);
3540
}
3541
3542
/// Test borrowing mutably from a field access on a call returning `*mut`.
3543
@test fn testMutableBorrowFromCallReturningMutablePointer() throws (testing::TestError) {
3544
    let mut a = testResolver();
3545
    let program = "record Box { x: i32 } fn idBox(b: *mut Box) -> *mut Box { return b; } unsafe fn f() -> *mut i32 { let mut b = Box { x: 1 }; let px: *mut i32 = &mut idBox(&mut b).x; return px; }";
3546
    let result = try resolveProgramStr(&mut a, program);
3547
    try expectNoErrors(&result);
3548
}
3549
3550
/// Test that calls returning immutable pointers cannot be mutably borrowed.
3551
@test fn testMutableBorrowFromCallReturningImmutablePointer() throws (testing::TestError) {
3552
    let mut a = testResolver();
3553
    let program = "record Box { x: i32 } fn idBox(b: *Box) -> *Box { return b; } fn borrow(px: &mut i32) {} fn f(b: *Box) { borrow(&mut idBox(b).x); }";
3554
    let result = try resolveProgramStr(&mut a, program);
3555
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3556
}
3557
3558
/// Test borrowing mutably from a public static through scope access.
3559
@test fn testMutableBorrowFromScopeAccessStatic() throws (testing::TestError) {
3560
    let mut a = testResolver();
3561
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3562
3563
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena);
3564
    let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static COUNTER: i32 = 0;", &mut arena);
3565
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn assign(p: &mut i32) { set *p = 7; } fn main() { assign(&mut statics::COUNTER); }", &mut arena);
3566
3567
    let result = try resolveModuleTree(&mut a, rootId);
3568
    try expectNoErrors(&result);
3569
}
3570
3571
/// Test that constants through scope access cannot be mutably borrowed.
3572
@test fn testMutableBorrowFromScopeAccessConstant() throws (testing::TestError) {
3573
    let mut a = testResolver();
3574
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3575
3576
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
3577
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant LIMIT: i32 = 7;", &mut arena);
3578
    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);
3579
3580
    let result = try resolveModuleTree(&mut a, rootId);
3581
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3582
}
3583
3584
/// Test that mutable bindings of immutable pointers cannot borrow mutably through the pointer.
3585
@test fn testMutableBorrowFromMutableBindingOfPointer() throws (testing::TestError) {
3586
    let mut a = testResolver();
3587
    let program = "fn borrow(value: &mut i32) {} fn f(input: *i32) { let mut p: *i32 = input; borrow(&mut *p); }";
3588
    let result = try resolveProgramStr(&mut a, program);
3589
    let err = try expectError(&result);
3590
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3591
}
3592
3593
/// Test that mutable pointer to immutable slice cannot be assigned through index.
3594
/// This tests the case where we have `*mut *[T]`; the outer pointer is mutable but
3595
/// the inner slice is immutable, so we shouldn't be able to mutate the elements.
3596
@test fn testAssignThroughMutablePointerToImmutableSlice() throws (testing::TestError) {
3597
    let mut a = testResolver();
3598
    let program = "fn f(slice: *[i32]) { let p: *mut *[i32] = &mut slice; set p[0] = 1; }";
3599
    let result = try resolveProgramStr(&mut a, program);
3600
3601
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3602
}
3603
3604
/// Test that mutable slice parameters can be assigned through index.
3605
@test fn testAssignThroughMutableSliceParam() throws (testing::TestError) {
3606
    {
3607
        // Mutable slice param: direct assignment should work
3608
        let mut a = testResolver();
3609
        let program = "fn f(slice: &mut [i32]) { set slice[0] = 1; }";
3610
        let result = try resolveProgramStr(&mut a, program);
3611
        try expectNoErrors(&result);
3612
    } {
3613
        // Immutable slice param: direct assignment should fail
3614
        let mut a = testResolver();
3615
        let program = "fn f(slice: &[i32]) { set slice[0] = 1; }";
3616
        let result = try resolveProgramStr(&mut a, program);
3617
        try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3618
    }
3619
}
3620
3621
/// Test range end type coercion with assignable types.
3622
@test fn testRangeEndTypeCoercion() throws (testing::TestError) {
3623
    {
3624
        let mut a = testResolver();
3625
        let program = "fn f(end: u32) { for i in 0..end {} }";
3626
        let result = try resolveProgramStr(&mut a, program);
3627
        try expectNoErrors(&result);
3628
    } {
3629
        let mut a = testResolver();
3630
        let program = "fn f(start: u32) { for i in start..9 {} }";
3631
        let result = try resolveProgramStr(&mut a, program);
3632
        try expectNoErrors(&result);
3633
    }
3634
}
3635
3636
/// Mixed-width range bounds require an explicit cast.
3637
@test fn testRangeEndTypeSubType() throws (testing::TestError) {
3638
    {
3639
        let mut a = testResolver();
3640
        let program = "fn f(start: i8, end: u32) { for i in start..end {} }";
3641
        let result = try resolveProgramStr(&mut a, program);
3642
        let err = try expectError(&result);
3643
        try expectTypeMismatch(err, super::Type::I8, super::Type::U32);
3644
    } {
3645
        let mut a = testResolver();
3646
        let program = "fn f(start: i8, end: u32) { for i in (start as u32)..end {} }";
3647
        let result = try resolveProgramStr(&mut a, program);
3648
        try expectNoErrors(&result);
3649
    }
3650
}
3651
3652
/// Test that try-catch expressions in statement context accept mismatched types.
3653
@test fn testTryCatchInStatementContextTypeMismatchOk() throws (testing::TestError) {
3654
    let mut a = testResolver();
3655
    let program = "fn f() { try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3656
    let result = try resolveProgramStr(&mut a, program);
3657
    try expectNoErrors(&result);
3658
}
3659
3660
/// Test that try-catch blocks in value context require divergence or void.
3661
@test fn testTryCatchInValueContextTypeMismatch() throws (testing::TestError) {
3662
    let mut a = testResolver();
3663
    let program = "fn f() -> bool { return try g() catch {}; } fn g() -> bool throws (i32) { panic; }";
3664
    let result = try resolveProgramStr(&mut a, program);
3665
    let err = try expectError(&result);
3666
    try expectTypeMismatch(err, super::Type::Bool, super::Type::Void);
3667
}
3668
3669
/// Test that try-catch blocks in value context work when they diverge.
3670
@test fn testTryCatchInValueContextDiverges() throws (testing::TestError) {
3671
    let mut a = testResolver();
3672
    let program = "fn f() -> bool { return try g() catch { return false; }; } fn g() -> bool throws (i32) { panic; }";
3673
    let result = try resolveProgramStr(&mut a, program);
3674
    try expectNoErrors(&result);
3675
}
3676
3677
/// Test that `try?` lifts result type to optional.
3678
@test fn testTryOptionalLiftsToOptional() throws (testing::TestError) {
3679
    let mut a = testResolver();
3680
    let program = "record S {} fn f() -> ?*S { return try? g(); } fn g() -> *S throws (i32) { panic; }";
3681
    let result = try resolveProgramStr(&mut a, program);
3682
    try expectNoErrors(&result);
3683
}
3684
3685
/// Test that record fields can be assigned if the record binding is mutable.
3686
@test fn testMutableAssignToMutableRecordBinding() throws (testing::TestError) {
3687
    let mut a = testResolver();
3688
    let program = "record S { x: i32 } fn f() { let mut s = S { x: 1 }; set s.x = 2; }";
3689
    let result = try resolveProgramStr(&mut a, program);
3690
    try expectNoErrors(&result);
3691
}
3692
3693
/// Test that record fields cannot be assigned if the record binding is immutable.
3694
@test fn testMutableAssignToImmutableRecordBinding() throws (testing::TestError) {
3695
    let mut a = testResolver();
3696
    let program = "record S { x: i32 } fn f() { let s = S { x: 1 }; set s.x = 2; }";
3697
    let result = try resolveProgramStr(&mut a, program);
3698
    let err = try expectError(&result);
3699
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3700
}
3701
3702
/// Test that record fields can be assigned through a mutable pointer.
3703
@test fn testMutableAssignToMutablePointerToRecord() throws (testing::TestError) {
3704
    let mut a = testResolver();
3705
    let program = "record S { x: i32 } fn f(p: &mut S) { set p.x = 2; }";
3706
    let result = try resolveProgramStr(&mut a, program);
3707
    try expectNoErrors(&result);
3708
}
3709
3710
/// Test that record fields cannot be assigned through an immutable pointer.
3711
@test fn testMutableAssignToImmutablePointerToRecord() throws (testing::TestError) {
3712
    let mut a = testResolver();
3713
    let program = "record S { x: i32 } fn f(p: *S) { set p.x = 2; }";
3714
    let result = try resolveProgramStr(&mut a, program);
3715
    let err = try expectError(&result);
3716
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
3717
}
3718
3719
// Opaque pointer tests.
3720
3721
/// You can assign any pointer (*T) to an opaque pointer (*opaque) without a cast.
3722
@test fn testOpaquePointerAutoCoercion() throws (testing::TestError) {
3723
    let mut a = testResolver();
3724
    let result = try resolveProgramStr(&mut a, "fn f(ptr: *i32) -> *opaque { return ptr; }");
3725
    try expectNoErrors(&result);
3726
}
3727
3728
/// You cannot assign an opaque pointer to a non-opaque pointer without a cast.
3729
@test fn testOpaquePointerNoReverseCoercion() throws (testing::TestError) {
3730
    let mut a = testResolver();
3731
    let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let ptr: *i32 = o; }");
3732
    let err = try expectError(&result);
3733
    let case super::ErrorKind::TypeMismatch(mismatch) = err.kind
3734
        else throw testing::TestError::Failed;
3735
    let case super::Type::Pointer { target: expectedTarget, .. } = mismatch.expected
3736
        else throw testing::TestError::Failed;
3737
    let case super::Type::Pointer { target: actualTarget, .. } = mismatch.actual
3738
        else throw testing::TestError::Failed;
3739
3740
    try testing::expect(*expectedTarget == super::Type::I32);
3741
    try testing::expect(*actualTarget == super::Type::Opaque);
3742
}
3743
3744
/// You cannot have a value of type `opaque` (function parameter).
3745
@test fn testOpaqueValue() throws (testing::TestError) {
3746
    {
3747
        let mut a = testResolver();
3748
        let result = try resolveProgramStr(&mut a, "fn f(x: opaque) {}");
3749
        let err = try expectError(&result);
3750
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3751
    } {
3752
        let mut a = testResolver();
3753
        let result = try resolveProgramStr(&mut a, "fn f() { unsafe { let x: opaque = undefined; } }");
3754
        let err = try expectError(&result);
3755
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3756
    } {
3757
        let mut a = testResolver();
3758
        let result = try resolveProgramStr(&mut a, "record R { x: opaque }");
3759
        let err = try expectError(&result);
3760
        try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed);
3761
    }
3762
}
3763
3764
/// You cannot dereference an opaque pointer, you have to cast it first.
3765
@test fn testOpaquePointerNoDereference() throws (testing::TestError) {
3766
    let mut a = testResolver();
3767
    let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = *o; }");
3768
    let err = try expectError(&result);
3769
    try expectErrorKind(&result, super::ErrorKind::OpaqueTypeDeref);
3770
}
3771
3772
/// Test that you can dereference after casting.
3773
@test fn testOpaquePointerDereferenceAfterCast() throws (testing::TestError) {
3774
    let mut a = testResolver();
3775
    let result = try resolveProgramStr(&mut a, "unsafe fn f(o: *opaque) -> *i32 { let ptr = o as *i32; let x = *ptr; return ptr; }");
3776
    try expectNoErrors(&result);
3777
}
3778
3779
/// You cannot do pointer arithmetic with an opaque pointer.
3780
@test fn testOpaquePointerNoArithmetic() throws (testing::TestError) {
3781
    {
3782
        let mut a = testResolver();
3783
        let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = o + 1; }");
3784
        let err = try expectError(&result);
3785
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3786
    } {
3787
        let mut a = testResolver();
3788
        let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = 1 + o; }");
3789
        let err = try expectError(&result);
3790
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3791
    } {
3792
        let mut a = testResolver();
3793
        let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = o - 1; }");
3794
        let err = try expectError(&result);
3795
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3796
    } {
3797
        let mut a = testResolver();
3798
        let result = try resolveProgramStr(&mut a, "fn f(o: *opaque) { let x = 1 - o; }");
3799
        let err = try expectError(&result);
3800
        try expectErrorKind(&result, super::ErrorKind::OpaquePointerArithmetic);
3801
    }
3802
}
3803
3804
// Wildcard import/reexport tests.
3805
3806
/// Test transitive re-export.
3807
@test fn testWildcardReexportTransitive() throws (testing::TestError) {
3808
    let mut a = testResolver();
3809
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3810
3811
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena);
3812
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena);
3813
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena);
3814
    let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena);
3815
    let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export constant MAX: i32 = 100;", &mut arena);
3816
3817
    let result = try resolveModuleTree(&mut a, rootId);
3818
    try expectNoErrors(&result);
3819
}
3820
3821
/// Test that wildcard import can access public symbols.
3822
@test fn testWildcardImportPublicOnly() throws (testing::TestError) {
3823
    let mut a = testResolver();
3824
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3825
3826
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3827
    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);
3828
    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);
3829
3830
    let result = try resolveModuleTree(&mut a, rootId);
3831
    try expectNoErrors(&result);
3832
}
3833
3834
/// Test that wildcard import cannot access private symbols.
3835
@test fn testWildcardImportSkipsPrivate() throws (testing::TestError) {
3836
    let mut a = testResolver();
3837
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3838
3839
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena);
3840
    let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena);
3841
    let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena);
3842
3843
    let result = try resolveModuleTree(&mut a, rootId);
3844
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private"));
3845
}
3846
3847
/// Test that a constant array can use another constant as its length.
3848
@test fn testConstArrayWithConstLength() throws (testing::TestError) {
3849
    let mut a = testResolver();
3850
    let program = "constant LEN: u32 = 3; constant ARR: [i32; LEN] = [1, 2, 3];";
3851
    let result = try resolveProgramStr(&mut a, program);
3852
    try expectNoErrors(&result);
3853
3854
    // Verify the array constant has the correct type with length 3.
3855
    let arrStmt = try getBlockStmt(result.root, 1);
3856
    let sym = super::symbolFor(&a, arrStmt)
3857
        else throw testing::TestError::Failed;
3858
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
3859
        else throw testing::TestError::Failed;
3860
    try testing::expect(arrType.length == 3);
3861
}
3862
3863
/// Test that a record field can use a constant as its array length.
3864
@test fn testRecordFieldWithConstArrayLength() throws (testing::TestError) {
3865
    let mut a = testResolver();
3866
    let program = "constant SIZE: u32 = 4; record Buffer { data: [i32; SIZE], }";
3867
    let result = try resolveProgramStr(&mut a, program);
3868
    try expectNoErrors(&result);
3869
}
3870
3871
/// Test that a constant can have a record literal value (lazy record body resolution).
3872
@test fn testConstWithRecordLiteral() throws (testing::TestError) {
3873
    let mut a = testResolver();
3874
    let program = "record Point { x: i32, y: i32 } constant ORIGIN: Point = Point { x: 0, y: 0 };";
3875
    let result = try resolveProgramStr(&mut a, program);
3876
    try expectNoErrors(&result);
3877
}
3878
3879
/// Test that a constant can have a union variant value (lazy union body resolution).
3880
@test fn testConstWithUnionVariant() throws (testing::TestError) {
3881
    let mut a = testResolver();
3882
    let program = "union Color { Red, Green, Blue } constant DEFAULT: Color = Color::Red;";
3883
    let result = try resolveProgramStr(&mut a, program);
3884
    try expectNoErrors(&result);
3885
}
3886
3887
/// Test that record field types can reference imported types.
3888
///
3889
/// This tests that `use` statements are processed before record body resolution,
3890
/// allowing record fields to use types from imported modules.
3891
@test fn testRecordFieldUsesImportedType() throws (testing::TestError) {
3892
    let mut a = testResolver();
3893
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3894
3895
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena);
3896
    let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena);
3897
    let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena);
3898
3899
    let result = try resolveModuleTree(&mut a, rootId);
3900
    try expectNoErrors(&result);
3901
}
3902
3903
/// Test that imported constants can be used in array size expressions.
3904
///
3905
/// This tests that constant values are propagated through scope access expressions,
3906
/// enabling compile-time evaluation of array sizes using imported constants.
3907
@test fn testImportedConstantInArraySize() throws (testing::TestError) {
3908
    let mut a = testResolver();
3909
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
3910
3911
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; unsafe mod app;", &mut arena);
3912
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant SIZE: u32 = 8;", &mut arena);
3913
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = undefined;", &mut arena);
3914
3915
    let result = try resolveModuleTree(&mut a, rootId);
3916
    try expectNoErrors(&result);
3917
}
3918
3919
/// Test that `if let case` binds payload variables in the then branch.
3920
///
3921
/// When using `if let case Union::Variant(x) = expr { ... }`, the variable `x` should
3922
/// be bound to the payload value within the then branch scope.
3923
@test fn testResolveIfCaseBindsPayload() throws (testing::TestError) {
3924
    let mut a = testResolver();
3925
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value { return x; } return 0; }";
3926
    let result = try resolveProgramStr(&mut a, program);
3927
    try expectNoErrors(&result);
3928
}
3929
3930
/// Test that `if let case` payload binding is scoped to the then branch.
3931
///
3932
/// The payload variable should not be accessible outside the then branch.
3933
@test fn testResolveIfCasePayloadScopeError() throws (testing::TestError) {
3934
    let mut a = testResolver();
3935
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { if let case Opt::Some(x) = value {} return x; }";
3936
    let result = try resolveProgramStr(&mut a, program);
3937
    let err = try expectError(&result);
3938
    let case super::ErrorKind::UnresolvedSymbol(name) = err.kind
3939
        else throw testing::TestError::Failed;
3940
    try testing::expect(mem::eq(name, "x"));
3941
}
3942
3943
/// Test that `let case` binds payload variables in the current scope.
3944
///
3945
/// When using `let case Union::Variant(x) = expr else { ... }`, the variable `x`
3946
/// should be bound in the scope after the statement.
3947
@test fn testResolveLetCaseElseBindsPayload() throws (testing::TestError) {
3948
    let mut a = testResolver();
3949
    let program = "union Opt { Some(i32), None } fn f(value: Opt) -> i32 { let case Opt::Some(x) = value else panic; return x; }";
3950
    let result = try resolveProgramStr(&mut a, program);
3951
    try expectNoErrors(&result);
3952
}
3953
3954
/// Test that function pointers with identical signatures are assignable.
3955
///
3956
/// Two function types with the same parameters, return type, and throw list
3957
/// should be considered structurally equal, even if they are separate allocations.
3958
@test fn testFnPointerAssignability() throws (testing::TestError) {
3959
    let mut a = testResolver();
3960
    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);";
3961
    let result = try resolveProgramStr(&mut a, program);
3962
    try expectNoErrors(&result);
3963
}
3964
3965
/// Test that function pointers with different parameter types are not assignable.
3966
@test fn testFnPointerParamMismatch() throws (testing::TestError) {
3967
    let mut a = testResolver();
3968
    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);";
3969
    let result = try resolveProgramStr(&mut a, program);
3970
    let err = try expectError(&result);
3971
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3972
        else throw testing::TestError::Failed;
3973
}
3974
3975
/// Test that function pointers with different return types are not assignable.
3976
@test fn testFnPointerReturnMismatch() throws (testing::TestError) {
3977
    let mut a = testResolver();
3978
    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);";
3979
    let result = try resolveProgramStr(&mut a, program);
3980
    let err = try expectError(&result);
3981
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3982
        else throw testing::TestError::Failed;
3983
}
3984
3985
/// Test that named records use nominal typing, not structural.
3986
///
3987
/// Two different named record types with identical fields should NOT be
3988
/// assignable to each other, because they are distinct nominal types.
3989
@test fn testNamedRecordNominalTyping() throws (testing::TestError) {
3990
    let mut a = testResolver();
3991
    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);";
3992
    let result = try resolveProgramStr(&mut a, program);
3993
    let err = try expectError(&result);
3994
    let case super::ErrorKind::TypeMismatch(_) = err.kind
3995
        else throw testing::TestError::Failed;
3996
}
3997
3998
/// Test that union variants with labeled record payloads can be constructed.
3999
@test fn testUnionVariantAnonRecordPayload() throws (testing::TestError) {
4000
    let mut a = testResolver();
4001
    let program = "union Event { Click { x: i32, y: i32 }, Key { code: u32 } } let e = Event::Click { x: 10, y: 20 };";
4002
    let result = try resolveProgramStr(&mut a, program);
4003
    try expectNoErrors(&result);
4004
}
4005
4006
/// Test that unlabeled record literals with positional fields work correctly.
4007
///
4008
/// When a record is declared with positional fields (e.g., `record R(i32, bool)`),
4009
/// the literal must use constructor call syntax with positional arguments.
4010
@test fn testResolveUnlabeledRecordLitValid() throws (testing::TestError) {
4011
    let mut a = testResolver();
4012
    let program = "record R(i32, bool); let r: R = R(1, true);";
4013
    let result = try resolveProgramStr(&mut a, program);
4014
    try expectNoErrors(&result);
4015
}
4016
4017
/// Test that using brace syntax for an unlabeled record causes an error.
4018
@test fn testResolveUnlabeledRecordLitStyleMismatch() throws (testing::TestError) {
4019
    let mut a = testResolver();
4020
    let program = "record R(i32); let r = R { x: 1 };";
4021
    let result = try resolveProgramStr(&mut a, program);
4022
    try expectErrorKind(&result, super::ErrorKind::RecordFieldStyleMismatch);
4023
}
4024
4025
/// Test that providing too many fields for an unlabeled record causes count mismatch.
4026
@test fn testResolveUnlabeledRecordLitTooManyFields() throws (testing::TestError) {
4027
    let mut a = testResolver();
4028
    let program = "record R(i32, bool); let r = R(1, true, 3);";
4029
    let result = try resolveProgramStr(&mut a, program);
4030
    let err = try expectError(&result);
4031
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4032
        else throw testing::TestError::Failed;
4033
}
4034
4035
/// Test that match pattern with wrong number of bindings causes count mismatch.
4036
@test fn testResolveMatchPatternWrongBindingCount() throws (testing::TestError) {
4037
    let mut a = testResolver();
4038
    let program = "union Event { Click { x: i32, y: i32 } } fn f(e: Event) { match e { case Event::Click(a) => {} } }";
4039
    let result = try resolveProgramStr(&mut a, program);
4040
    let err = try expectError(&result);
4041
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4042
        else throw testing::TestError::Failed;
4043
}
4044
4045
/// Test that shorthand field syntax works in record literals.
4046
/// `Point { x, y }` should be equivalent to `Point { x: x, y: y }`.
4047
@test fn testResolveRecordLiteralShorthand() throws (testing::TestError) {
4048
    let mut a = testResolver();
4049
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 1; let y: i32 = 2; let p = Point { x, y }; }";
4050
    let result = try resolveProgramStr(&mut a, program);
4051
    try expectNoErrors(&result);
4052
}
4053
4054
/// Test shorthand field syntax with mixed explicit and shorthand fields.
4055
@test fn testResolveRecordLiteralMixedShorthand() throws (testing::TestError) {
4056
    let mut a = testResolver();
4057
    let program = "record Point { x: i32, y: i32 } fn f() { let x: i32 = 5; let p = Point { x, y: 10 }; }";
4058
    let result = try resolveProgramStr(&mut a, program);
4059
    try expectNoErrors(&result);
4060
}
4061
4062
/// Test record-style union variant patterns with shorthand syntax.
4063
@test fn testResolveMatchRecordPatternShorthand() throws (testing::TestError) {
4064
    let mut a = testResolver();
4065
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { width, height } => return width + height } }";
4066
    let result = try resolveProgramStr(&mut a, program);
4067
    try expectNoErrors(&result);
4068
}
4069
4070
/// Test record pattern with mixed shorthand and explicit labels.
4071
@test fn testResolveMatchRecordPatternMixed() throws (testing::TestError) {
4072
    let mut a = testResolver();
4073
    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 } }";
4074
    let result = try resolveProgramStr(&mut a, program);
4075
    try expectNoErrors(&result);
4076
}
4077
4078
/// Test record pattern with fields in reverse order.
4079
@test fn testResolveMatchRecordPatternReversed() throws (testing::TestError) {
4080
    let mut a = testResolver();
4081
    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 } }";
4082
    let result = try resolveProgramStr(&mut a, program);
4083
    try expectNoErrors(&result);
4084
}
4085
4086
/// Test record pattern with shorthand syntax in reverse order.
4087
/// Pattern `{ height, width }` binds all fields using shorthand, but not in definition order.
4088
@test fn testResolveMatchRecordPatternShorthandReversed() throws (testing::TestError) {
4089
    let mut a = testResolver();
4090
    let program = "union Shape { Rect { width: i32, height: i32 } } fn f(s: Shape) -> i32 { match s { case Shape::Rect { height, width } => return width + height } }";
4091
    let result = try resolveProgramStr(&mut a, program);
4092
    try expectNoErrors(&result);
4093
}
4094
4095
/// Test record pattern with `..` ignoring fields.
4096
@test fn testResolveMatchRecordPatternIgnoreRest() throws (testing::TestError) {
4097
    {
4098
        let mut a = testResolver();
4099
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { x, .. } => return x } }";
4100
        let result = try resolveProgramStr(&mut a, program);
4101
        try expectNoErrors(&result);
4102
    } {
4103
        let mut a = testResolver();
4104
        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 } }";
4105
        let result = try resolveProgramStr(&mut a, program);
4106
        try expectNoErrors(&result);
4107
    } {
4108
        let mut a = testResolver();
4109
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> i32 { match g { case G::Point { z, .. } => return z } }";
4110
        let result = try resolveProgramStr(&mut a, program);
4111
        try expectNoErrors(&result);
4112
    } {
4113
        let mut a = testResolver();
4114
        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 } }";
4115
        let result = try resolveProgramStr(&mut a, program);
4116
        try expectNoErrors(&result);
4117
    } {
4118
        let mut a = testResolver();
4119
        let program = "union G { Point { x: i32, y: i32, z: i32 } } fn f(g: G) -> bool { match g { case G::Point { .. } => return true } }";
4120
        let result = try resolveProgramStr(&mut a, program);
4121
        try expectNoErrors(&result);
4122
    }
4123
}
4124
4125
/// Test standalone record pattern matching with unlabeled patterns.
4126
@test fn testResolveMatchStandaloneRecordUnlabeledPattern() throws (testing::TestError) {
4127
    let mut a = testResolver();
4128
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x) => return x, else => return 0 } }";
4129
    let result = try resolveProgramStr(&mut a, program);
4130
    try expectNoErrors(&result);
4131
}
4132
4133
/// Test standalone record pattern matching with labeled patterns.
4134
/// Pattern syntax: `T { x }` matches a named record and binds x to the field.
4135
@test fn testResolveMatchStandaloneRecordLabeledPattern() throws (testing::TestError) {
4136
    let mut a = testResolver();
4137
    let program = "record T { x: i32 } fn f(t: T) -> i32 { match t { case T { x } => return x, else => return 0 } }";
4138
    let result = try resolveProgramStr(&mut a, program);
4139
    try expectNoErrors(&result);
4140
}
4141
4142
/// Test standalone record pattern with multiple fields.
4143
/// Pattern syntax: `R(a, b)` matches an unlabeled record with multiple fields.
4144
@test fn testResolveMatchStandaloneRecordMultipleFields() throws (testing::TestError) {
4145
    let mut a = testResolver();
4146
    let program = "record R(bool, u8); fn f(r: R) -> u8 { match r { case R(_, x) => return x, else => return 0 } }";
4147
    let result = try resolveProgramStr(&mut a, program);
4148
    try expectNoErrors(&result);
4149
}
4150
4151
/// Test standalone record pattern with wrong field count.
4152
/// Pattern `S(x, y)` should fail for a single-field record.
4153
@test fn testResolveMatchStandaloneRecordWrongFieldCount() throws (testing::TestError) {
4154
    let mut a = testResolver();
4155
    let program = "record S(i32); fn f(s: S) -> i32 { match s { case S(x, y) => return x + y, else => return 0 } }";
4156
    let result = try resolveProgramStr(&mut a, program);
4157
    let err = try expectError(&result);
4158
    let case super::ErrorKind::RecordFieldCountMismatch(_) = err.kind
4159
        else throw testing::TestError::Failed;
4160
}
4161
4162
/// Test array pattern matching with element bindings.
4163
/// Pattern syntax: `[x, y]` matches an array and binds elements.
4164
@test fn testResolveMatchArrayPattern() throws (testing::TestError) {
4165
    let mut a = testResolver();
4166
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [x, y] => return x + y } }";
4167
    let result = try resolveProgramStr(&mut a, program);
4168
    try expectNoErrors(&result);
4169
}
4170
4171
/// Test array pattern with placeholder elements.
4172
/// Pattern syntax: `[_, y]` ignores first element.
4173
@test fn testResolveMatchArrayPatternPlaceholder() throws (testing::TestError) {
4174
    let mut a = testResolver();
4175
    let program = "fn f(arr: [i32; 2]) -> i32 { match arr { case [_, y] => return y } }";
4176
    let result = try resolveProgramStr(&mut a, program);
4177
    try expectNoErrors(&result);
4178
}
4179
4180
/// Test identifier pattern that binds the whole value.
4181
/// Pattern syntax: `x` matches any value and binds it.
4182
@test fn testResolveMatchIdentPattern() throws (testing::TestError) {
4183
    let mut a = testResolver();
4184
    let program = "fn f(val: i32) -> i32 { match val { x => return x } }";
4185
    let result = try resolveProgramStr(&mut a, program);
4186
    try expectNoErrors(&result);
4187
}
4188
4189
/// Test numeric literal pattern matching.
4190
@test fn testResolveMatchNumericLiteralPattern() throws (testing::TestError) {
4191
    let mut a = testResolver();
4192
    let program = "fn f(val: i32) -> i32 { match val { case 42 => return 1, else => return 0 } }";
4193
    let result = try resolveProgramStr(&mut a, program);
4194
    try expectNoErrors(&result);
4195
}
4196
4197
/// Test string literal pattern matching.
4198
@test fn testResolveMatchStringLiteralPattern() throws (testing::TestError) {
4199
    let mut a = testResolver();
4200
    let program = "fn f(val: *[u8]) -> i32 { match val { case \"hello\" => return 1, else => return 0 } }";
4201
    let result = try resolveProgramStr(&mut a, program);
4202
    try expectNoErrors(&result);
4203
}
4204
4205
/// Test boolean literal pattern matching.
4206
@test fn testResolveMatchBoolLiteralPattern() throws (testing::TestError) {
4207
    let mut a = testResolver();
4208
    let program = "fn f(val: bool) -> i32 { match val { case true => return 1, case false => return 0 } }";
4209
    let result = try resolveProgramStr(&mut a, program);
4210
    try expectNoErrors(&result);
4211
}
4212
4213
/// Test @sliceOf with correct arguments succeeds.
4214
@test fn testResolveSliceOfCorrect() throws (testing::TestError) {
4215
    // Immutable pointer.
4216
    {
4217
        let mut a = testResolver();
4218
        let program = "fn f(ptr: *u8, len: u32) -> *[u8] { unsafe { return @sliceOf(ptr, len); } }";
4219
        let result = try resolveProgramStr(&mut a, program);
4220
        try expectNoErrors(&result);
4221
    }
4222
    // Mutable pointer produces mutable slice.
4223
    {
4224
        let mut a = testResolver();
4225
        let program = "fn f(ptr: *mut u8, len: u32) -> *mut [u8] { unsafe { return @sliceOf(ptr, len); } }";
4226
        let result = try resolveProgramStr(&mut a, program);
4227
        try expectNoErrors(&result);
4228
    }
4229
}
4230
4231
/// Test @sliceOf with wrong argument count produces an error.
4232
@test fn testResolveSliceOfWrongArgCount() throws (testing::TestError) {
4233
    // No arguments.
4234
    {
4235
        let mut a = testResolver();
4236
        let program = "fn f() -> *[u8] { return @sliceOf(); }";
4237
        let result = try resolveProgramStr(&mut a, program);
4238
        let err = try expectError(&result);
4239
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4240
            else throw testing::TestError::Failed;
4241
        try testing::expect(mismatch.expected == 2);
4242
        try testing::expect(mismatch.actual == 0);
4243
    }
4244
    // Too few arguments.
4245
    {
4246
        let mut a = testResolver();
4247
        let program = "fn f(ptr: *u8) -> *[u8] { return @sliceOf(ptr); }";
4248
        let result = try resolveProgramStr(&mut a, program);
4249
        let err = try expectError(&result);
4250
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4251
            else throw testing::TestError::Failed;
4252
        try testing::expect(mismatch.expected == 2);
4253
        try testing::expect(mismatch.actual == 1);
4254
    }
4255
    // Too many arguments.
4256
    {
4257
        let mut a = testResolver();
4258
        let program = "fn f(ptr: *u8, len: u32, cap: u32, extra: u32) -> *[u8] { return @sliceOf(ptr, len, cap, extra); }";
4259
        let result = try resolveProgramStr(&mut a, program);
4260
        let err = try expectError(&result);
4261
        let case super::ErrorKind::BuiltinArgCountMismatch(mismatch) = err.kind
4262
            else throw testing::TestError::Failed;
4263
        try testing::expect(mismatch.expected == 2);
4264
        try testing::expect(mismatch.actual == 4);
4265
    }
4266
}
4267
4268
/// Test @sliceOf with wrong argument types produces errors.
4269
@test fn testResolveSliceOfWrongArgTypes() throws (testing::TestError) {
4270
    // Non-pointer first argument.
4271
    {
4272
        let mut a = testResolver();
4273
        let program = "fn f(val: u32, len: u32) -> *[u8] { return unsafe { @sliceOf(val, len) }; }";
4274
        let result = try resolveProgramStr(&mut a, program);
4275
        let err = try expectError(&result);
4276
        let case super::ErrorKind::ExpectedPointer = err.kind
4277
            else throw testing::TestError::Failed;
4278
    }
4279
    // Array instead of pointer.
4280
    {
4281
        let mut a = testResolver();
4282
        let program = "fn f(arr: [u8; 4], len: u32) -> *[u8] { return unsafe { @sliceOf(arr, len) }; }";
4283
        let result = try resolveProgramStr(&mut a, program);
4284
        let err = try expectError(&result);
4285
        let case super::ErrorKind::ExpectedPointer = err.kind
4286
            else throw testing::TestError::Failed;
4287
    }
4288
    // Non-numeric second argument.
4289
    {
4290
        let mut a = testResolver();
4291
        let program = "fn f(ptr: *u8, len: bool) -> *[u8] { return unsafe { @sliceOf(ptr, len) }; }";
4292
        let result = try resolveProgramStr(&mut a, program);
4293
        let err = try expectError(&result);
4294
        let case super::ErrorKind::TypeMismatch(_) = err.kind
4295
            else throw testing::TestError::Failed;
4296
    }
4297
    // Pointer second argument.
4298
    {
4299
        let mut a = testResolver();
4300
        let program = "fn f(ptr: *u8, len: *u32) -> *[u8] { return unsafe { @sliceOf(ptr, len) }; }";
4301
        let result = try resolveProgramStr(&mut a, program);
4302
        let err = try expectError(&result);
4303
        let case super::ErrorKind::TypeMismatch(_) = err.kind
4304
            else throw testing::TestError::Failed;
4305
    }
4306
}
4307
4308
/// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds.
4309
@test fn testResolveSliceOfWithCap() throws (testing::TestError) {
4310
    {
4311
        let mut a = testResolver();
4312
        let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { unsafe { return @sliceOf(ptr, len, cap); } }";
4313
        let result = try resolveProgramStr(&mut a, program);
4314
        try expectNoErrors(&result);
4315
    }
4316
    // Mutable pointer produces mutable slice.
4317
    {
4318
        let mut a = testResolver();
4319
        let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { unsafe { return @sliceOf(ptr, len, cap); } }";
4320
        let result = try resolveProgramStr(&mut a, program);
4321
        try expectNoErrors(&result);
4322
    }
4323
}
4324
4325
/// Test @sliceOf with 3 arguments but wrong cap type.
4326
@test fn testResolveSliceOfCapWrongType() throws (testing::TestError) {
4327
    let mut a = testResolver();
4328
    let program = "fn f(ptr: *u8, len: u32, cap: bool) -> *[u8] { return unsafe { @sliceOf(ptr, len, cap) }; }";
4329
    let result = try resolveProgramStr(&mut a, program);
4330
    let err = try expectError(&result);
4331
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4332
        else throw testing::TestError::Failed;
4333
}
4334
4335
/// Test .cap field access on slices resolves to u32.
4336
@test fn testResolveSliceCapField() throws (testing::TestError) {
4337
    let mut a = testResolver();
4338
    let program = "fn f(s: &[u8]) -> u32 { return s.cap; }";
4339
    let result = try resolveProgramStr(&mut a, program);
4340
    try expectNoErrors(&result);
4341
}
4342
4343
/// Require a slice append allocator expression to produce a type mismatch.
4344
fn expectSliceAppendAllocatorError(program: *[u8]) throws (testing::TestError) {
4345
    let mut a = testResolver();
4346
    let result = try resolveProgramStr(&mut a, program);
4347
    let err = try expectError(&result);
4348
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4349
        else throw testing::TestError::Failed;
4350
}
4351
4352
/// Test `.append()` on immutable slice produces an error.
4353
@test fn testResolveSliceAppendImmutable() throws (testing::TestError) {
4354
    let mut a = testResolver();
4355
    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); }";
4356
    let result = try resolveProgramStr(&mut a, program);
4357
    let err = try expectError(&result);
4358
    let case super::ErrorKind::ImmutableBinding = err.kind
4359
        else throw testing::TestError::Failed;
4360
}
4361
4362
/// Test `.append()` with wrong argument count produces an error.
4363
@test fn testResolveSliceAppendWrongArgCount() throws (testing::TestError) {
4364
    // Too few arguments.
4365
    {
4366
        let mut a = testResolver();
4367
        let program = "fn f(s: *mut [i32]) { s.append(1); }";
4368
        let result = try resolveProgramStr(&mut a, program);
4369
        let err = try expectError(&result);
4370
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4371
            else throw testing::TestError::Failed;
4372
        try testing::expect(m.expected == 2);
4373
        try testing::expect(m.actual == 1);
4374
    }
4375
    // Too many arguments.
4376
    {
4377
        let mut a = testResolver();
4378
        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); }";
4379
        let result = try resolveProgramStr(&mut a, program);
4380
        let err = try expectError(&result);
4381
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4382
            else throw testing::TestError::Failed;
4383
        try testing::expect(m.expected == 2);
4384
        try testing::expect(m.actual == 3);
4385
    }
4386
}
4387
4388
/// Test `.append()` with correct arguments succeeds.
4389
@test fn testResolveSliceAppendCorrect() throws (testing::TestError) {
4390
    // A structurally matching allocator record is accepted.
4391
    {
4392
        let mut a = testResolver();
4393
        let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: &mut [i32], a: A) { s.append(1, a); }";
4394
        let result = try resolveProgramStr(&mut a, program);
4395
        try expectNoErrors(&result);
4396
    }
4397
    // Function structural matching and concrete-to-opaque pointer coercion are preserved.
4398
    {
4399
        let mut a = testResolver();
4400
        let program = "record C { value: u32 } record A { callback: fn(*mut opaque, u32, u32) -> *mut opaque, context: *mut opaque } fn allocate(ctx: *mut opaque, size: u32, alignment: u32) -> *mut opaque { return ctx; } unsafe fn f(s: &mut [i32], ctx: *mut C) { s.append(1, A { callback: allocate, context: ctx }); }";
4401
        let result = try resolveProgramStr(&mut a, program);
4402
        try expectNoErrors(&result);
4403
    }
4404
}
4405
4406
/// Test `.append()` requires an unsafe context even with a valid allocator.
4407
@test fn testResolveSliceAppendRequiresUnsafe() throws (testing::TestError) {
4408
    let mut a = testResolver();
4409
    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); }";
4410
    let result = try resolveProgramStr(&mut a, program);
4411
    let err = try expectError(&result);
4412
    let case super::ErrorKind::UnsafeOperation = err.kind
4413
        else throw testing::TestError::Failed;
4414
}
4415
4416
/// Test `.append()` rejects allocator values without the exact structural contract.
4417
@test fn testResolveSliceAppendInvalidAllocator() throws (testing::TestError) {
4418
    try expectSliceAppendAllocatorError(
4419
        "unsafe fn f(s: *mut [i32]) { s.append(1, 0); }"
4420
    );
4421
    try expectSliceAppendAllocatorError(
4422
        "unsafe fn f(s: *mut [i32]) { s.append(1, undefined); }"
4423
    );
4424
    try expectSliceAppendAllocatorError(
4425
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque, extra: u32 } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4426
    );
4427
    try expectSliceAppendAllocatorError(
4428
        "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4429
    );
4430
    try expectSliceAppendAllocatorError(
4431
        "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4432
    );
4433
    try expectSliceAppendAllocatorError(
4434
        "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4435
    );
4436
    try expectSliceAppendAllocatorError(
4437
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4438
    );
4439
    try expectSliceAppendAllocatorError(
4440
        "record A { func: fn(*unsafe mut opaque, u32, u32) -> *unsafe mut opaque, ctx: *unsafe mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(1, a); }"
4441
    );
4442
}
4443
4444
/// Test `.append()` with wrong element type produces an error.
4445
@test fn testResolveSliceAppendWrongElemType() throws (testing::TestError) {
4446
    let mut a = testResolver();
4447
    let program = "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } unsafe fn f(s: *mut [i32], a: A) { s.append(true, a); }";
4448
    let result = try resolveProgramStr(&mut a, program);
4449
    let err = try expectError(&result);
4450
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4451
        else throw testing::TestError::Failed;
4452
}
4453
4454
/// Test `.delete()` on immutable slice produces an error.
4455
@test fn testResolveSliceDeleteImmutable() throws (testing::TestError) {
4456
    let mut a = testResolver();
4457
    let program = "fn f(s: *[i32]) { s.delete(0); }";
4458
    let result = try resolveProgramStr(&mut a, program);
4459
    let err = try expectError(&result);
4460
    let case super::ErrorKind::ImmutableBinding = err.kind
4461
        else throw testing::TestError::Failed;
4462
}
4463
4464
/// Test `.delete()` with wrong argument count produces an error.
4465
@test fn testResolveSliceDeleteWrongArgCount() throws (testing::TestError) {
4466
    // No arguments.
4467
    {
4468
        let mut a = testResolver();
4469
        let program = "fn f(s: *mut [i32]) { s.delete(); }";
4470
        let result = try resolveProgramStr(&mut a, program);
4471
        let err = try expectError(&result);
4472
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4473
            else throw testing::TestError::Failed;
4474
        try testing::expect(m.expected == 1);
4475
        try testing::expect(m.actual == 0);
4476
    }
4477
    // Too many arguments.
4478
    {
4479
        let mut a = testResolver();
4480
        let program = "fn f(s: *mut [i32]) { s.delete(0, 1); }";
4481
        let result = try resolveProgramStr(&mut a, program);
4482
        let err = try expectError(&result);
4483
        let case super::ErrorKind::FnArgCountMismatch(m) = err.kind
4484
            else throw testing::TestError::Failed;
4485
        try testing::expect(m.expected == 1);
4486
        try testing::expect(m.actual == 2);
4487
    }
4488
}
4489
4490
/// Test `.delete()` with correct arguments succeeds.
4491
@test fn testResolveSliceDeleteCorrect() throws (testing::TestError) {
4492
    let mut a = testResolver();
4493
    let program = "unsafe fn f(s: &mut [i32]) { s.delete(0); }";
4494
    let result = try resolveProgramStr(&mut a, program);
4495
    try expectNoErrors(&result);
4496
}
4497
4498
/// Test `.delete()` with wrong argument type produces an error.
4499
@test fn testResolveSliceDeleteWrongArgType() throws (testing::TestError) {
4500
    let mut a = testResolver();
4501
    let program = "fn f(s: *mut [i32]) { s.delete(true); }";
4502
    let result = try resolveProgramStr(&mut a, program);
4503
    let err = try expectError(&result);
4504
    let case super::ErrorKind::TypeMismatch(_) = err.kind
4505
        else throw testing::TestError::Failed;
4506
}
4507
4508
/// Safe slice delete borrows its receiver for the call and leaves it available.
4509
@test fn testLinearSliceDeletePreservesReceiver() throws (testing::TestError) {
4510
    let program = "fn consumeSlice(slice: *mut [u32]) { consumeSlice(slice); } fn run(slice: *mut [u32]) { slice.delete(0); consumeSlice(slice); }";
4511
    try expectAnalyzeOk(program);
4512
}
4513
4514
/// Slice append consumes its receiver, element, and allocator before rebinding.
4515
@test fn testLinearSliceAppendInUnsafeBlock() throws (testing::TestError) {
4516
    let program = "record Token: Linear {} record Allocator { func: fn(*mut opaque, u32, u32) -> *mut opaque, ctx: *mut opaque } fn consumeSlice(slice: *mut [Token]) { consumeSlice(slice); } fn run(slice: *mut [Token], token: Token, allocator: Allocator) { unsafe { set slice = slice.append(token, allocator); } consumeSlice(slice); }";
4517
    try expectAnalyzeOk(program);
4518
}
4519
4520
/// Test `match &opt` produces immutable pointer bindings.
4521
@test fn testResolveMatchRefUnionBinding() throws (testing::TestError) {
4522
    let mut a = testResolver();
4523
    let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }";
4524
    let result = try resolveProgramStr(&mut a, program);
4525
    try expectNoErrors(&result);
4526
4527
    let fnBlock = try getFnBody(&a, result.root, "f");
4528
    let matchNode = fnBlock.statements[1];
4529
    let case ast::NodeValue::Match(sw) = matchNode.value
4530
        else throw testing::TestError::Failed;
4531
    let caseNode = sw.prongs[0];
4532
4533
    let scope = super::scopeFor(&a, caseNode)
4534
        else throw testing::TestError::Failed;
4535
    let payloadSym = super::findSymbolInScope(scope, "x")
4536
        else throw testing::TestError::Failed;
4537
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4538
        else throw testing::TestError::Failed;
4539
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4540
        else throw testing::TestError::Failed;
4541
    try testing::expect(not mutable);
4542
    try testing::expect(*target == super::Type::I32);
4543
}
4544
4545
/// Test `match &mut opt` produces mutable pointer bindings.
4546
@test fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) {
4547
    let mut a = testResolver();
4548
    let program = "union Opt { Some(i32), None } fn f() { let mut opt = Opt::Some(42); match &mut opt { case Opt::Some(x) => { *x; } else => {} } }";
4549
    let result = try resolveProgramStr(&mut a, program);
4550
    try expectNoErrors(&result);
4551
4552
    let fnBlock = try getFnBody(&a, result.root, "f");
4553
    let matchNode = fnBlock.statements[1];
4554
    let case ast::NodeValue::Match(sw) = matchNode.value
4555
        else throw testing::TestError::Failed;
4556
    let caseNode = sw.prongs[0];
4557
4558
    let scope = super::scopeFor(&a, caseNode)
4559
        else throw testing::TestError::Failed;
4560
    let payloadSym = super::findSymbolInScope(scope, "x")
4561
        else throw testing::TestError::Failed;
4562
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4563
        else throw testing::TestError::Failed;
4564
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4565
        else throw testing::TestError::Failed;
4566
    try testing::expect(mutable);
4567
    try testing::expect(*target == super::Type::I32);
4568
}
4569
4570
/// Non-constant integer widening must use an explicit cast.
4571
@test fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) {
4572
    {
4573
        let mut a = testResolver();
4574
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x;");
4575
        let err = try expectError(&result);
4576
        try expectTypeMismatch(err, super::Type::U32, super::Type::U8);
4577
    } {
4578
        let mut a = testResolver();
4579
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u16 = x;");
4580
        let err = try expectError(&result);
4581
        try expectTypeMismatch(err, super::Type::U16, super::Type::U8);
4582
    } {
4583
        let mut a = testResolver();
4584
        let result = try resolveBlockStr(&mut a, "let x: u16 = 1; let y: u32 = x;");
4585
        let err = try expectError(&result);
4586
        try expectTypeMismatch(err, super::Type::U32, super::Type::U16);
4587
    } {
4588
        let mut a = testResolver();
4589
        let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x;");
4590
        let err = try expectError(&result);
4591
        try expectTypeMismatch(err, super::Type::I32, super::Type::I8);
4592
    } {
4593
        let mut a = testResolver();
4594
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = x as u32;");
4595
        try expectNoErrors(&result);
4596
    } {
4597
        let mut a = testResolver();
4598
        let result = try resolveBlockStr(&mut a, "let x: i8 = 1; let y: i32 = x as i32;");
4599
        try expectNoErrors(&result);
4600
    }
4601
}
4602
4603
/// Mixed-width integer binary ops require an explicit cast.
4604
@test fn testResolveIntegerWideningBinOpRequiresCast() throws (testing::TestError) {
4605
    {
4606
        let mut a = testResolver();
4607
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x | y;");
4608
        let err = try expectError(&result);
4609
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
4610
    } {
4611
        let mut a = testResolver();
4612
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 0xFF; let z: u32 = x & y;");
4613
        let err = try expectError(&result);
4614
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
4615
    } {
4616
        let mut a = testResolver();
4617
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = x + y;");
4618
        let err = try expectError(&result);
4619
        try expectTypeMismatch(err, super::Type::U8, super::Type::U32);
4620
    } {
4621
        let mut a = testResolver();
4622
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u8 = x << 2;");
4623
        try expectNoErrors(&result);
4624
    } {
4625
        let mut a = testResolver();
4626
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) | y;");
4627
        try expectNoErrors(&result);
4628
    } {
4629
        let mut a = testResolver();
4630
        let result = try resolveBlockStr(&mut a, "let x: u8 = 1; let y: u32 = 2; let z: u32 = (x as u32) + y;");
4631
        try expectNoErrors(&result);
4632
    }
4633
}
4634
4635
/// A mutable slice pointer should be assignable to an immutable slice pointer.
4636
@test fn testResolveMutSliceAssignableToImmutSlice() throws (testing::TestError) {
4637
    let mut a = testResolver();
4638
    let result = try resolveProgramStr(&mut a, "fn f(slice: *mut [i32]) -> *[i32] { return slice; }");
4639
    try expectNoErrors(&result);
4640
}
4641
4642
/// Safe `as` casts preserve pointer representation or discard mutability.
4643
@test fn testResolveAsCasts() throws (testing::TestError) {
4644
    { // Pointer to numeric.
4645
        let mut a = testResolver();
4646
        let result = try resolveProgramStr(&mut a, "fn f(p: *i32) { p as u64; }");
4647
        try expectNoErrors(&result);
4648
    } { // Function pointer to numeric.
4649
        let mut a = testResolver();
4650
        let result = try resolveProgramStr(&mut a, "fn target() {} fn f() { target as u32; }");
4651
        try expectNoErrors(&result);
4652
    } { // Identity cast: *mut [i32] to *mut [i32].
4653
        let mut a = testResolver();
4654
        let result = try resolveProgramStr(&mut a, "fn f(s: *mut [i32]) -> *mut [i32] { return s as *mut [i32]; }");
4655
        try expectNoErrors(&result);
4656
    } { // Identity cast: *i32 to *i32.
4657
        let mut a = testResolver();
4658
        let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *i32 { return p as *i32; }");
4659
        try expectNoErrors(&result);
4660
    } { // Identity cast: i32 to i32.
4661
        let mut a = testResolver();
4662
        let result = try resolveBlockStr(&mut a, "let x: i32 = 0; x as i32;");
4663
        try expectNoErrors(&result);
4664
    } { // Mutable pointer to immutable pointer.
4665
        let mut a = testResolver();
4666
        let result = try resolveProgramStr(&mut a, "fn f(p: *mut i32) -> *i32 { return p as *i32; }");
4667
        try expectNoErrors(&result);
4668
    } { // Mutable slice to immutable slice.
4669
        let mut a = testResolver();
4670
        let result = try resolveProgramStr(&mut a, "fn f(s: *mut [i32]) -> *[i32] { return s as *[i32]; }");
4671
        try expectNoErrors(&result);
4672
    }
4673
}
4674
4675
/// Representation-changing pointer and slice casts require an unsafe context.
4676
@test fn testResolveAsCastsRequireUnsafe() throws (testing::TestError) {
4677
    { // Pointer pointee representation.
4678
        let mut a = testResolver();
4679
        let result = try resolveProgramStr(&mut a, "fn f(p: *u8) -> *i32 { return p as *i32; }");
4680
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4681
    } { // Nested pointer pointee representation.
4682
        let mut a = testResolver();
4683
        let result = try resolveProgramStr(&mut a, "fn f(p: **u8) -> **i32 { return p as **i32; }");
4684
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4685
    } { // Concrete pointer to opaque pointer.
4686
        let mut a = testResolver();
4687
        let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *opaque { return p as *opaque; }");
4688
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4689
    } { // Opaque pointer to concrete pointer.
4690
        let mut a = testResolver();
4691
        let result = try resolveProgramStr(&mut a, "fn f(p: *opaque) -> *i32 { return p as *i32; }");
4692
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4693
    } { // Concrete slice to opaque slice.
4694
        let mut a = testResolver();
4695
        let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[opaque] { return s as *[opaque]; }");
4696
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4697
    } { // Opaque slice to concrete slice.
4698
        let mut a = testResolver();
4699
        let result = try resolveProgramStr(&mut a, "fn f(s: *[opaque]) -> *[i32] { return s as *[i32]; }");
4700
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4701
    } { // Integer slice to byte slice.
4702
        let mut a = testResolver();
4703
        let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[u8] { return s as *[u8]; }");
4704
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4705
    } { // Byte slice to integer slice.
4706
        let mut a = testResolver();
4707
        let result = try resolveProgramStr(&mut a, "fn f(s: *[u8]) -> *[i32] { return s as *[i32]; }");
4708
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4709
    } { // Record slice to byte slice.
4710
        let mut a = testResolver();
4711
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[u8] { return s as *[u8]; }");
4712
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4713
    } { // Nested slice element representation.
4714
        let mut a = testResolver();
4715
        let result = try resolveProgramStr(&mut a, "fn f(s: *[*unsafe u8]) -> *[*unsafe i32] { return s as *[*unsafe i32]; }");
4716
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4717
    } { // Record pointer representation.
4718
        let mut a = testResolver();
4719
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) -> *i32 { return p as *i32; }");
4720
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4721
    } { // Record slice element representation.
4722
        let mut a = testResolver();
4723
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[i32] { return s as *[i32]; }");
4724
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
4725
    }
4726
}
4727
4728
/// Unsafe blocks may reinterpret pointers and slices without changing their class.
4729
@test fn testResolveAsCastsInUnsafeBlock() throws (testing::TestError) {
4730
    { // Pointer pointee representation.
4731
        let mut a = testResolver();
4732
        let result = try resolveProgramStr(&mut a, "fn f(p: *u8) -> *i32 { unsafe { return p as *i32; } }");
4733
        try expectNoErrors(&result);
4734
    } { // Nested pointer pointee representation.
4735
        let mut a = testResolver();
4736
        let result = try resolveProgramStr(&mut a, "fn f(p: **u8) -> **i32 { unsafe { return p as **i32; } }");
4737
        try expectNoErrors(&result);
4738
    } { // Concrete pointer to opaque pointer.
4739
        let mut a = testResolver();
4740
        let result = try resolveProgramStr(&mut a, "fn f(p: *i32) -> *opaque { unsafe { return p as *opaque; } }");
4741
        try expectNoErrors(&result);
4742
    } { // Opaque pointer to concrete pointer.
4743
        let mut a = testResolver();
4744
        let result = try resolveProgramStr(&mut a, "fn f(p: *opaque) -> *i32 { unsafe { return p as *i32; } }");
4745
        try expectNoErrors(&result);
4746
    } { // Concrete slice to opaque slice.
4747
        let mut a = testResolver();
4748
        let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[opaque] { unsafe { return s as *[opaque]; } }");
4749
        try expectNoErrors(&result);
4750
    } { // Opaque slice to concrete slice.
4751
        let mut a = testResolver();
4752
        let result = try resolveProgramStr(&mut a, "fn f(s: *[opaque]) -> *[i32] { unsafe { return s as *[i32]; } }");
4753
        try expectNoErrors(&result);
4754
    } { // Integer slice to byte slice.
4755
        let mut a = testResolver();
4756
        let result = try resolveProgramStr(&mut a, "fn f(s: *[i32]) -> *[u8] { unsafe { return s as *[u8]; } }");
4757
        try expectNoErrors(&result);
4758
    } { // Byte slice to integer slice.
4759
        let mut a = testResolver();
4760
        let result = try resolveProgramStr(&mut a, "fn f(s: *[u8]) -> *[i32] { unsafe { return s as *[i32]; } }");
4761
        try expectNoErrors(&result);
4762
    } { // Record slice to byte slice.
4763
        let mut a = testResolver();
4764
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[u8] { unsafe { return s as *[u8]; } }");
4765
        try expectNoErrors(&result);
4766
    } { // Nested slice element representation.
4767
        let mut a = testResolver();
4768
        let result = try resolveProgramStr(&mut a, "fn f(s: *[*unsafe u8]) -> *[*unsafe i32] { unsafe { return s as *[*unsafe i32]; } }");
4769
        try expectNoErrors(&result);
4770
    } { // Record pointer representation.
4771
        let mut a = testResolver();
4772
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(p: *R) -> *i32 { unsafe { return p as *i32; } }");
4773
        try expectNoErrors(&result);
4774
    } { // Record slice element representation.
4775
        let mut a = testResolver();
4776
        let result = try resolveProgramStr(&mut a, "record R { x: i32 } fn f(s: *[R]) -> *[i32] { unsafe { return s as *[i32]; } }");
4777
        try expectNoErrors(&result);
4778
    }
4779
}
4780
4781
/// Tests for invalid `as` casts that should be rejected even in unsafe code.
4782
@test fn testResolveAsCastsInvalid() throws (testing::TestError) {
4783
    { // Pointer to slice is invalid.
4784
        let mut a = testResolver();
4785
        let result = try resolveBlockStr(&mut a, "unsafe { let p: *i32 = undefined; p as *[i32]; }");
4786
        let err = try expectError(&result);
4787
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4788
            else throw testing::TestError::Failed;
4789
    } { // Slice to pointer is invalid.
4790
        let mut a = testResolver();
4791
        let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as *i32; }");
4792
        let err = try expectError(&result);
4793
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4794
            else throw testing::TestError::Failed;
4795
    } { // Slice to numeric is invalid.
4796
        let mut a = testResolver();
4797
        let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as u32; }");
4798
        let err = try expectError(&result);
4799
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4800
            else throw testing::TestError::Failed;
4801
    } { // Integer to pointer is invalid.
4802
        let mut a = testResolver();
4803
        let result = try resolveBlockStr(&mut a, "let address: u64 = 0; unsafe { address as *u8; }");
4804
        let err = try expectError(&result);
4805
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4806
            else throw testing::TestError::Failed;
4807
    } { // Pointer cast cannot add mutability.
4808
        let mut a = testResolver();
4809
        let result = try resolveBlockStr(&mut a, "unsafe { let p: *i32 = undefined; p as *mut i32; }");
4810
        let err = try expectError(&result);
4811
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4812
            else throw testing::TestError::Failed;
4813
    } { // Slice cast cannot add mutability.
4814
        let mut a = testResolver();
4815
        let result = try resolveBlockStr(&mut a, "unsafe { let s: *[i32] = undefined; s as *mut [i32]; }");
4816
        let err = try expectError(&result);
4817
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
4818
            else throw testing::TestError::Failed;
4819
    }
4820
}
4821
4822
/// Test that catch binding is available in catch block scope.
4823
@test fn testResolveTryCatchBinding() throws (testing::TestError) {
4824
    {
4825
        let mut a = testResolver();
4826
        let program = "union Error { Fail } fn fallible() -> u32 throws (Error) { throw Error::Fail; } fn caller() -> u32 { return try fallible() catch err { return 0; }; }";
4827
        let result = try resolveProgramStr(&mut a, program);
4828
        try expectNoErrors(&result);
4829
    } {
4830
        let mut a = testResolver();
4831
        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; } }; }";
4832
        let result = try resolveProgramStr(&mut a, program);
4833
        try expectNoErrors(&result);
4834
    } {
4835
        let mut a = testResolver();
4836
        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; }; }";
4837
        let result = try resolveProgramStr(&mut a, program);
4838
        try expectNoErrors(&result);
4839
    } {
4840
        let mut a = testResolver();
4841
        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, } }; }";
4842
        let result = try resolveProgramStr(&mut a, program);
4843
        try expectNoErrors(&result);
4844
    }
4845
}
4846
4847
/// Test that duplicate union variant patterns are detected.
4848
@test fn testResolveMatchDuplicateUnionPattern() throws (testing::TestError) {
4849
    {
4850
        let mut a = testResolver();
4851
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::A => {}, else => {} } }";
4852
        let result = try resolveProgramStr(&mut a, program);
4853
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4854
    } {
4855
        // No duplicate: distinct variants are fine.
4856
        let mut a = testResolver();
4857
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {} } }";
4858
        let result = try resolveProgramStr(&mut a, program);
4859
        try expectNoErrors(&result);
4860
    }
4861
}
4862
4863
/// Test that duplicate bool patterns are detected.
4864
@test fn testResolveMatchDuplicateBoolPattern() throws (testing::TestError) {
4865
    {
4866
        let mut a = testResolver();
4867
        let program = "fn f(x: bool) { match x { case true => {}, case true => {}, else => {} } }";
4868
        let result = try resolveProgramStr(&mut a, program);
4869
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4870
    } {
4871
        let mut a = testResolver();
4872
        let program = "fn f(x: bool) { match x { case false => {}, case false => {}, else => {} } }";
4873
        let result = try resolveProgramStr(&mut a, program);
4874
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4875
    }
4876
}
4877
4878
/// Test that duplicate nil patterns in optional match are detected.
4879
@test fn testResolveMatchDuplicateOptionalPattern() throws (testing::TestError) {
4880
    {
4881
        let mut a = testResolver();
4882
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, case nil => {} } }";
4883
        let result = try resolveProgramStr(&mut a, program);
4884
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4885
    } {
4886
        // Duplicate value binding.
4887
        let mut a = testResolver();
4888
        let program = "fn f(opt: ?i32) { match opt { v => {}, w => {}, case nil => {} } }";
4889
        let result = try resolveProgramStr(&mut a, program);
4890
        try expectErrorKind(&result, super::ErrorKind::DuplicateMatchPattern);
4891
    }
4892
}
4893
4894
/// Test that guarded match arms are not considered duplicates.
4895
@test fn testResolveMatchGuardedNotDuplicate() throws (testing::TestError) {
4896
    {
4897
        // Guarded union variant followed by same variant is fine.
4898
        let mut a = testResolver();
4899
        let program = "union U { A, B } fn f(u: U) { match u { case U::A if true => {}, case U::A => {}, case U::B => {} } }";
4900
        let result = try resolveProgramStr(&mut a, program);
4901
        try expectNoErrors(&result);
4902
    } {
4903
        // Guarded bool pattern followed by same bool is fine.
4904
        let mut a = testResolver();
4905
        let program = "fn f(x: bool) { match x { case true if true => {}, case true => {}, case false => {} } }";
4906
        let result = try resolveProgramStr(&mut a, program);
4907
        try expectNoErrors(&result);
4908
    } {
4909
        // Guarded nil pattern followed by nil is fine.
4910
        let mut a = testResolver();
4911
        let program = "fn f(opt: ?i32) { match opt { case nil if true => {}, case nil => {}, v => {} } }";
4912
        let result = try resolveProgramStr(&mut a, program);
4913
        try expectNoErrors(&result);
4914
    } {
4915
        // Guarded value binding followed by another binding is fine.
4916
        let mut a = testResolver();
4917
        let program = "fn f(opt: ?i32) { match opt { v if true => {}, w => {}, case nil => {} } }";
4918
        let result = try resolveProgramStr(&mut a, program);
4919
        try expectNoErrors(&result);
4920
    }
4921
}
4922
4923
/// Test that unreachable else is detected when all union variants are covered.
4924
@test fn testResolveMatchUnreachableElseUnion() throws (testing::TestError) {
4925
    {
4926
        let mut a = testResolver();
4927
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, case U::B => {}, else => {} } }";
4928
        let result = try resolveProgramStr(&mut a, program);
4929
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4930
    } {
4931
        // Partial coverage with else is fine.
4932
        let mut a = testResolver();
4933
        let program = "union U { A, B } fn f(u: U) { match u { case U::A => {}, else => {} } }";
4934
        let result = try resolveProgramStr(&mut a, program);
4935
        try expectNoErrors(&result);
4936
    }
4937
}
4938
4939
/// Test that unreachable else is detected when both bool cases are covered.
4940
@test fn testResolveMatchUnreachableElseBool() throws (testing::TestError) {
4941
    {
4942
        let mut a = testResolver();
4943
        let program = "fn f(x: bool) { match x { case true => {}, case false => {}, else => {} } }";
4944
        let result = try resolveProgramStr(&mut a, program);
4945
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4946
    } {
4947
        // Only one case with else is fine.
4948
        let mut a = testResolver();
4949
        let program = "fn f(x: bool) { match x { case true => {}, else => {} } }";
4950
        let result = try resolveProgramStr(&mut a, program);
4951
        try expectNoErrors(&result);
4952
    }
4953
}
4954
4955
/// Test that unreachable else is detected when both optional cases are covered.
4956
@test fn testResolveMatchUnreachableElseOptional() throws (testing::TestError) {
4957
    {
4958
        let mut a = testResolver();
4959
        let program = "fn f(opt: ?i32) { match opt { v => {}, case nil => {}, else => {} } }";
4960
        let result = try resolveProgramStr(&mut a, program);
4961
        try expectErrorKind(&result, super::ErrorKind::UnreachableElse);
4962
    } {
4963
        // Only value binding with else is fine.
4964
        let mut a = testResolver();
4965
        let program = "fn f(opt: ?i32) { match opt { v => {}, else => {} } }";
4966
        let result = try resolveProgramStr(&mut a, program);
4967
        try expectNoErrors(&result);
4968
    }
4969
}
4970
4971
// --- Multi-error typed catch tests ---
4972
4973
@test fn testTypedCatchExhaustive() throws (testing::TestError) {
4974
    let mut a = testResolver();
4975
    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; }; }";
4976
    let result = try resolveProgramStr(&mut a, program);
4977
    try expectNoErrors(&result);
4978
}
4979
4980
@test fn testTypedCatchNonExhaustive() throws (testing::TestError) {
4981
    let mut a = testResolver();
4982
    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; }; }";
4983
    let result = try resolveProgramStr(&mut a, program);
4984
    try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
4985
}
4986
4987
@test fn testTypedCatchDuplicate() throws (testing::TestError) {
4988
    let mut a = testResolver();
4989
    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; }; }";
4990
    let result = try resolveProgramStr(&mut a, program);
4991
    try expectErrorKind(&result, super::ErrorKind::TryCatchDuplicateType);
4992
}
4993
4994
@test fn testTypedCatchWithCatchAll() throws (testing::TestError) {
4995
    let mut a = testResolver();
4996
    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; }; }";
4997
    let result = try resolveProgramStr(&mut a, program);
4998
    try expectNoErrors(&result);
4999
}
5000
5001
@test fn testTypedCatchWrongType() throws (testing::TestError) {
5002
    let mut a = testResolver();
5003
    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; }; }";
5004
    let result = try resolveProgramStr(&mut a, program);
5005
    try expectErrorKind(&result, super::ErrorKind::TryIncompatibleError);
5006
}
5007
5008
@test fn testInferredCatchMultiError() throws (testing::TestError) {
5009
    let mut a = testResolver();
5010
    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; }; }";
5011
    let result = try resolveProgramStr(&mut a, program);
5012
    try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError);
5013
}
5014
5015
@test fn testResolveInstanceMissingMethod() throws (testing::TestError) {
5016
    let mut a = testResolver();
5017
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}";
5018
    let result = try resolveProgramStr(&mut a, program);
5019
    try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f"));
5020
}
5021
5022
@test fn testResolveInstanceUnknownMethod() throws (testing::TestError) {
5023
    let mut a = testResolver();
5024
    let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }";
5025
    let result = try resolveProgramStr(&mut a, program);
5026
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x"));
5027
}
5028
5029
@test fn testResolveTraitDuplicateMethodRejected() throws (testing::TestError) {
5030
    let mut a = testResolver();
5031
    let program = "trait Adder { fn (*mut Adder) add(n: i32) -> i32; fn (*mut Adder) add(n: i32) -> i32; }";
5032
    let result = try resolveProgramStr(&mut a, program);
5033
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("add"));
5034
}
5035
5036
@test fn testResolveInstanceReceiverTypeMustMatchTarget() throws (testing::TestError) {
5037
    let mut a = testResolver();
5038
    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; } }";
5039
    let result = try resolveProgramStr(&mut a, program);
5040
    let err = try expectError(&result);
5041
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5042
        else throw testing::TestError::Failed;
5043
}
5044
5045
@test fn testResolveTraitMethodThrowsRequireTry() throws (testing::TestError) {
5046
    let mut a = testResolver();
5047
    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); }";
5048
    let result = try resolveProgramStr(&mut a, program);
5049
    try expectErrorKind(&result, super::ErrorKind::MissingTry);
5050
}
5051
5052
/// Trait declares immutable receiver (*Trait) but instance uses mutable (*mut Type).
5053
/// The instance method could mutate through what was originally an immutable pointer.
5054
@test fn testResolveInstanceMutReceiverOnImmutableTrait() throws (testing::TestError) {
5055
    let mut a = testResolver();
5056
    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; } }";
5057
    let result = try resolveProgramStr(&mut a, program);
5058
    // Should reject: instance declares *mut receiver but trait only requires immutable.
5059
    try expectErrorKind(&result, super::ErrorKind::ReceiverMutabilityMismatch);
5060
}
5061
5062
/// Instance method declares different parameter types than the trait.
5063
/// The resolver should reject the mismatch rather than silently using the trait's types.
5064
@test fn testResolveInstanceParamTypeMismatch() throws (testing::TestError) {
5065
    let mut a = testResolver();
5066
    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; } }";
5067
    let result = try resolveProgramStr(&mut a, program);
5068
    // Should reject: instance param type u8 doesn't match trait param type i32.
5069
    let err = try expectError(&result);
5070
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5071
        else throw testing::TestError::Failed;
5072
}
5073
5074
/// Duplicate instance declarations for the same (trait, type) pair should be rejected.
5075
@test fn testResolveInstanceDuplicateRejected() throws (testing::TestError) {
5076
    let mut a = testResolver();
5077
    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; } }";
5078
    let result = try resolveProgramStr(&mut a, program);
5079
    // Should reject: duplicate instance for (Adder, Counter).
5080
    try expectErrorKind(&result, super::ErrorKind::DuplicateInstance);
5081
}
5082
5083
/// Trait method receiver must point to the declaring trait type.
5084
@test fn testResolveTraitReceiverMismatch() throws (testing::TestError) {
5085
    let mut a = testResolver();
5086
    let program = "record Other { x: i32 } trait Foo { fn (*mut Other) bar() -> i32; }";
5087
    let result = try resolveProgramStr(&mut a, program);
5088
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
5089
}
5090
5091
/// Using a trait name as a value expression should be rejected.
5092
@test fn testResolveTraitNameAsValueRejected() throws (testing::TestError) {
5093
    let mut a = testResolver();
5094
    let program = "trait Foo { fn (*Foo) bar() -> i32; } fn test() -> i32 { let x = Foo; return 0; }";
5095
    let result = try resolveProgramStr(&mut a, program);
5096
    try expectErrorKind(&result, super::ErrorKind::UnexpectedTraitName);
5097
}
5098
5099
/// Cross-module trait: coerce to trait object and dispatch from a different module.
5100
@test fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) {
5101
    let mut a = testResolver();
5102
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5103
5104
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena);
5105
    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);
5106
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn dispatch(a: &mut opaque defs::Adder) -> i32 { return a.add(5); } fn test() -> i32 { let mut c = defs::Counter { value: 10 }; return dispatch(&mut c); }", &mut arena);
5107
5108
    let result = try resolveModuleTree(&mut a, rootId);
5109
    try expectNoErrors(&result);
5110
}
5111
5112
/// Instance in a different module from trait and type.
5113
@test fn testResolveInstanceCrossModule() throws (testing::TestError) {
5114
    let mut a = testResolver();
5115
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5116
5117
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena);
5118
    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);
5119
    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);
5120
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn dispatch(a: &mut opaque defs::Adder) -> i32 { return a.add(5); } fn test() -> i32 { let mut c = defs::Counter { value: 10 }; return dispatch(&mut c); }", &mut arena);
5121
5122
    let result = try resolveModuleTree(&mut a, rootId);
5123
    try expectNoErrors(&result);
5124
}
5125
5126
/// Calling a mutable-receiver trait method on an immutable trait object
5127
/// must be rejected.
5128
@test fn testResolveTraitMutMethodOnImmutableObject() throws (testing::TestError) {
5129
    let mut a = testResolver();
5130
    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); }";
5131
    let result = try resolveProgramStr(&mut a, program);
5132
    try expectErrorKind(&result, super::ErrorKind::ImmutableBinding);
5133
}
5134
5135
/// Immutable methods on an immutable trait object should be accepted.
5136
@test fn testResolveTraitImmutableMethodOnImmutableObject() throws (testing::TestError) {
5137
    let mut a = testResolver();
5138
    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(); }";
5139
    let result = try resolveProgramStr(&mut a, program);
5140
    try expectNoErrors(&result);
5141
}
5142
5143
/// Both mutable and immutable methods on a mutable trait object should work.
5144
@test fn testResolveTraitMixedMethodsOnMutableObject() throws (testing::TestError) {
5145
    let mut a = testResolver();
5146
    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(); }";
5147
    let result = try resolveProgramStr(&mut a, program);
5148
    try expectNoErrors(&result);
5149
}
5150
5151
/// Instance method body type must match the trait return type.
5152
/// The trait declares `-> i32` but the body returns `bool`.
5153
@test fn testResolveInstanceReturnTypeMismatch() throws (testing::TestError) {
5154
    let mut a = testResolver();
5155
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() -> bool { return true; } }";
5156
    let result = try resolveProgramStr(&mut a, program);
5157
    let err = try expectError(&result);
5158
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5159
        else throw testing::TestError::Failed;
5160
}
5161
5162
/// Diamond supertrait inheritance: traits B and C both extend A.
5163
/// Declaring them independently should work fine.
5164
@test fn testResolveTraitDiamondSupertrait() throws (testing::TestError) {
5165
    let mut a = testResolver();
5166
    let program = "trait A { fn (*A) f() -> i32; } trait B: A { fn (*B) g() -> i32; } trait C: A { fn (*C) h() -> i32; }";
5167
    let result = try resolveProgramStr(&mut a, program);
5168
    try expectNoErrors(&result);
5169
}
5170
5171
/// Diamond supertrait with a combined trait that would cause duplicate
5172
/// method names should be detected.
5173
@test fn testResolveTraitDiamondDuplicateMethod() throws (testing::TestError) {
5174
    let mut a = testResolver();
5175
    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; }";
5176
    let result = try resolveProgramStr(&mut a, program);
5177
    // B inherits `f` from A, C inherits `f` from A. D: B + C sees duplicate `f`.
5178
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("f"));
5179
}
5180
5181
/// Supertrait instance must exist when declaring a combined trait instance.
5182
@test fn testResolveInstanceMissingSupertraitInstance() throws (testing::TestError) {
5183
    let mut a = testResolver();
5184
    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; } }";
5185
    let result = try resolveProgramStr(&mut a, program);
5186
    try expectErrorKind(&result, super::ErrorKind::MissingSupertraitInstance("Base"));
5187
}
5188
5189
/// Instance method omits return type when the trait declares `-> i32`.
5190
/// This is rejected -- the return type must be stated explicitly.
5191
@test fn testResolveInstanceReturnTypeOmitted() throws (testing::TestError) {
5192
    let mut a = testResolver();
5193
    let program = "record R { x: i32 } trait T { fn (*T) get() -> i32; } instance T for R { fn (r: *R) get() { } }";
5194
    let result = try resolveProgramStr(&mut a, program);
5195
    let err = try expectError(&result);
5196
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5197
        else throw testing::TestError::Failed;
5198
}
5199
5200
/// Instance method declares throws but the trait method does not throw.
5201
@test fn testResolveInstanceThrowsMismatchExtra() throws (testing::TestError) {
5202
    let mut a = testResolver();
5203
    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; } }";
5204
    let result = try resolveProgramStr(&mut a, program);
5205
    let err = try expectError(&result);
5206
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
5207
        else throw testing::TestError::Failed;
5208
}
5209
5210
/// Instance method declares a different throws type than the trait.
5211
@test fn testResolveInstanceThrowsMismatchWrongType() throws (testing::TestError) {
5212
    let mut a = testResolver();
5213
    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; } }";
5214
    let result = try resolveProgramStr(&mut a, program);
5215
    let err = try expectError(&result);
5216
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5217
        else throw testing::TestError::Failed;
5218
}
5219
5220
/// Instance method omits throws clause when trait declares throws.
5221
/// This is rejected -- the throws clause must match exactly.
5222
@test fn testResolveInstanceThrowsOmitted() throws (testing::TestError) {
5223
    let mut a = testResolver();
5224
    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; } }";
5225
    let result = try resolveProgramStr(&mut a, program);
5226
    let err = try expectError(&result);
5227
    let case super::ErrorKind::FnThrowCountMismatch(_) = err.kind
5228
        else throw testing::TestError::Failed;
5229
}
5230
5231
/// Instance method correctly matches the trait's throws clause.
5232
@test fn testResolveInstanceThrowsMatch() throws (testing::TestError) {
5233
    let mut a = testResolver();
5234
    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; } }";
5235
    let result = try resolveProgramStr(&mut a, program);
5236
    try expectNoErrors(&result);
5237
}
5238
5239
// Constant expression folding tests //////////////////////////////////////////
5240
5241
/// Resolve a program and verify that the constant at the given statement index
5242
/// has the expected integer magnitude.
5243
fn expectConstFold(program: *[u8], stmtIdx: u32, expected: u64)
5244
    throws (testing::TestError)
5245
{
5246
    let mut a = testResolver();
5247
    let result = try resolveProgramStr(&mut a, program);
5248
    try expectNoErrors(&result);
5249
5250
    let stmt = try getBlockStmt(result.root, stmtIdx);
5251
    let sym = super::symbolFor(&a, stmt)
5252
        else throw testing::TestError::Failed;
5253
    let case super::SymbolData::Constant { value, .. } = sym.data
5254
        else throw testing::TestError::Failed;
5255
    let val = value else throw testing::TestError::Failed;
5256
    let case super::ConstValue::Int(intVal) = val
5257
        else throw testing::TestError::Failed;
5258
    try testing::expect(intVal.magnitude == expected);
5259
}
5260
5261
/// Test arithmetic constant folding: add, sub, mul, div.
5262
@test fn testConstExprArithmetic() throws (testing::TestError) {
5263
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B;", 2, 30);
5264
    try expectConstFold("constant A: i32 = 50; constant B: i32 = 20; constant C: i32 = A - B;", 2, 30);
5265
    try expectConstFold("constant A: i32 = 6; constant B: i32 = 7; constant C: i32 = A * B;", 2, 42);
5266
    try expectConstFold("constant A: i32 = 100; constant B: i32 = 5; constant C: i32 = A / B;", 2, 20);
5267
}
5268
5269
/// Test bitwise constant folding: and, or, xor.
5270
@test fn testConstExprBitwise() throws (testing::TestError) {
5271
    try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A & B;", 2, 0x0F);
5272
    try expectConstFold("constant A: i32 = 0xF0; constant B: i32 = 0x0F; constant C: i32 = A | B;", 2, 0xFF);
5273
    try expectConstFold("constant A: i32 = 0xFF; constant B: i32 = 0x0F; constant C: i32 = A ^ B;", 2, 0xF0);
5274
}
5275
5276
/// Test shift constant folding.
5277
@test fn testConstExprShift() throws (testing::TestError) {
5278
    try expectConstFold("constant A: i32 = 1; constant B: i32 = A << 4;", 1, 16);
5279
    try expectConstFold("constant A: i32 = 32; constant B: i32 = A >> 2;", 1, 8);
5280
}
5281
5282
/// Test chained constant expressions (C depends on A + B, D depends on C).
5283
@test fn testConstExprChained() throws (testing::TestError) {
5284
    try expectConstFold("constant A: i32 = 10; constant B: i32 = 20; constant C: i32 = A + B; constant D: i32 = C * 2;", 3, 60);
5285
}
5286
5287
/// Test constant expression used as array size.
5288
@test fn testConstExprAsArraySize() throws (testing::TestError) {
5289
    let mut a = testResolver();
5290
    let program = "constant A: u32 = 2; constant B: u32 = 3; constant SIZE: u32 = A + B; constant ARR: [i32; SIZE] = [1, 2, 3, 4, 5];";
5291
    let result = try resolveProgramStr(&mut a, program);
5292
    try expectNoErrors(&result);
5293
5294
    let arrStmt = try getBlockStmt(result.root, 3);
5295
    let sym = super::symbolFor(&a, arrStmt)
5296
        else throw testing::TestError::Failed;
5297
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5298
        else throw testing::TestError::Failed;
5299
    try testing::expect(arrType.length == 5);
5300
}
5301
5302
/// Test cross-module constant expression: a constant in one module references
5303
/// a constant from another module via scope access.
5304
@test fn testCrossModuleConstExpr() throws (testing::TestError) {
5305
    let mut a = testResolver();
5306
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5307
5308
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena);
5309
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant BASE: i32 = 100;", &mut arena);
5310
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant DERIVED: i32 = consts::BASE + 50;", &mut arena);
5311
5312
    let result = try resolveModuleTree(&mut a, rootId);
5313
    try expectNoErrors(&result);
5314
}
5315
5316
/// Test cross-module constant expression used as array size.
5317
@test fn testCrossModuleConstExprArraySize() throws (testing::TestError) {
5318
    let mut a = testResolver();
5319
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5320
5321
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; unsafe mod app;", &mut arena);
5322
    let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export constant WIDTH: u32 = 8; export constant HEIGHT: u32 = 4;", &mut arena);
5323
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; constant TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = undefined;", &mut arena);
5324
5325
    let result = try resolveModuleTree(&mut a, rootId);
5326
    try expectNoErrors(&result);
5327
}
5328
5329
/// Test that non-constant expressions in constant declarations are still rejected.
5330
@test fn testConstExprNonConstRejected() throws (testing::TestError) {
5331
    let mut a = testResolver();
5332
    let program = "fn value() -> i32 { return 1; } constant BAD: i32 = value() + 1;";
5333
    let result = try resolveProgramStr(&mut a, program);
5334
    let err = try expectError(&result);
5335
    let case super::ErrorKind::ConstExprRequired = err.kind
5336
        else throw testing::TestError::Failed;
5337
}
5338
5339
/// Test unary negation in constant expressions.
5340
@test fn testConstExprUnaryNeg() throws (testing::TestError) {
5341
    let mut a = testResolver();
5342
    let program = "constant A: i32 = 10; constant B: i32 = -A;";
5343
    let result = try resolveProgramStr(&mut a, program);
5344
    try expectNoErrors(&result);
5345
}
5346
5347
/// Test unary not in constant expressions.
5348
@test fn testConstExprUnaryNot() throws (testing::TestError) {
5349
    let mut a = testResolver();
5350
    let program = "constant A: bool = true; constant B: bool = not A;";
5351
    let result = try resolveProgramStr(&mut a, program);
5352
    try expectNoErrors(&result);
5353
}
5354
5355
/// Test `as` casts in constant expressions: widening, narrowing, sign changes, chaining.
5356
@test fn testConstExprCast() throws (testing::TestError) {
5357
    try expectConstFold("constant A: i32 = 42; constant B: u64 = A as u64;", 1, 42);
5358
    try expectConstFold("constant A: u64 = 10; constant B: u8 = A as u8;", 1, 10);
5359
    try expectConstFold("constant A: i32 = 7; constant B: u32 = A as u32;", 1, 7);
5360
    try expectConstFold("constant A: u32 = 100; constant B: i32 = A as i32;", 1, 100);
5361
    try expectConstFold("constant A: u8 = 5; constant B: u64 = (A as u32) as u64;", 1, 5);
5362
    try expectConstFold("constant A: u8 = 3; constant B: u8 = 4; constant C: i32 = (A as i32) + (B as i32);", 2, 7);
5363
    // Cast of unsuffixed literal arithmetic.
5364
    try expectConstFold("constant A: u32 = (3 + 4) as u32;", 0, 7);
5365
    try expectConstFold("constant A: u32 = ((3 + 4) as u64) as u32;", 0, 7);
5366
    try expectConstFold("constant A: u32 = (3 + 4) as u32 + 1;", 0, 8);
5367
    try expectConstFold("constant A: i32 = (2 as i32) * (3 + 4);", 0, 14);
5368
}
5369
5370
/// Test `as` cast in constant expressions used as array size.
5371
@test fn testConstExprCastAsArraySize() throws (testing::TestError) {
5372
    let mut a = testResolver();
5373
    let program = "constant LEN: u64 = 4; constant SIZE: u32 = LEN as u32; constant ARR: [i32; SIZE] = [1, 2, 3, 4];";
5374
    let result = try resolveProgramStr(&mut a, program);
5375
    try expectNoErrors(&result);
5376
5377
    let arrStmt = try getBlockStmt(result.root, 2);
5378
    let sym = super::symbolFor(&a, arrStmt)
5379
        else throw testing::TestError::Failed;
5380
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
5381
        else throw testing::TestError::Failed;
5382
    try testing::expect(arrType.length == 4);
5383
}
5384
5385
/// Test unsuffixed integer literals in constant expressions.
5386
@test fn testConstExprUnsuffixedLiterals() throws (testing::TestError) {
5387
    try expectConstFold("constant A: u32 = 4 * 4;", 0, 16);
5388
    try expectConstFold("constant B: u32 = 10; constant C: u32 = B * 2;", 1, 20);
5389
    try expectConstFold("constant D: u32 = 3 + 7;", 0, 10);
5390
    try expectConstFold("constant E: u32 = 2 * 3 + 4;", 0, 10);
5391
    try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7);
5392
}
5393
5394
/// References cannot escape through return types.
5395
@test fn testRefReturnRejected() throws (testing::TestError) {
5396
    let mut a = testResolver();
5397
    let program = "record Marker: Linear {} fn bad(value: &u32) -> &u32 { return value; }";
5398
    let result = try resolveProgramStr(&mut a, program);
5399
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5400
}
5401
5402
/// Field observation cannot read an owner after it was moved.
5403
@test fn testLinearFieldObserveAfterMoveRejected() throws (testing::TestError) {
5404
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run() { let token = Token { value: 1 }; consume(token); token.value; }";
5405
    try expectLinearUseAfterConsume(program, "token", 140);
5406
}
5407
5408
/// Dereferencing cannot read an owner after it was moved.
5409
@test fn testLinearDerefAfterMoveRejected() throws (testing::TestError) {
5410
    let program = "fn consume(pointer: *u32) { consume(pointer); } fn run(pointer: *u32) { consume(pointer); *pointer; }";
5411
    try expectLinearUseAfterConsume(program, "pointer", 91);
5412
}
5413
5414
/// Immutable borrowing cannot borrow an owner after it was moved.
5415
@test fn testLinearBorrowAfterMoveRejected() throws (testing::TestError) {
5416
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn inspect(token: &Token) {} fn run() { let token = Token { value: 1 }; consume(token); inspect(&token); }";
5417
    try expectLinearUseAfterConsume(program, "token", 178);
5418
}
5419
5420
/// Mutable borrowing cannot borrow an owner after it was moved.
5421
@test fn testLinearMutableBorrowAfterMoveRejected() throws (testing::TestError) {
5422
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn inspect(token: &mut Token) {} fn run() { let mut token = Token { value: 1 }; consume(token); inspect(&mut token); }";
5423
    try expectLinearUseAfterConsume(program, "token", 190);
5424
}
5425
5426
/// A non-consuming receiver cannot borrow an owner after it was moved.
5427
@test fn testLinearRefReceiverAfterMoveRejected() throws (testing::TestError) {
5428
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn (token: &Token) inspect() {} fn run() { let token = Token { value: 1 }; consume(token); token.inspect(); }";
5429
    try expectLinearUseAfterConsume(program, "token", 172);
5430
}
5431
5432
/// A branch-joined consumed owner remains unavailable afterward.
5433
@test fn testLinearObserveAfterBranchJoinedMoveRejected() throws (testing::TestError) {
5434
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token { value: 1 }; if flag { consume(token); } else { consume(token); } token.value; }";
5435
    try expectLinearUseAfterConsume(program, "token", 187);
5436
}
5437
5438
/// Observation before exactly one move remains valid.
5439
@test fn testLinearObserveBeforeMoveAllowed() throws (testing::TestError) {
5440
    let program = "record Token: Linear { value: u32 } fn consume(token: Token) { consume(token); } fn run() { let token = Token { value: 1 }; token.value; consume(token); }";
5441
    try expectAnalyzeOk(program);
5442
}
5443
5444
/// A short-circuit RHS cannot be the only path that consumes an owner.
5445
@test fn testLinearShortCircuitConsumptionRejected() throws (testing::TestError) {
5446
    {
5447
        let mut a = testResolver();
5448
        let program = "record Token: Linear {} fn consume(token: Token) { consume(token); } fn take(token: Token) -> bool { consume(token); return true; } fn run(flag: bool) { let token = Token {}; flag and take(token); }";
5449
        let result = try resolveProgramStr(&mut a, program);
5450
        try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5451
    } {
5452
        let mut a = testResolver();
5453
        let program = "record Token: Linear {} fn consume(token: Token) { consume(token); } fn take(token: Token) -> bool { consume(token); return true; } fn run(flag: bool) { let token = Token {}; flag or take(token); }";
5454
        let result = try resolveProgramStr(&mut a, program);
5455
        try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5456
    }
5457
}
5458
5459
/// Short-circuit branches with identical owner states remain valid.
5460
@test fn testLinearShortCircuitIdenticalStatesAllowed() throws (testing::TestError) {
5461
    try expectAnalyzeOk(
5462
        "record Token: Linear {} fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token {}; flag and true; consume(token); }"
5463
    );
5464
    try expectAnalyzeOk(
5465
        "record Token: Linear {} fn consume(token: Token) { consume(token); } fn run(flag: bool) { let token = Token {}; flag or false; consume(token); }"
5466
    );
5467
}
5468
5469
/// A propagated error exit must not leave an owner available.
5470
@test fn testLinearTryPropagationChecksErrorExit() throws (testing::TestError) {
5471
    let mut a = testResolver();
5472
    let program = "union Failure { Failed } record Token: Linear {} fn consume(token: Token) { consume(token); } fn fail() throws (Failure) { throw Failure::Failed; } fn run(token: Token) throws (Failure) { try fail(); consume(token); }";
5473
    let result = try resolveProgramStr(&mut a, program);
5474
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token"));
5475
}
5476
5477
/// Propagation remains valid when the call consumes every live owner.
5478
@test fn testLinearTryPropagationAfterConsumptionAllowed() throws (testing::TestError) {
5479
    let program = "union Failure { Failed } record Token: Linear {} fn consume(token: Token) { consume(token); } fn fail(token: Token) throws (Failure) { consume(token); throw Failure::Failed; } fn run(token: Token) throws (Failure) { try fail(token); }";
5480
    try expectAnalyzeOk(program);
5481
}
5482
5483
/// Case-pattern fallbacks must terminate instead of synthesizing bindings.
5484
@test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5485
    let mut a = testResolver();
5486
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
5487
    let result = try resolveProgramStr(&mut a, program);
5488
    try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
5489
}
5490
5491
/// Case bindings are unavailable on the pattern-failure path.
5492
@test fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5493
    let mut a = testResolver();
5494
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
5495
    let result = try resolveProgramStr(&mut a, program);
5496
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
5497
}
5498
5499
/// Unsafe pointer dereference requires an unsafe declaration.
5500
@test fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5501
    let mut a = testResolver();
5502
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5503
    let result = try resolveProgramStr(&mut a, program);
5504
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5505
}
5506
5507
/// Unsafe pointers remain freely copyable inside an unsafe declaration.
5508
@test fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5509
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5510
    try expectAnalyzeOk(program);
5511
}
5512
5513
/// Safe code cannot call a function that accepts unsafe operations.
5514
@test fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5515
    let mut a = testResolver();
5516
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
5517
    let result = try resolveProgramStr(&mut a, program);
5518
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5519
}
5520
5521
/// Unsafe function values retain their call-site safety requirement.
5522
@test fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5523
    let mut a = testResolver();
5524
    let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
5525
    let result = try resolveProgramStr(&mut a, program);
5526
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5527
}
5528
5529
/// A safe function may dereference an unsafe pointer inside an unsafe block.
5530
@test fn testUnsafeBlockPointerOperationAllowed() throws (testing::TestError) {
5531
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { *pointer; } } fn run(pointer: *unsafe u32) { load(pointer); }";
5532
    try expectAnalyzeOk(program);
5533
}
5534
5535
/// An unsafe block permits calls to unsafe functions.
5536
@test fn testUnsafeBlockCallAllowed() throws (testing::TestError) {
5537
    let program = "unsafe fn dangerous() {} fn run() { unsafe { dangerous(); } }";
5538
    try expectAnalyzeOk(program);
5539
}
5540
5541
/// Unsafe context ends at the closing brace of an unsafe block.
5542
@test fn testUnsafeBlockContextDoesNotLeak() throws (testing::TestError) {
5543
    let mut a = testResolver();
5544
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { *pointer; } *pointer; }";
5545
    let result = try resolveProgramStr(&mut a, program);
5546
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5547
}
5548
5549
/// Leaving a nested unsafe block preserves the enclosing unsafe context.
5550
@test fn testNestedUnsafeBlockPreservesContext() throws (testing::TestError) {
5551
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) { unsafe { unsafe { *pointer; } *pointer; } }";
5552
    try expectAnalyzeOk(program);
5553
}
5554
5555
/// Linear consumption inside an unsafe block remains visible after the block.
5556
@test fn testUnsafeBlockPreservesLinearConsumption() throws (testing::TestError) {
5557
    let mut a = testResolver();
5558
    let program = "union Token: Linear { Value(u32) } fn consume(token: Token) { match token { case Token::Value(value) => {} } } fn run() { let token = Token::Value(1); unsafe { consume(token); } consume(token); }";
5559
    let result = try resolveProgramStr(&mut a, program);
5560
    try expectErrorKind(
5561
        &result,
5562
        super::ErrorKind::LinearUseAfterConsume("token"),
5563
    );
5564
}
5565
5566
/// Pointer arithmetic requires an unsafe context.
5567
@test fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) {
5568
    let mut a = testResolver();
5569
    let program = "fn advance(pointer: *u32) -> *u32 { return pointer + 1; }";
5570
    let result = try resolveProgramStr(&mut a, program);
5571
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5572
}
5573
5574
/// An unsafe block permits pointer arithmetic.
5575
@test fn testPointerArithmeticAllowedInUnsafeBlock() throws (testing::TestError) {
5576
    let program = "fn advance(pointer: *u32) -> *u32 { unsafe { return pointer + 1; } }";
5577
    try expectAnalyzeOk(program);
5578
}
5579
5580
/// Unchecked slice construction requires an unsafe context.
5581
@test fn testSliceOfRequiresUnsafe() throws (testing::TestError) {
5582
    let mut a = testResolver();
5583
    let program = "fn make(pointer: *u32) -> *[u32] { return @sliceOf(pointer, 1); }";
5584
    let result = try resolveProgramStr(&mut a, program);
5585
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5586
}
5587
5588
/// An unsafe block permits unchecked slice construction.
5589
@test fn testSliceOfAllowedInUnsafeBlock() throws (testing::TestError) {
5590
    let program = "fn make(pointer: *u32) -> *[u32] { unsafe { return @sliceOf(pointer, 1); } }";
5591
    try expectAnalyzeOk(program);
5592
}
5593
5594
/// Undefined source values require an unsafe context.
5595
@test fn testUndefinedRequiresUnsafe() throws (testing::TestError) {
5596
    let mut a = testResolver();
5597
    let program = "fn value() -> u32 { return undefined; }";
5598
    let result = try resolveProgramStr(&mut a, program);
5599
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5600
}
5601
5602
/// An unsafe block permits an undefined source value.
5603
@test fn testUndefinedAllowedInUnsafeBlock() throws (testing::TestError) {
5604
    let program = "fn value() -> u32 { unsafe { return undefined; } }";
5605
    try expectAnalyzeOk(program);
5606
}
5607
5608
/// A safe static initializer cannot use an undefined value.
5609
@test fn testUndefinedStaticRequiresUnsafe() throws (testing::TestError) {
5610
    let mut a = testResolver();
5611
    let result = try resolveProgramStr(&mut a, "static VALUE: u32 = undefined;");
5612
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5613
}
5614
5615
/// An unsafe static initializer may use an undefined value.
5616
@test fn testUndefinedAllowedInUnsafeStatic() throws (testing::TestError) {
5617
    try expectAnalyzeOk("unsafe static VALUE: u32 = undefined;");
5618
}
5619
5620
/// References cannot be embedded in aggregate fields.
5621
@test fn testRefFieldRejected() throws (testing::TestError) {
5622
    let mut a = testResolver();
5623
    let program = "record Marker: Linear {} record Bad { value: &u32 }";
5624
    let result = try resolveProgramStr(&mut a, program);
5625
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5626
}
5627
5628
/// Trait methods may use reference receivers.
5629
@test fn testTraitRefReceiver() throws (testing::TestError) {
5630
    let program = "record Marker: Linear {} 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); }";
5631
    try expectAnalyzeOk(program);
5632
}
5633
5634
/// Trait implementations must preserve the receiver pointer class.
5635
@test fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5636
    let mut a = testResolver();
5637
    let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
5638
    let result = try resolveProgramStr(&mut a, program);
5639
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
5640
}
5641
5642
/// The compiler-known marker cannot be derived more than once.
5643
@test fn testDuplicateLinearMarkerRejected() throws (testing::TestError) {
5644
    let mut a = testResolver();
5645
    let program = "record Token: Linear + Linear { value: u32 }";
5646
    let result = try resolveProgramStr(&mut a, program);
5647
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Linear"));
5648
}
5649
5650
/// Address-of inference creates a loan that cannot be stored in a local.
5651
@test fn testAddressOfInferenceCreatesLoan() throws (testing::TestError) {
5652
    let mut a = testResolver();
5653
    let program = "fn run() { let value: u32 = 0; let pointer = &value; }";
5654
    let result = try resolveProgramStr(&mut a, program);
5655
    try expectErrorKind(&result, super::ErrorKind::RefBinding);
5656
}
5657
5658
/// Taking a local address cannot create an owner that escapes the function.
5659
@test fn testAddressOfLocalCannotCreateOwner() throws (testing::TestError) {
5660
    let mut a = testResolver();
5661
    let program = "fn bad() -> *u32 { let value: u32 = 0; return &value; }";
5662
    let result = try resolveProgramStr(&mut a, program);
5663
    let err = try expectError(&result);
5664
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5665
        else throw testing::TestError::Failed;
5666
}
5667
5668
/// A loan cannot be converted into an owning pointer.
5669
@test fn testReferenceCannotBecomeOwner() throws (testing::TestError) {
5670
    let mut a = testResolver();
5671
    let program = "fn bad(value: &u32) -> *u32 { return value; }";
5672
    let result = try resolveProgramStr(&mut a, program);
5673
    let err = try expectError(&result);
5674
    let case super::ErrorKind::TypeMismatch(_) = err.kind
5675
        else throw testing::TestError::Failed;
5676
}
5677
5678
/// References are rejected from every nested or storable type position.
5679
@test fn testNestedRefPositionsRejected() throws (testing::TestError) {
5680
    {
5681
        let mut a = testResolver();
5682
        let program = "record Marker: Linear {} union Bad { Value(&u32) }";
5683
        let result = try resolveProgramStr(&mut a, program);
5684
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5685
    } {
5686
        let mut a = testResolver();
5687
        let program = "record Marker: Linear {} fn bad(value: ?&u32) {}";
5688
        let result = try resolveProgramStr(&mut a, program);
5689
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5690
    } {
5691
        let mut a = testResolver();
5692
        let program = "record Marker: Linear {} fn bad(value: [&u32; 1]) {}";
5693
        let result = try resolveProgramStr(&mut a, program);
5694
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5695
    } {
5696
        let mut a = testResolver();
5697
        let program = "record Marker: Linear {} fn bad(value: *&u32) {}";
5698
        let result = try resolveProgramStr(&mut a, program);
5699
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5700
    } {
5701
        let mut a = testResolver();
5702
        let program = "record Marker: Linear {} static BAD: &u32 = undefined;";
5703
        let result = try resolveProgramStr(&mut a, program);
5704
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5705
    } {
5706
        let mut a = testResolver();
5707
        let program = "record Marker: Linear {} fn bad(callback: fn() -> &u32) {}";
5708
        let result = try resolveProgramStr(&mut a, program);
5709
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5710
    }
5711
}
5712
5713
/// Function pointer parameter references remain call-scoped and valid.
5714
@test fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5715
    let program = "record Marker: Linear {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }";
5716
    try expectAnalyzeOk(program);
5717
}
5718
5719
/// Pointer and slice casts cannot change reference ownership.
5720
@test fn testRefCastClassPreserved() throws (testing::TestError) {
5721
    {
5722
        let mut a = testResolver();
5723
        let program = "record Marker: Linear {} fn cast(value: &u32) { value as *u32; }";
5724
        let result = try resolveProgramStr(&mut a, program);
5725
        let err = try expectError(&result);
5726
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5727
            else throw testing::TestError::Failed;
5728
    } {
5729
        let mut a = testResolver();
5730
        let program = "record Marker: Linear {} fn cast(values: &[u32]) { values as *[u32]; }";
5731
        let result = try resolveProgramStr(&mut a, program);
5732
        let err = try expectError(&result);
5733
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5734
            else throw testing::TestError::Failed;
5735
    }
5736
}
5737
5738
/// Every operation that interprets an unsafe address requires an unsafe declaration.
5739
@test fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5740
    {
5741
        let mut a = testResolver();
5742
        let program = "record Marker: Linear {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
5743
        let result = try resolveProgramStr(&mut a, program);
5744
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5745
    } {
5746
        let mut a = testResolver();
5747
        let program = "record Marker: Linear {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }";
5748
        let result = try resolveProgramStr(&mut a, program);
5749
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5750
    } {
5751
        let mut a = testResolver();
5752
        let program = "record Marker: Linear {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }";
5753
        let result = try resolveProgramStr(&mut a, program);
5754
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5755
    } {
5756
        let mut a = testResolver();
5757
        let program = "record Marker: Linear {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }";
5758
        let result = try resolveProgramStr(&mut a, program);
5759
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5760
    } {
5761
        let mut a = testResolver();
5762
        let program = "record Marker: Linear {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }";
5763
        let result = try resolveProgramStr(&mut a, program);
5764
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5765
    } {
5766
        let mut a = testResolver();
5767
        let program = "record Marker: Linear {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }";
5768
        let result = try resolveProgramStr(&mut a, program);
5769
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5770
    } {
5771
        let mut a = testResolver();
5772
        let program = "record Marker: Linear {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }";
5773
        let result = try resolveProgramStr(&mut a, program);
5774
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5775
    }
5776
}
5777
5778
/// Unsafe declarations may compose unsafe operations and calls.
5779
@test fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5780
    let program = "record Marker: Linear {} 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); }";
5781
    try expectAnalyzeOk(program);
5782
}
5783
5784
/// Unsafe code may drop a checked reference to an unsafe pointer.
5785
@test fn testUnsafePointerFromReference() throws (testing::TestError) {
5786
    let program = "record Marker: Linear {} 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); }";
5787
    try expectAnalyzeOk(program);
5788
}
5789
5790
/// Dropping a reference to an unsafe pointer cannot add mutability.
5791
@test fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5792
    let mut a = testResolver();
5793
    let program = "record Marker: Linear {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
5794
    let result = try resolveProgramStr(&mut a, program);
5795
    let err = try expectError(&result);
5796
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5797
        else throw testing::TestError::Failed;
5798
}
5799
5800
/// Recursive cast validation cannot hide a checked-to-unsafe transition.
5801
@test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5802
    let mut a = testResolver();
5803
    let program = "record Marker: Linear {} fn run(value: &*u32) { unsafe { value as **unsafe u32; } }";
5804
    let result = try resolveProgramStr(&mut a, program);
5805
    let err = try expectError(&result);
5806
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5807
        else throw testing::TestError::Failed;
5808
}
5809
5810
/// Unsafe code may drop a checked slice reference to an unsafe slice.
5811
@test fn testUnsafeSliceFromReference() throws (testing::TestError) {
5812
    let program = "record Marker: Linear {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }";
5813
    try expectAnalyzeOk(program);
5814
}
5815
5816
/// Slice casts cannot add mutability.
5817
@test fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5818
    let mut a = testResolver();
5819
    let program = "record Marker: Linear {} fn run(values: &[u32]) { values as &mut [u32]; }";
5820
    let result = try resolveProgramStr(&mut a, program);
5821
    let err = try expectError(&result);
5822
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5823
        else throw testing::TestError::Failed;
5824
}
5825
5826
/// Mutable unsafe receivers do not create checked exclusive loans.
5827
@test fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5828
    let program = "record Marker: Linear {} 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); }";
5829
    try expectAnalyzeOk(program);
5830
}
5831
5832
/// Unsafe instance-method attributes enable unsafe operations in the body.
5833
@test fn testUnsafeInstanceMethodBody() throws (testing::TestError) {
5834
    let program = "record Marker: Linear {} 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; } }";
5835
    try expectAnalyzeOk(program);
5836
}
5837
5838
/// Unsafe instance methods cannot implement safe trait contracts.
5839
@test fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5840
    let mut a = testResolver();
5841
    let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
5842
    let result = try resolveProgramStr(&mut a, program);
5843
    try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
5844
}
5845
5846
/// Unsafe trait methods retain their call-site requirement through dispatch.
5847
@test fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5848
    let mut a = testResolver();
5849
    let program = "record Marker: Linear {} 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(); }";
5850
    let result = try resolveProgramStr(&mut a, program);
5851
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5852
}
5853
5854
/// Module-qualified statics retain one loan root across call arguments.
5855
@test fn testScopedStaticLoanConflicts() throws (testing::TestError) {
5856
    {
5857
        let mut a = testResolver();
5858
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5859
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_mut_root", "export mod values; mod app;", &mut arena);
5860
        let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0;", &mut arena);
5861
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_mut_root::values; fn both(left: &mut u32, right: &mut u32) {} fn run() { both(&mut values::X, &mut values::X); }", &mut arena);
5862
        let result = try resolveModuleTree(&mut a, rootId);
5863
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("X"));
5864
    } {
5865
        let mut a = testResolver();
5866
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5867
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_shared_root", "export mod values; mod app;", &mut arena);
5868
        let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0;", &mut arena);
5869
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_shared_root::values; fn both(left: &u32, right: &mut u32) {} fn run() { both(&values::X, &mut values::X); }", &mut arena);
5870
        let result = try resolveModuleTree(&mut a, rootId);
5871
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("X"));
5872
    }
5873
}
5874
5875
/// Distinct module-qualified statics do not overlap.
5876
@test fn testDistinctScopedStaticLoansAllowed() throws (testing::TestError) {
5877
    let mut a = testResolver();
5878
    let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5879
    let rootId = try registerModule(&mut MODULE_GRAPH, nil, "loan_distinct_root", "export mod values; mod app;", &mut arena);
5880
    let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export static X: u32 = 0; export static Y: u32 = 0;", &mut arena);
5881
    let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use loan_distinct_root::values; fn both(left: &mut u32, right: &mut u32) {} fn run() { both(&mut values::X, &mut values::Y); }", &mut arena);
5882
    let result = try resolveModuleTree(&mut a, rootId);
5883
    try expectNoErrors(&result);
5884
}
5885
5886
/// Earlier argument loans remain active inside later nested calls.
5887
@test fn testOuterArgumentLoanConflictsWithNestedCall() throws (testing::TestError) {
5888
    {
5889
        let mut a = testResolver();
5890
        let program = "fn observe(value: &u32) -> u32 { return *value; } fn pair(first: &mut u32, second: u32) {} fn run() { let mut value: u32 = 0; pair(&mut value, observe(&value)); }";
5891
        let result = try resolveProgramStr(&mut a, program);
5892
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
5893
    } {
5894
        let mut a = testResolver();
5895
        let program = "record Token: Linear { value: u32 } fn consume(token: Token) -> u32 { return consume(token); } fn pair(first: &Token, second: u32) {} fn run() { let token = Token { value: 0 }; pair(&token, consume(token)); }";
5896
        let result = try resolveProgramStr(&mut a, program);
5897
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token"));
5898
    } {
5899
        let mut a = testResolver();
5900
        let program = "fn update(value: &mut u32) -> u32 { set *value = 1; return *value; } fn pair(first: &u32, second: u32) {} fn run() { let mut value: u32 = 0; pair(&value, update(&mut value)); }";
5901
        let result = try resolveProgramStr(&mut a, program);
5902
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
5903
    }
5904
}
5905
5906
/// Nested later arguments may use a different root.
5907
@test fn testNonOverlappingNestedCallArgumentsAllowed() throws (testing::TestError) {
5908
    let program = "fn observe(value: &u32) -> u32 { return *value; } fn pair(first: &mut u32, second: u32) {} fn run() { let mut left: u32 = 0; let right: u32 = 1; pair(&mut left, observe(&right)); }";
5909
    try expectAnalyzeOk(program);
5910
}
5911
5912
/// Linear values cannot live in reusable global declarations.
5913
@test fn testLinearGlobalsRejected() throws (testing::TestError) {
5914
    {
5915
        let mut a = testResolver();
5916
        let program = "record Token: Linear { value: u32 } static TOKEN: Token = Token { value: 1 };";
5917
        let result = try resolveProgramStr(&mut a, program);
5918
        try expectErrorKind(&result, super::ErrorKind::LinearDiscard);
5919
    } {
5920
        let mut a = testResolver();
5921
        let program = "record Token: Linear { value: u32 } constant TOKEN: Token = Token { value: 1 };";
5922
        let result = try resolveProgramStr(&mut a, program);
5923
        try expectErrorKind(&result, super::ErrorKind::LinearDiscard);
5924
    }
5925
    {
5926
        let mut a = testResolver();
5927
        let program = "constant TEXT: *[u8] = \"text\";";
5928
        let result = try resolveProgramStr(&mut a, program);
5929
        try expectErrorKind(&result, super::ErrorKind::LinearDiscard);
5930
    }
5931
}
5932
5933
/// Function-typed unsafe statics require unsafe access like other globals.
5934
@test fn testUnsafeFunctionStaticAccessRequiresUnsafe() throws (testing::TestError) {
5935
    let mut a = testResolver();
5936
    let program = "fn callback() {} unsafe static CALLBACK: fn() = callback; fn run() { CALLBACK(); }";
5937
    let result = try resolveProgramStr(&mut a, program);
5938
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5939
}
5940
5941
/// Assignments cannot overlap an earlier shared call-scoped loan.
5942
@test fn testOuterArgumentLoanConflictsWithLaterAssignment() throws (testing::TestError) {
5943
    let mut a = testResolver();
5944
    let program = "union Error { Fail } fn fail() -> i32 throws (Error) { return 0; } fn pair(first: &u32, second: i32) {} fn run() -> i32 { let mut value: u32 = 0; pair(&value, try fail() catch { set value = 1; return 0; }); return 0; }";
5945
    let result = try resolveProgramStr(&mut a, program);
5946
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
5947
}
5948
5949
/// Ordinary scalar globals remain reusable.
5950
@test fn testScalarGlobalsAllowed() throws (testing::TestError) {
5951
    try expectAnalyzeOk("static COUNT: u32 = 0; constant LIMIT: u32 = 1;");
5952
}
5953
5954
/// Explicit unsafe globals may hold linear values.
5955
@test fn testUnsafeLinearGlobalsAllowed() throws (testing::TestError) {
5956
    let program = "record Token: Linear { value: u32 } unsafe static TOKEN: Token = Token { value: 1 }; unsafe constant TEXT: *[u8] = \"text\";";
5957
    try expectAnalyzeOk(program);
5958
}
5959
5960
/// Reading an unsafe global requires unsafe context.
5961
@test fn testUnsafeGlobalAccessRequiresUnsafe() throws (testing::TestError) {
5962
    let mut a = testResolver();
5963
    let program = "unsafe static VALUE: u32 = 0; fn read() -> u32 { return VALUE; }";
5964
    let result = try resolveProgramStr(&mut a, program);
5965
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5966
}
5967
5968
/// Unsafe context permits reading an unsafe linear global.
5969
@test fn testUnsafeGlobalAccessAllowedInUnsafeBlock() throws (testing::TestError) {
5970
    let program = "unsafe constant TEXT: *[u8] = \"text\"; fn consume(value: *[u8]) { consume(value); } fn run() { unsafe { consume(TEXT); } }";
5971
    try expectAnalyzeOk(program);
5972
}
5973
5974
/// Exported unsafe globals retain their access requirement across modules.
5975
@test fn testScopedUnsafeGlobalAccess() throws (testing::TestError) {
5976
    {
5977
        let mut a = testResolver();
5978
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5979
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "unsafe_global_root", "export mod values; mod app;", &mut arena);
5980
        let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export unsafe static VALUE: u32 = 0;", &mut arena);
5981
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use unsafe_global_root::values; fn read() -> u32 { return values::VALUE; }", &mut arena);
5982
        let result = try resolveModuleTree(&mut a, rootId);
5983
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5984
    } {
5985
        let mut a = testResolver();
5986
        let mut arena = ast::nodeArena(&mut AST_ARENA[..]);
5987
        let rootId = try registerModule(&mut MODULE_GRAPH, nil, "unsafe_global_ok_root", "export mod values; mod app;", &mut arena);
5988
        let valuesId = try registerModule(&mut MODULE_GRAPH, rootId, "values", "export unsafe static VALUE: u32 = 0;", &mut arena);
5989
        let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use unsafe_global_ok_root::values; fn read() -> u32 { unsafe { return values::VALUE; } }", &mut arena);
5990
        let result = try resolveModuleTree(&mut a, rootId);
5991
        try expectNoErrors(&result);
5992
    }
5993
}
5994
5995
/// Identity casts do not hide duplicate or conflicting loans.
5996
@test fn testCastWrappedLoanConflicts() throws (testing::TestError) {
5997
    {
5998
        let mut a = testResolver();
5999
        let program = "fn both(left: &mut u32, right: &mut u32) {} fn run() { let mut value: u32 = 0; both(&mut value as &mut u32, &mut value); }";
6000
        let result = try resolveProgramStr(&mut a, program);
6001
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
6002
    } {
6003
        let mut a = testResolver();
6004
        let program = "fn both(left: &u32, right: &mut u32) {} fn run() { let mut value: u32 = 0; both(&value as &u32, &mut value); }";
6005
        let result = try resolveProgramStr(&mut a, program);
6006
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
6007
    }
6008
}