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