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