lib/std/lang/resolver.rad 314.0 KiB raw
1
//! Radiance semantic analyzer and type resolver.
2
//!
3
//! This module performs scope construction, symbol binding, and identifier
4
//! resolution on top of the AST produced by the parser.
5
6
export mod printer;
7
8
/// Unit tests for the resolver.
9
@test mod tests;
10
11
// TODO: Move to raw vectors to reduce list duplication?
12
// TODO: When a function declaration fails to typecheck, it should still "exist".
13
// TODO: `ensureNominalResolved` should just run when you call `typeFor`.
14
// TODO: Have different types for positional vs. named field records.
15
16
use std::mem;
17
use std::io;
18
use std::lang::alloc;
19
use std::lang::types;
20
use std::lang::ast;
21
use std::lang::parser;
22
use std::lang::module;
23
24
/// Maximum number of diagnostics recorded.
25
export constant MAX_ERRORS: u32 = 64;
26
27
/// Synthetic function name used when wrapping a bare expression for analysis.
28
export unsafe constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__";
29
/// Synthetic function name used when wrapping a block for analysis.
30
export unsafe constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__";
31
32
/// Maximum number of symbols stored within a module scope.
33
export constant MAX_MODULE_SYMBOLS: u32 = 512;
34
/// Maximum number of symbols stored within a local scope.
35
export constant MAX_LOCAL_SYMBOLS: u32 = 32;
36
/// Maximum function parameters.
37
export constant MAX_FN_PARAMS: u32 = 8;
38
/// Maximum function thrown types.
39
export constant MAX_FN_THROWS: u32 = 8;
40
/// Maximum number of variants in a union.
41
/// Nb. This should not be raised above `255`,
42
/// as tags are stored using 8-bits only.
43
export constant MAX_UNION_VARIANTS: u32 = 128;
44
/// Maximum nesting of loops.
45
export constant MAX_LOOP_DEPTH: u32 = 16;
46
/// Maximum trait instances.
47
export constant MAX_INSTANCES: u32 = 128;
48
/// Maximum standalone methods (across all types).
49
export constant MAX_METHODS: u32 = 256;
50
/// Maximum number of linear bindings active in one function.
51
constant MAX_LINEAR_BINDINGS: u32 = 32;
52
/// Maximum nesting depth tracked for loops.
53
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
54
55
/// Trait definition stored in the resolver.
56
export record TraitType {
57
    /// Trait name.
58
    name: *[u8],
59
    /// Method signatures, including from supertraits.
60
    methods: *mut [TraitMethod],
61
    /// Supertraits that must also be implemented.
62
    supertraits: *mut [*TraitType],
63
}
64
65
/// A single method signature within a trait.
66
export record TraitMethod {
67
    /// Method name.
68
    name: *[u8],
69
    /// Function type for the method, excluding the receiver.
70
    fnType: *FnType,
71
    /// Whether the receiver is mutable.
72
    mutable: bool,
73
    /// Pointer-like class used by the receiver.
74
    receiverClass: types::PointerClass,
75
    /// V-table slot index.
76
    index: u32,
77
}
78
79
/// An entry in the trait instance registry.
80
export record InstanceEntry {
81
    /// Trait type descriptor.
82
    traitType: *TraitType,
83
    /// Concrete type that implements the trait.
84
    concreteType: Type,
85
    /// Name of the concrete type.
86
    concreteTypeName: *[u8],
87
    /// Module where this instance was declared.
88
    moduleId: u16,
89
    /// Method symbols for each trait method, in declaration order.
90
    methods: *mut [*mut Symbol],
91
}
92
93
/// An entry in the method registry.
94
export record MethodEntry {
95
    /// Concrete type that owns the method.
96
    concreteType: Type,
97
    /// Name of the concrete type.
98
    concreteTypeName: *[u8],
99
    /// Method name.
100
    name: *[u8],
101
    /// Function type excluding the receiver.
102
    fnType: *FnType,
103
    /// Whether the receiver is mutable.
104
    mutable: bool,
105
    /// Pointer-like class used by the receiver.
106
    receiverClass: types::PointerClass,
107
    /// Symbol for the method.
108
    symbol: *mut Symbol,
109
}
110
111
/// Identifier for the synthetic `len` field.
112
export unsafe constant LEN_FIELD: *[u8] = "len";
113
/// Identifier for the synthetic `ptr` field.
114
export unsafe constant PTR_FIELD: *[u8] = "ptr";
115
/// Identifier for the synthetic `cap` field.
116
export unsafe constant CAP_FIELD: *[u8] = "cap";
117
118
/// Maximum `u16` value.
119
constant U16_MAX: u16 = 0xFFFF;
120
/// Maximum `u8` value.
121
constant U8_MAX: u16 = 0xFF;
122
123
/// Minimum `i8` value.
124
constant I8_MIN: i32 = -128;
125
/// Maximum `i8` value.
126
constant I8_MAX: i32 = 127;
127
/// Minimum `i16` value.
128
constant I16_MIN: i32 = -32768;
129
/// Maximum `i16` value.
130
constant I16_MAX: i32 = 32767;
131
132
/// Minimum `i32` value.
133
constant I32_MIN: i32 = -2147483648;
134
/// Maximum `i32` value.
135
constant I32_MAX: i32 = 2147483647;
136
/// Minimum `i64` value: -(2^63).
137
constant I64_MIN: i64 = -9223372036854775808;
138
/// Maximum `i64` value: 2^63 - 1.
139
constant I64_MAX: i64 = 9223372036854775807;
140
141
/// Size of a pointer in bytes.
142
export constant PTR_SIZE: u32 = 8;
143
144
/// Information about a record or tuple field.
145
export record RecordField {
146
    /// Field name, `nil` for positional fields.
147
    name: ?*[u8],
148
    /// Field type.
149
    fieldType: Type,
150
    /// Byte offset from the start of the record.
151
    offset: i32,
152
}
153
154
/// Information about a union variant.
155
record UnionVariant {
156
    name: *[u8],
157
    valueType: Type,
158
    symbol: *mut Symbol,
159
}
160
161
/// Array type payload.
162
export record ArrayType {
163
    item: *Type,
164
    length: u32,
165
}
166
167
/// Record nominal type.
168
export record RecordType {
169
    fields: *[RecordField],
170
    labeled: bool,
171
    /// Cached layout.
172
    layout: Layout,
173
    /// Whether the declaration explicitly carries the `Linear` marker.
174
    declaredLinear: bool,
175
}
176
177
/// Union nominal type.
178
export record UnionType {
179
    variants: *[UnionVariant],
180
    /// Cached layout.
181
    layout: Layout,
182
    /// Cached payload offset within the union aggregate.
183
    valOffset: u32,
184
    /// If all variants have void payloads.
185
    isAllVoid: bool,
186
    /// Whether the declaration explicitly carries the `Linear` marker.
187
    declaredLinear: bool,
188
}
189
190
/// Metadata for user-defined types.
191
export union NominalType {
192
    /// Placeholder for a type that hasn't been fully resolved yet.
193
    /// Stores the declaration node for lazy resolution.
194
    Placeholder(*ast::Node),
195
    Record(RecordType),
196
    Union(UnionType),
197
}
198
199
/// Coercion plan, when coercion from one type to another.
200
export union Coercion {
201
    /// No coercion, eg. `T -> T`.
202
    Identity,
203
    /// Eg. `u8 -> i32`. Stores both source and target types for lowering.
204
    NumericCast { from: Type, to: Type },
205
    /// Eg. `T -> ?T`. Stores the inner value type.
206
    OptionalLift(Type),
207
    /// Wrap return value in success variant of result type.
208
    ResultWrap,
209
    /// Coerce a concrete pointer to a trait object.
210
    TraitObject {
211
        /// Trait type information.
212
        traitInfo: *TraitType,
213
        /// Instance entry for v-table lookup.
214
        inst: *InstanceEntry,
215
    },
216
}
217
218
/// Result of resolving a module path.
219
record ResolvedModule {
220
    /// Module entry in the graph.
221
    entry: *module::ModuleEntry,
222
    /// Scope containing the module's declarations.
223
    scope: *mut Scope,
224
}
225
226
/// Type layout.
227
export record Layout {
228
    /// Size in bytes.
229
    size: u32,
230
    /// Alignment in bytes.
231
    alignment: u32,
232
}
233
234
/// Computed union layout parameters.
235
record UnionLayoutInfo {
236
    layout: Layout,
237
    valOffset: u32,
238
    isAllVoid: bool,
239
}
240
241
/// Pre-computed metadata for slice range expressions.
242
/// Used by the lowerer.
243
export record SliceRangeInfo {
244
    /// Element type of the resulting slice.
245
    itemType: *Type,
246
    /// Whether the resulting slice is mutable.
247
    mutable: bool,
248
    /// Static capacity if container is an array.
249
    capacity: ?u32,
250
}
251
252
/// Pre-computed metadata for `for` loop iteration.
253
/// Used by the lowerer to avoid re-analyzing the iterable type.
254
export union ForLoopInfo {
255
    /// Iterating over a range expression (e.g., `for i in 0..n`).
256
    Range {
257
        valType: *Type,
258
        range: ast::Range,
259
        bindingName: ?*[u8],
260
        indexName: ?*[u8]
261
    },
262
    /// Iterating over an array or slice. For arrays, the length field is set.
263
    Collection {
264
        elemType: *Type,
265
        length: ?u32,
266
        bindingName: ?*[u8],
267
        indexName: ?*[u8]
268
    },
269
}
270
271
/// Resolved function signature details.
272
export record FnType {
273
    paramTypes: *[*Type],
274
    returnType: *Type,
275
    throwList: *[*Type],
276
    /// Whether calling this function requires an unsafe context.
277
    isUnsafe: bool,
278
    localCount: u32,
279
}
280
281
/// Describes a type computed during semantic analysis.
282
export union Type {
283
    /// A type that couldn't be decided.
284
    Unknown,
285
    /// Types only used during inference.
286
    Nil, Undefined, Int,
287
    /// Primitive types.
288
    Void, Opaque, Never, Bool,
289
    /// Integer types.
290
    U8, U16, U32, U64, I8, I16, I32, I64,
291
    /// Range types, eg. `start..end`.
292
    Range {
293
        start: ?*Type,
294
        end: ?*Type,
295
    },
296
    /// Owning pointer-like address.
297
    Pointer {
298
        class: types::PointerClass,
299
        target: *Type,
300
        mutable: bool,
301
    },
302
    /// Owning slice.
303
    Slice {
304
        class: types::PointerClass,
305
        item: *Type,
306
        mutable: bool,
307
    },
308
    /// Eg. `[i32; 32]`.
309
    Array(ArrayType),
310
    /// Eg. `?T`.
311
    Optional(*Type),
312
    /// Eg. `fn id(i32) -> i32`.
313
    Fn(*FnType),
314
    /// Named, ie. user-defined types, includes union variants.
315
    Nominal(*NominalType),
316
    /// Owning trait object. An erased type with v-table.
317
    TraitObject {
318
        /// Ownership and safety class.
319
        class: types::PointerClass,
320
        /// Trait definition.
321
        traitInfo: *TraitType,
322
        /// Whether the pointer is mutable.
323
        mutable: bool,
324
    },
325
}
326
327
/// Structured diagnostic payload for type mismatches.
328
export record TypeMismatch {
329
    expected: Type,
330
    actual: Type,
331
}
332
333
/// Structured diagnostic payload for invalid `as` casts.
334
export record InvalidAsCast {
335
    from: Type,
336
    to: Type,
337
}
338
339
/// Diagnostic payload for argument count mismatches.
340
export record CountMismatch {
341
    expected: u32,
342
    actual: u32,
343
}
344
345
/// Detailed payload attached to a symbol, specialized per symbol kind.
346
export union SymbolData {
347
    /// Payload describing mutable bindings like variables or functions.
348
    Value {
349
        /// Whether the binding permits mutation.
350
        mutable: bool,
351
        /// Custom alignment requirement, or 0 for default.
352
        alignment: u32,
353
        /// Resolved type associated with the value.
354
        type: Type,
355
        /// Whether the variable's address is taken anywhere (via `&` or `&mut`).
356
        /// Used by the lowerer to allocate a stack slot eagerly.
357
        addressTaken: bool,
358
    },
359
    /// Payload describing constants.
360
    Constant {
361
        /// Resolved type associated with the value.
362
        type: Type,
363
        /// Constant value, if any.
364
        value: ?ConstValue,
365
    },
366
    /// Payload describing union variants and the union type they instantiate.
367
    Variant {
368
        /// Variant payload type.
369
        type: Type,
370
        /// Union declaration.
371
        decl: *ast::Node,
372
        /// Variant ordinal in declaration order.
373
        ordinal: u32,
374
        /// Variant index within the union.
375
        index: u32,
376
    },
377
    /// Module reference.
378
    Module {
379
        /// Module entry in the graph.
380
        entry: *module::ModuleEntry,
381
        /// Module scope.
382
        scope: *mut Scope,
383
    },
384
    /// Payload describing type symbols with their resolved type.
385
    Type(*mut NominalType),
386
    /// Trait symbol.
387
    Trait(*mut TraitType),
388
}
389
390
/// Resolved symbol allocated during semantic analysis.
391
export record Symbol {
392
    /// Symbol name in source code.
393
    name: *[u8],
394
    /// Data associated with the symbol.
395
    data: SymbolData,
396
    /// Bitset of attributes applied to the declaration.
397
    attrs: u32,
398
    /// AST node that introduced the symbol.
399
    node: *ast::Node,
400
    /// Module ID this symbol belongs to. Only for module-level symbols.
401
    moduleId: ?u16,
402
}
403
404
/// Integer constant payload.
405
export record ConstInt {
406
    /// Absolute magnitude of the value.
407
    magnitude: u64,
408
    /// Bit width of the integer.
409
    bits: u8,
410
    /// Whether the integer is signed.
411
    signed: bool,
412
    /// Whether the value is negative (only valid when `signed` is true).
413
    negative: bool,
414
}
415
416
/// Constant value recorded for literal nodes.
417
export union ConstValue {
418
    Bool(bool),
419
    Char(u8),
420
    String(*[u8]),
421
    Int(ConstInt),
422
}
423
424
/// Integer range metadata for primitive integer types.
425
union IntegerRange {
426
    Signed {
427
        bits: u8,
428
        min: i64,
429
        max: i64,
430
        lim: u64,
431
    },
432
    Unsigned {
433
        bits: u8,
434
        max: u64,
435
    },
436
}
437
438
/// Diagnostic emitted by the analyzer.
439
export record Error {
440
    /// Error category.
441
    kind: ErrorKind,
442
    /// Node associated with the error, if known.
443
    node: ?*ast::Node,
444
    /// Module ID where this error occurred.
445
    moduleId: u16,
446
}
447
448
/// High-level classification for semantic diagnostics.
449
export union ErrorKind {
450
    /// Identifier declared more than once in the same scope.
451
    DuplicateBinding(*[u8]),
452
    /// Identifier referenced before it was declared.
453
    UnresolvedSymbol(*[u8]),
454
    /// Attempted to assign to an immutable binding.
455
    ImmutableBinding,
456
    /// Expected a compile-time constant expression.
457
    ConstExprRequired,
458
    /// Symbol arena exhausted while binding identifiers.
459
    SymbolOverflow,
460
    /// Expression has the wrong type.
461
    TypeMismatch(TypeMismatch),
462
    /// Numeric literal does not fit within the required range.
463
    NumericLiteralOverflow,
464
    /// Record literal omitted a required field.
465
    RecordFieldMissing(*[u8]),
466
    /// Record literal referenced a field that does not exist.
467
    RecordFieldUnknown(*[u8]),
468
    /// Brace syntax used on unlabeled record.
469
    RecordFieldStyleMismatch,
470
    /// Record literal supplied the wrong number of fields.
471
    RecordFieldCountMismatch(CountMismatch),
472
    /// Record literal fields not in declaration order.
473
    RecordFieldOutOfOrder { field: *[u8], prev: *[u8] },
474
    /// Function call supplied the wrong number of arguments.
475
    FnArgCountMismatch(CountMismatch),
476
    /// Function throws list has the wrong number of types.
477
    FnThrowCountMismatch(CountMismatch),
478
    /// Expected an identifier node.
479
    ExpectedIdentifier,
480
    /// Expected any optional type.
481
    ExpectedOptional,
482
    /// Expected a numeric type.
483
    ExpectedNumeric,
484
    /// Expected a pointer type.
485
    ExpectedPointer,
486
    /// Expected a record type.
487
    ExpectedRecord,
488
    /// Expected an array or slice value.
489
    ExpectedIndexable,
490
    /// Expected an iterable (array, slice, or range) for a `for` loop.
491
    ExpectedIterable,
492
    /// Invalid `as` cast between the provided types.
493
    InvalidAsCast(InvalidAsCast),
494
    /// Invalid alignment value specified.
495
    InvalidAlignmentValue(u32),
496
    /// Invalid module path.
497
    InvalidModulePath,
498
    /// Invalid identifier.
499
    InvalidIdentifier(*ast::Node),
500
    /// Invalid scope access.
501
    InvalidScopeAccess,
502
    /// Referenced an unknown array field.
503
    ArrayFieldUnknown(*[u8]),
504
    /// Referenced an unknown slice field.
505
    SliceFieldUnknown(*[u8]),
506
    /// Array slicing without taking an address.
507
    SliceRequiresAddress,
508
    /// Slice bounds exceed array length.
509
    SliceRangeOutOfBounds,
510
    /// Unexpected `return` statement.
511
    UnexpectedReturn,
512
    /// Unexpected module name.
513
    UnexpectedModuleName,
514
    /// Unexpected node.
515
    UnexpectedNode(*ast::Node),
516
    /// Function with non-void return type falls through without returning.
517
    FnMissingReturn,
518
    /// Function is missing a body.
519
    FnMissingBody,
520
    /// Function body is not expected.
521
    FnUnexpectedBody,
522
    /// Intrinsic function must not have a body.
523
    IntrinsicUnexpectedBody,
524
    /// Ecall intrinsic declaration does not match the canonical unsafe ABI.
525
    InvalidEcallIntrinsicSignature,
526
    /// Encountered loop control outside of a loop construct.
527
    InvalidLoopControl,
528
    /// `try` used when the enclosing function does not declare throws.
529
    TryRequiresThrows,
530
    /// `try` used to propagate an error not declared by the enclosing function.
531
    TryIncompatibleError,
532
    /// `throw` used when the enclosing function does not declare throws.
533
    ThrowRequiresThrows,
534
    /// `throw` used with an error type not declared by the enclosing function.
535
    ThrowIncompatibleError,
536
    /// `try` applied to an expression that cannot throw.
537
    TryNonThrowing,
538
    /// Inferred catch binding used with multi-error callee.
539
    TryCatchMultiError,
540
    /// Duplicate error type in typed catch clauses.
541
    TryCatchDuplicateType,
542
    /// Typed catch clauses do not cover all error types.
543
    TryCatchNonExhaustive,
544
    /// Called a fallible function without using `try`.
545
    MissingTry,
546
    /// Cannot use opaque type in this context.
547
    OpaqueTypeNotAllowed,
548
    /// Cannot dereference pointer to opaque type.
549
    OpaqueTypeDeref,
550
    /// Cannot perform pointer arithmetic on opaque pointer.
551
    OpaquePointerArithmetic,
552
    /// Cannot infer type from context.
553
    CannotInferType,
554
    /// Cannot assign a void value to a variable.
555
    CannotAssignVoid,
556
    /// `default` attribute used on a non-function declaration.
557
    DefaultAttrOnlyOnFn,
558
    /// Union variant requires a payload but none was provided.
559
    UnionVariantPayloadMissing(*[u8]),
560
    /// Union variant does not expect a payload but one was provided.
561
    UnionVariantPayloadUnexpected(*[u8]),
562
    /// `match` on a union omits a variant without a `default` case.
563
    UnionMatchNonExhaustive(*[u8]),
564
    /// `match` on an optional is missing a value case.
565
    OptionalMatchMissingValue,
566
    /// `match` on an optional is missing a nil case.
567
    OptionalMatchMissingNil,
568
    /// `match` on a bool is missing a case (true or false).
569
    BoolMatchMissing(bool),
570
    /// `match` on a non-union type is missing a catch-all.
571
    MatchNonExhaustive,
572
    /// `match` has more than one catch-all prongs.
573
    DuplicateCatchAll,
574
    /// `match` has a duplicate case pattern.
575
    DuplicateMatchPattern,
576
    /// `match` has an unreachable `else`: all cases are already handled.
577
    UnreachableElse,
578
    /// Builtin called with wrong number of arguments.
579
    BuiltinArgCountMismatch(CountMismatch),
580
    /// Instance method receiver mutability does not match the trait declaration.
581
    ReceiverMutabilityMismatch,
582
    /// Duplicate instance declaration for the same (trait, type) pair.
583
    DuplicateInstance,
584
    /// Instance declaration is missing a required trait method.
585
    MissingTraitMethod(*[u8]),
586
    /// Trait name used as a value expression.
587
    UnexpectedTraitName,
588
    /// Trait method receiver does not point to the declaring trait.
589
    TraitReceiverMismatch,
590
    /// Trait declaration and instance disagree about unsafe call requirements.
591
    TraitMethodSafetyMismatch,
592
    /// Function declaration has too many parameters.
593
    FnParamOverflow(CountMismatch),
594
    /// Function declaration has too many throws.
595
    FnThrowOverflow(CountMismatch),
596
    /// Trait declaration has too many methods.
597
    TraitMethodOverflow(CountMismatch),
598
    /// Instance declaration is missing a required supertrait instance.
599
    MissingSupertraitInstance(*[u8]),
600
    /// Linear binding was consumed more than once.
601
    LinearUseAfterConsume(*[u8]),
602
    /// Linear binding remains available at an exit.
603
    LinearNotConsumed(*[u8]),
604
    /// A case-pattern `let-else` fallback must terminate control flow.
605
    LinearLetElseMustTerminate,
606
    /// Branches disagree about a linear binding's state.
607
    LinearBranchMismatch(*[u8]),
608
    /// A linear field cannot be moved independently.
609
    LinearPartialMove,
610
    /// A linear value cannot be discarded.
611
    LinearDiscard,
612
    /// Assignment would overwrite a live linear value.
613
    LinearOverwrite,
614
    /// `undefined` cannot initialize a linear type.
615
    LinearUndefined,
616
    /// A reference appears in a storable or escaping position.
617
    InvalidRefPosition,
618
    /// A reference cannot be bound to a local.
619
    RefBinding,
620
    /// Call arguments contain overlapping incompatible loans.
621
    BorrowConflict(*[u8]),
622
    /// Unsafe pointer operation outside an `unsafe` declaration.
623
    UnsafeOperation,
624
    /// Safe code cannot call an `unsafe` function.
625
    UnsafeCall,
626
    /// Internal error.
627
    Internal,
628
}
629
630
/// Diagnostics returned by the analyzer.
631
export record Diagnostics {
632
    errors: *mut [Error],
633
}
634
635
/// Call context.
636
union CallCtx {
637
    /// Normal function call.
638
    Normal,
639
    /// Fallible function call, ie. `try f()`.
640
    Try,
641
}
642
643
/// Result of resolving a record literal's type name.
644
record ResolvedRecordLitType {
645
    /// The record nominal type to use for field checking.
646
    recordType: *NominalType,
647
    /// The result type of the literal (record type or union type for variants).
648
    resultType: Type,
649
}
650
651
/// Result of checking for a `super` path prefix.
652
record SuperAccessResult {
653
    scope: *mut Scope,
654
    child: *ast::Node,
655
}
656
657
/// Node-specific resolver metadata.
658
export union NodeExtra {
659
    /// No extra data for this node.
660
    None,
661
    /// Resolved field index for record literal fields.
662
    RecordField { index: u32 },
663
    /// Slice range metadata for subscript expressions with ranges.
664
    SliceRange(SliceRangeInfo),
665
    /// Cached union variant metadata for patterns/constructors.
666
    UnionVariant { ordinal: u32, tag: u32 },
667
    /// Match prong metadata.
668
    MatchProng { catchAll: bool },
669
    /// Match expression metadata.
670
    Match { isConst: bool },
671
    /// For-loop iteration metadata.
672
    ForLoop(ForLoopInfo),
673
    /// Trait method call metadata.
674
    TraitMethodCall {
675
        /// Trait definition.
676
        traitInfo: *TraitType,
677
        /// Method index in the v-table.
678
        methodIndex: u32,
679
    },
680
    /// Standalone method call metadata.
681
    MethodCall { method: *MethodEntry },
682
    /// Slice `.append(val, allocator)` method call.
683
    SliceAppend { elemType: *Type },
684
    /// Slice `.delete(index)` method call.
685
    SliceDelete { elemType: *Type },
686
}
687
688
/// Combined resolver metadata for a single AST node.
689
export record NodeData {
690
    /// Resolved type for this node.
691
    ty: Type,
692
    /// Coercion plan applied to this node.
693
    coercion: Coercion,
694
    /// Symbol associated with this node.
695
    sym: ?*mut Symbol,
696
    /// Constant value for literal nodes.
697
    constValue: ?ConstValue,
698
    /// Lexical scope owned by this node.
699
    scope: ?*mut Scope,
700
    /// Node-specific extra data.
701
    extra: NodeExtra,
702
    /// Whether the declaration body belongs to a trusted unsafe module.
703
    trustedBody: bool,
704
}
705
706
/// Table storing all resolver metadata indexed by node ID.
707
record NodeDataTable {
708
    entries: *mut [NodeData],
709
}
710
711
/// Lexical scope.
712
export record Scope {
713
    /// Owning AST node, or `nil` for the root scope.
714
    owner: ?*ast::Node,
715
    /// Parent/enclosing scope.
716
    parent: ?*mut Scope,
717
    /// Module ID if this is a module scope.
718
    moduleId: ?u16,
719
    /// Symbols introduced inside the scope, allocated from the arena.
720
    symbols: *mut [*mut Symbol],
721
    /// Number of live symbols.
722
    symbolsLen: u32,
723
}
724
725
/// An object used by the enter and exit functions for module scopes.
726
record ModuleScope {
727
    /// Module root node.
728
    root: *ast::Node,
729
    /// Module entry in graph.
730
    entry: *module::ModuleEntry,
731
    /// The newly entered scope.
732
    newScope: *mut Scope,
733
    /// The previous scope.
734
    prevScope: *mut Scope,
735
    /// The previous module.
736
    prevMod: u16,
737
}
738
739
/// Loop context for tracking control flow within loops.
740
record LoopCtx {
741
    /// Whether a reachable break was encountered in this loop.
742
    /// This is used to determine whether a loop diverges.
743
    hasBreak: bool,
744
}
745
746
/// Configuration for semantic analysis.
747
export record Config {
748
    /// Whether we're building in test mode.
749
    buildTest: bool,
750
}
751
752
/// How pattern bindings are created during match.
753
export union MatchBy {
754
    /// Match by value.
755
    Value,
756
    /// Match by immutable reference.
757
    Ref,
758
    /// Match by mutable reference.
759
    MutRef,
760
}
761
762
/// State of a match statement being resolved.
763
// TODO: This is only used because of the maximum function param limitation.
764
record MatchState {
765
    /// Is the match catch-all?
766
    catchAll: bool,
767
    /// Is the match constant?
768
    isConst: bool
769
}
770
771
/// Result of unwrapping a type for pattern matching.
772
export record MatchSubject {
773
    /// The effective type to match against.
774
    effectiveTy: Type,
775
    /// How bindings should be created.
776
    by: MatchBy,
777
}
778
779
/// How an expression uses a linear result.
780
union LinearUse {
781
    /// Consume the value and end its availability.
782
    Consume,
783
    /// Read the value without consuming it.
784
    Observe,
785
    /// Borrow the value through a reference.
786
    Borrow,
787
    /// Discard an unused expression result.
788
    Discard,
789
    /// Use the value as an assignment target.
790
    Place,
791
}
792
793
/// Per-control-flow-path ownership state.
794
record LinearEnv {
795
    /// Symbols tracked on this control-flow path.
796
    symbols: [?*mut Symbol; MAX_LINEAR_BINDINGS],
797
    /// Bit set for each binding that remains available.
798
    available: u64,
799
    /// Number of entries in `symbols`.
800
    len: u32,
801
    /// Whether this control-flow path has terminated.
802
    terminated: bool,
803
}
804
805
/// Loans kept alive while later call arguments are evaluated.
806
record LinearLoans {
807
    /// Enclosing call's active loans.
808
    parent: ?*LinearLoans,
809
    /// Root symbol for each active loan.
810
    roots: [?*mut Symbol; MAX_FN_PARAMS + 1],
811
    /// Whether each loan excludes every other access.
812
    exclusive: [bool; MAX_FN_PARAMS + 1],
813
    /// Number of active entries.
814
    len: u32,
815
}
816
817
/// Function-local exact-use checker state.
818
record LinearChecker {
819
    /// Resolver that owns the symbols and diagnostics.
820
    resolver: *mut Resolver,
821
    /// Active loans from enclosing and earlier call arguments.
822
    loans: ?*LinearLoans,
823
    /// Binding count at entry to each active loop.
824
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
825
    /// Available bindings at entry to each active loop.
826
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
827
    /// Available bindings shared by the exits from each active loop.
828
    loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
829
    /// Whether each active loop can exit without `break`.
830
    loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH],
831
    /// Whether each active loop contains a reachable `break`.
832
    loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH],
833
    /// Number of active loops.
834
    loopDepth: u32,
835
}
836
837
/// Unwrap a pointer type for pattern matching.
838
export fn unwrapMatchSubject(ty: Type) -> MatchSubject {
839
    if let case Type::Pointer { target, mutable, .. } = ty {
840
        let by = MatchBy::MutRef if mutable else MatchBy::Ref;
841
        return MatchSubject { effectiveTy: *target, by };
842
    }
843
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
844
}
845
846
/// Global resolver state.
847
export record Resolver {
848
    /// Current scope.
849
    scope: *mut Scope,
850
    /// Package scope containing package roots and top-level symbols.
851
    pkgScope: *mut Scope,
852
    /// Stack of loop contexts for nested loops.
853
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
854
    /// Current loop depth, indexes into loop stack.
855
    loopDepth: u32,
856
    /// Signature of the function currently being analyzed.
857
    currentFn: ?*FnType,
858
    /// Current module being analyzed.
859
    currentMod: u16,
860
    /// Nesting depth of unsafe modules and function bodies.
861
    unsafeDepth: u32,
862
    /// Configuration for semantic analysis.
863
    config: Config,
864
    /// Unified arena for symbols, scopes, and nominal type.
865
    arena: alloc::Arena,
866
    /// Combined semantic metadata table indexed by node ID.
867
    nodeData: NodeDataTable,
868
    /// Linked list of interned types.
869
    types: ?*TypeNode,
870
    /// Diagnostics recorded so far.
871
    errors: *mut [Error],
872
    /// Module graph for the current package.
873
    moduleGraph: *module::ModuleGraph,
874
    /// Cache of module scopes indexed by module ID.
875
    moduleScopes: [?*mut Scope; module::MAX_MODULES],
876
    /// Trait instance registry.
877
    instances: [InstanceEntry; MAX_INSTANCES],
878
    /// Number of registered instances.
879
    instancesLen: u32,
880
    /// Standalone method registry.
881
    methods: [MethodEntry; MAX_METHODS],
882
    /// Number of registered standalone methods.
883
    methodsLen: u32,
884
}
885
886
/// Internal error sentinel thrown when analysis cannot proceed.
887
export union ResolveError {
888
    Failure,
889
}
890
891
/// Node in the type interning linked list.
892
record TypeNode {
893
    ty: Type,
894
    next: ?*TypeNode,
895
}
896
897
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
898
export fn allocType(self: *mut Resolver, ty: Type) -> *Type {
899
    // Search existing types for a match.
900
    let mut cursor = self.types;
901
    while let node = cursor {
902
        if node.ty == ty {
903
            return &node.ty;
904
        }
905
        set cursor = node.next;
906
    }
907
    // Allocate a new type node from the arena.
908
    let node = try! alloc::alloc(
909
        &mut self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
910
    ) as *mut TypeNode;
911
912
    set *node = TypeNode { ty, next: self.types };
913
    set self.types = node;
914
915
    return &node.ty;
916
}
917
918
/// Allocate a nominal type descriptor and return a pointer to it.
919
fn allocNominalType(self: *mut Resolver, info: NominalType) -> *mut NominalType {
920
    // Nb. We don't attempt to de-duplicate nominal type entries,
921
    // since they don't carry node information and we create
922
    // placeholder entries when binding symbols.
923
    let entry = try! alloc::alloc(
924
        &mut self.arena, @sizeOf(NominalType), @alignOf(NominalType)
925
    ) as *mut NominalType;
926
927
    set *entry = info;
928
929
    return entry;
930
}
931
932
/// Allocate a function type descriptor and return a pointer to it.
933
fn allocFnType(self: *mut Resolver, info: FnType) -> *FnType {
934
    let entry = try! alloc::alloc(
935
        &mut self.arena, @sizeOf(FnType), @alignOf(FnType)
936
    ) as *mut FnType;
937
938
    set *entry = info;
939
940
    return entry;
941
}
942
943
/// Returns an error, if any, associated with the given node.
944
fn errorForNode(self: *Resolver, node: *ast::Node) -> ?*Error {
945
    for i in 0..self.errors.len {
946
        let err = &self.errors[i];
947
        if err.node == node {
948
            return err;
949
        }
950
    }
951
    return nil;
952
}
953
954
/// Storage buffers used by the analyzer.
955
export record ResolverStorage {
956
    /// Unified arena for symbols, scopes, and nominal type.
957
    arena: alloc::Arena,
958
    /// Node semantic metadata indexed by node ID.
959
    nodeData: *mut [NodeData],
960
    /// Package scope.
961
    pkgScope: *mut Scope,
962
    /// Error storage.
963
    errors: *mut [Error],
964
}
965
966
/// Input for resolving a single package.
967
export record Pkg {
968
    /// Root module entry.
969
    rootEntry: *module::ModuleEntry,
970
    /// Root AST node.
971
    rootAst: *ast::Node,
972
}
973
974
/// Construct a resolver with module context and backing storage.
975
export fn resolver(
976
    storage: ResolverStorage,
977
    config: Config
978
) -> Resolver {
979
    let mut arena = storage.arena;
980
    let symbols = try! alloc::allocSlice(
981
        &mut arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), MAX_MODULE_SYMBOLS
982
    ) as *mut [*mut Symbol];
983
984
    // Initialize the root scope.
985
    // TODO: Set this up when declaring `PKG_SCOPE`, not here.
986
    set *storage.pkgScope = Scope {
987
        owner: nil,
988
        parent: nil,
989
        moduleId: nil,
990
        symbols,
991
        symbolsLen: 0,
992
    };
993
994
    // Clear all node semantic metadata to sentinel values.
995
    // TODO: Use array repeat literal?
996
    for i in 0..storage.nodeData.len {
997
        set storage.nodeData[i] = NodeData {
998
            ty: Type::Unknown,
999
            coercion: Coercion::Identity,
1000
            sym: nil,
1001
            constValue: nil,
1002
            scope: nil,
1003
            extra: NodeExtra::None,
1004
            trustedBody: false,
1005
        };
1006
    }
1007
1008
    let mut moduleScopes: [?*mut Scope; module::MAX_MODULES] = undefined;
1009
    // TODO: Simplify.
1010
    for i in 0..moduleScopes.len {
1011
        set moduleScopes[i] = nil;
1012
    }
1013
    return Resolver {
1014
        scope: storage.pkgScope,
1015
        pkgScope: storage.pkgScope,
1016
        loopStack: undefined,
1017
        loopDepth: 0,
1018
        currentFn: nil,
1019
        currentMod: 0,
1020
        unsafeDepth: 0,
1021
        config,
1022
        arena,
1023
        nodeData: NodeDataTable { entries: storage.nodeData },
1024
        types: nil,
1025
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1026
        // TODO: Shouldn't be undefined.
1027
        moduleGraph: undefined,
1028
        moduleScopes,
1029
        instances: undefined,
1030
        instancesLen: 0,
1031
        methods: undefined,
1032
        methodsLen: 0,
1033
    };
1034
}
1035
1036
/// Return `true` if there are no errors in the diagnostics.
1037
export fn success(diag: *Diagnostics) -> bool {
1038
    return diag.errors.len == 0;
1039
}
1040
1041
/// Retrieve an error diagnostic by index, if present.
1042
export fn errorAt(errs: *[Error], index: u32) -> ?*Error {
1043
    if index >= errs.len {
1044
        return nil;
1045
    }
1046
    return &errs[index];
1047
}
1048
1049
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1050
fn emitError(self: *mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1051
    // If our error list is full, just return an error without recording it.
1052
    if self.errors.len >= self.errors.cap {
1053
        return ResolveError::Failure;
1054
    }
1055
    // Don't record more than one error per node.
1056
    if let n = node; errorForNode(self, n) <> nil {
1057
        return ResolveError::Failure;
1058
    }
1059
    let idx = self.errors.len;
1060
    set self.errors = @sliceOf(self.errors.ptr, idx + 1, self.errors.cap);
1061
    set self.errors[idx] = Error { kind, node, moduleId: self.currentMod };
1062
1063
    return ResolveError::Failure;
1064
}
1065
1066
/// Like [`emitError`], but for type mismatches specifically.
1067
fn emitTypeMismatch(self: *mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1068
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1069
}
1070
1071
/// Allocate a scope object with the given symbol capacity.
1072
fn allocScope(self: *mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope {
1073
    // Check for an existing scope for this node, and don't allocate a new
1074
    // one in that case.
1075
    if let scope = scopeFor(self, owner) {
1076
        return scope;
1077
    }
1078
    assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds";
1079
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(Scope), @alignOf(Scope));
1080
    let entry = p as *mut Scope;
1081
1082
    // Allocate symbols from the arena.
1083
    let symbols = try! alloc::allocSlice(
1084
        &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), capacity
1085
    ) as *mut [*mut Symbol];
1086
1087
    set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 };
1088
    set self.nodeData.entries[owner.id].scope = entry;
1089
1090
    return entry;
1091
}
1092
1093
/// Enter a new local scope that is the child of the current scope.
1094
/// This creates a parent/child relationship that means that lookups in the
1095
/// child scope can recurse upwards.
1096
export fn enterScope(self: *mut Resolver, owner: *ast::Node) -> *Scope {
1097
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1098
    set scope.parent = self.scope;
1099
    set self.scope = scope;
1100
    return scope;
1101
}
1102
1103
/// Enter a module scope. Returns an object that can be used to exit the scope.
1104
export fn enterModuleScope(self: *mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1105
    let prevScope = self.scope;
1106
    let prevMod = self.currentMod;
1107
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1108
1109
    set self.scope = scope;
1110
    set self.scope.moduleId = module.id;
1111
    set self.currentMod = module.id;
1112
    // TODO: Allow any unsigned integer to index an array.
1113
    set self.moduleScopes[module.id as u32] = scope;
1114
1115
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1116
}
1117
1118
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1119
fn enterSubModule(self: *mut Resolver, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1120
    let modEntry = module::findChild(self.moduleGraph, name, self.currentMod)
1121
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1122
    let modRoot = modEntry.ast
1123
        else panic "enterSubModule: analyzing module that wasn't parsed";
1124
1125
    return enterModuleScope(self, modRoot, modEntry);
1126
}
1127
1128
/// Exit a module scope, given the object returned by `enterModuleScope`.
1129
export fn exitModuleScope(self: *mut Resolver, entry: ModuleScope) {
1130
    set self.scope = entry.prevScope;
1131
    set self.currentMod = entry.prevMod;
1132
}
1133
1134
/// Exit the most recent scope.
1135
export fn exitScope(self: *mut Resolver) {
1136
    let parent = self.scope.parent else {
1137
        // TODO: This should be a panic, but one of the tests hits this
1138
        // clause, which might be a bug in the generator.
1139
        return;
1140
    };
1141
    set self.scope = parent;
1142
}
1143
1144
/// Visit the body of a loop while tracking nesting depth.
1145
fn visitLoop(self: *mut Resolver, body: *ast::Node) -> Type
1146
    throws (ResolveError)
1147
{
1148
    assert self.loopDepth < MAX_LOOP_DEPTH, "visitLoop: loop nesting depth exceeded";
1149
    set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false };
1150
    set self.loopDepth += 1;
1151
1152
    let ty = try infer(self, body) catch {
1153
        assert self.loopDepth <> 0, "visitLoop: loop depth underflow";
1154
        set self.loopDepth -= 1;
1155
        throw ResolveError::Failure;
1156
    };
1157
    // Pop and check if break was encountered.
1158
    set self.loopDepth -= 1;
1159
1160
    if self.loopStack[self.loopDepth].hasBreak {
1161
        return Type::Void;
1162
    }
1163
    return Type::Never;
1164
}
1165
1166
/// Require that loop control statements appear inside a loop.
1167
fn ensureInsideLoop(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
1168
    if self.loopDepth == 0 {
1169
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1170
    }
1171
}
1172
1173
/// Bind a loop pattern to the provided type.
1174
fn bindForLoopPattern(self: *mut Resolver, pattern: *ast::Node, ty: Type, mutable: bool)
1175
    throws (ResolveError)
1176
{
1177
    match pattern.value {
1178
        case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => {
1179
            let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0);
1180
        }
1181
        else => {
1182
            let actualTy = try checkAssignable(self, pattern, ty);
1183
            setNodeType(self, pattern, actualTy);
1184
        }
1185
    }
1186
}
1187
1188
/// Set the expected return type for a new function body.
1189
fn enterFn(self: *mut Resolver, node: *ast::Node, ty: *FnType) {
1190
    assert self.currentFn == nil, "enterFn: already in a function";
1191
    set self.currentFn = ty;
1192
    enterScope(self, node);
1193
}
1194
1195
/// Clear the expected return type when leaving a function body.
1196
fn exitFn(self: *mut Resolver) {
1197
    if self.currentFn == nil {
1198
        // TODO: This should be a panic, but one of the tests hits this
1199
        // clause, which might be a bug in the generator.
1200
        return;
1201
    }
1202
    set self.currentFn = nil;
1203
    exitScope(self);
1204
}
1205
1206
/// Extract the identifier text from a node.
1207
fn nodeName(self: *mut Resolver, node: *ast::Node) -> *[u8]
1208
    throws (ResolveError)
1209
{
1210
    let case ast::NodeValue::Ident(name) = node.value
1211
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1212
    return name;
1213
}
1214
1215
/// Associate a resolved symbol with an AST node.
1216
fn setNodeSymbol(self: *mut Resolver, node: *ast::Node, symbol: *mut Symbol) {
1217
    if let existingSym = self.nodeData.entries[node.id].sym {
1218
        panic "setNodeSymbol: a symbol is already associated with this node";
1219
    }
1220
    set self.nodeData.entries[node.id].sym = symbol;
1221
}
1222
1223
/// Associate a resolved type with an AST node and return it.
1224
fn setNodeType(self: *mut Resolver, node: *ast::Node, ty: Type) -> Type {
1225
    if ty == Type::Unknown {
1226
        // In this case, we simply don't associate a type.
1227
        return ty;
1228
    }
1229
    set self.nodeData.entries[node.id].ty = ty;
1230
1231
    return ty;
1232
}
1233
1234
/// Unify the types of two branches for control flow. Returns `never` only if
1235
/// both branches diverge, otherwise returns `void`. If the else branch is
1236
/// absent, we assume it doesn't diverge.
1237
fn unifyBranches(left: Type, right: ?Type) -> Type {
1238
    if left == Type::Never {
1239
        if let ty = right; ty == Type::Never {
1240
            return Type::Never;
1241
        }
1242
    }
1243
    return Type::Void;
1244
}
1245
1246
/// Associate a coercion plan with an AST node.
1247
fn setNodeCoercion(self: *mut Resolver, node: *ast::Node, coercion: Coercion) -> Coercion {
1248
    if coercion == Coercion::Identity {
1249
        return coercion;
1250
    }
1251
    set self.nodeData.entries[node.id].coercion = coercion;
1252
1253
    return coercion;
1254
}
1255
1256
/// Associate a constant value with an AST node.
1257
fn setNodeConstValue(self: *mut Resolver, node: *ast::Node, value: ConstValue) {
1258
    set self.nodeData.entries[node.id].constValue = value;
1259
}
1260
1261
/// Associate a record field index with a record literal field node.
1262
fn setRecordFieldIndex(self: *mut Resolver, node: *ast::Node, index: u32) {
1263
    set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index };
1264
}
1265
1266
/// Associate slice range metadata with a subscript expression.
1267
fn setSliceRangeInfo(self: *mut Resolver, node: *ast::Node, info: SliceRangeInfo) {
1268
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
1269
}
1270
1271
/// Associate union variant metadata with a pattern or constructor node.
1272
fn setVariantInfo(self: *mut Resolver, node: *ast::Node, ordinal: u32, tag: u32) {
1273
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
1274
}
1275
1276
/// Associate trait method call metadata with a call node.
1277
fn setTraitMethodCall(self: *mut Resolver, node: *ast::Node, traitInfo: *TraitType, methodIndex: u32) {
1278
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
1279
}
1280
1281
/// Associate for-loop metadata with a for-loop node.
1282
fn setForLoopInfo(self: *mut Resolver, node: *ast::Node, info: ForLoopInfo) {
1283
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
1284
}
1285
1286
/// Retrieve the constant value associated with a node, if any.
1287
export fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue {
1288
    return self.nodeData.entries[node.id].constValue;
1289
}
1290
1291
/// Get the resolved record field index for a record literal field node.
1292
export fn recordFieldIndexFor(self: *Resolver, node: *ast::Node) -> ?u32 {
1293
    if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra {
1294
        return index;
1295
    }
1296
    return nil;
1297
}
1298
1299
/// Get the slice range metadata for a subscript expression with a range index.
1300
export fn sliceRangeInfoFor(self: *Resolver, node: *ast::Node) -> ?SliceRangeInfo {
1301
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
1302
        return info;
1303
    }
1304
    return nil;
1305
}
1306
1307
/// Get the for-loop metadata for a for-loop node.
1308
export fn forLoopInfoFor(self: *Resolver, node: *ast::Node) -> ?ForLoopInfo {
1309
    if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra {
1310
        return info;
1311
    }
1312
    return nil;
1313
}
1314
1315
/// Associate match prong metadata with a match prong node.
1316
fn setProngCatchAll(self: *mut Resolver, node: *ast::Node, catchAll: bool) {
1317
    set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll };
1318
}
1319
1320
/// Check if a prong is catch-all.
1321
export fn isProngCatchAll(self: *Resolver, node: *ast::Node) -> bool {
1322
    if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra {
1323
        return catchAll;
1324
    }
1325
    return false;
1326
}
1327
1328
/// Set match metadata.
1329
fn setMatchConst(self: *mut Resolver, node: *ast::Node, isConst: bool) {
1330
    set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst };
1331
}
1332
1333
/// Check if a match has all constant patterns.
1334
export fn isMatchConst(self: *Resolver, node: *ast::Node) -> bool {
1335
    if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra {
1336
        return isConst;
1337
    }
1338
    return false;
1339
}
1340
1341
/// Get the resolver metadata for a node.
1342
export fn nodeData(self: *Resolver, node: *ast::Node) -> *NodeData {
1343
    return &self.nodeData.entries[node.id];
1344
}
1345
1346
/// Get the type for a node, or `nil` if unknown.
1347
export fn typeFor(self: *Resolver, node: *ast::Node) -> ?Type {
1348
    let ty = self.nodeData.entries[node.id].ty;
1349
    if ty == Type::Unknown {
1350
        return nil;
1351
    }
1352
    return ty;
1353
}
1354
1355
/// Get the scope associated with a node.
1356
export fn scopeFor(self: *Resolver, node: *ast::Node) -> ?*mut Scope {
1357
    return self.nodeData.entries[node.id].scope;
1358
}
1359
1360
/// Get the symbol bound to a node.
1361
export fn symbolFor(self: *Resolver, node: *ast::Node) -> ?*mut Symbol {
1362
    return self.nodeData.entries[node.id].sym;
1363
}
1364
1365
/// Get the coercion plan associated with a node, if any.
1366
export fn coercionFor(self: *Resolver, node: *ast::Node) -> ?Coercion {
1367
    let c = self.nodeData.entries[node.id].coercion;
1368
    if c == Coercion::Identity {
1369
        return nil;
1370
    }
1371
    return c;
1372
}
1373
1374
/// Get the module ID for a symbol by walking up its scope chain.
1375
export fn moduleIdForSymbol(self: *Resolver, sym: *Symbol) -> ?u16 {
1376
    // For module-level symbols, return the cached module ID.
1377
    if let id = sym.moduleId {
1378
        return id;
1379
    }
1380
    // For module symbols, return the module ID directly.
1381
    if let case SymbolData::Module { entry, .. } = sym.data {
1382
        return entry.id;
1383
    }
1384
    // If this node has its own scope (functions, types, etc.), walk up from there.
1385
    if let scope = self.nodeData.entries[sym.node.id].scope {
1386
        return findModuleForScope(scope);
1387
    }
1388
    return nil;
1389
}
1390
1391
/// Get the binding node for a variant pattern.
1392
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
1393
export fn variantPatternBinding(self: *Resolver, pattern: *ast::Node) -> ?*ast::Node {
1394
    let case ast::NodeValue::Call(call) = pattern.value
1395
        else return nil;
1396
    let sym = symbolFor(self, call.callee)
1397
        else return nil;
1398
    let case SymbolData::Variant { .. } = sym.data
1399
        else return nil;
1400
1401
    if call.args.len == 0 {
1402
        return nil;
1403
    }
1404
    let arg = call.args[0];
1405
1406
    if let case ast::NodeValue::Placeholder = arg.value {
1407
        return nil;
1408
    }
1409
    return arg;
1410
}
1411
1412
/// Allocate a new symbol, and return a reference to it.
1413
fn allocSymbol(self: *mut Resolver, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *mut Symbol {
1414
    let sym = try! alloc::alloc(&mut self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *mut Symbol;
1415
    set *sym = Symbol { name, data, attrs, node, moduleId: nil };
1416
1417
    return sym;
1418
}
1419
1420
/// Check that a type is boolean, otherwise throw an error.
1421
fn checkBoolean(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1422
    return try checkEqual(self, node, Type::Bool);
1423
}
1424
1425
/// Check that a type is numeric, otherwise throw an error.
1426
fn checkNumeric(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
1427
    let ty = try infer(self, node);
1428
    if not isNumericType(ty) {
1429
        throw emitError(self, node, ErrorKind::ExpectedNumeric);
1430
    }
1431
    return ty;
1432
}
1433
1434
/// Check if a type is a numeric type.
1435
fn isNumericType(ty: Type) -> bool {
1436
    match ty {
1437
        case Type::U8, Type::U16, Type::U32, Type::U64,
1438
             Type::I8, Type::I16, Type::I32, Type::I64,
1439
             Type::Int => return true,
1440
        else => return false,
1441
    }
1442
}
1443
1444
/// Check if a type is an unsigned integer type.
1445
export fn isUnsignedIntegerType(ty: Type) -> bool {
1446
    match ty {
1447
        case Type::U8, Type::U16, Type::U32, Type::U64 => return true,
1448
        else => return false,
1449
    }
1450
}
1451
1452
/// Return the maximum of two u32 values.
1453
fn max(a: u32, b: u32) -> u32 {
1454
    if a > b {
1455
        return a;
1456
    }
1457
    return b;
1458
}
1459
1460
/// Get the layout of a type.
1461
export fn getTypeLayout(ty: Type) -> Layout {
1462
    match ty {
1463
        case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1464
        case Type::Slice { .. }, Type::TraitObject { .. } =>
1465
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1466
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
1467
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
1468
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
1469
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
1470
        case Type::Int => return Layout { size: 8, alignment: 8 },
1471
        case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 },
1472
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1473
        case Type::Array(arr) => return getArrayLayout(arr),
1474
        case Type::Optional(inner) => return getOptionalLayout(*inner),
1475
        case Type::Nominal(info) => return getNominalLayout(*info),
1476
        else => {
1477
            panic "getTypeLayout: the given type cannot be layed out";
1478
        }
1479
    }
1480
}
1481
1482
/// Get the layout of a type or value.
1483
export fn getLayout(self: *Resolver, node: *ast::Node, ty: Type) -> Layout {
1484
    let mut layout = getTypeLayout(ty);
1485
    // Check for symbol-specific alignment override.
1486
    if let sym = symbolFor(self, node) {
1487
        if let case SymbolData::Value { alignment, .. } = sym.data {
1488
            if alignment > 0 {
1489
                set layout.alignment = alignment;
1490
            }
1491
        }
1492
    }
1493
    return layout;
1494
}
1495
1496
/// Get the layout of an array type.
1497
export fn getArrayLayout(arr: ArrayType) -> Layout {
1498
    let itemLayout = getTypeLayout(*arr.item);
1499
    return Layout {
1500
        size: itemLayout.size * arr.length,
1501
        alignment: itemLayout.alignment,
1502
    };
1503
}
1504
1505
/// Get the layout of an optional type.
1506
export fn getOptionalLayout(inner: Type) -> Layout {
1507
    // Nullable types use null pointer optimization -- no tag byte needed.
1508
    if isNullableType(inner) {
1509
        return getTypeLayout(inner);
1510
    }
1511
    let innerLayout = getTypeLayout(inner);
1512
    let tagSize: u32 = 1;
1513
    let valOffset = mem::alignUp(tagSize, innerLayout.alignment);
1514
    let alignment = max(innerLayout.alignment, 1);
1515
1516
    return Layout {
1517
        size: mem::alignUp(valOffset + innerLayout.size, alignment),
1518
        alignment,
1519
    };
1520
}
1521
1522
/// Get the payload offset within an optional aggregate.
1523
export fn getOptionalValOffset(inner: Type) -> u32 {
1524
    let innerLayout = getTypeLayout(inner);
1525
    return mem::alignUp(1, innerLayout.alignment);
1526
}
1527
1528
/// Check if a type is optional.
1529
export fn isOptionalType(ty: Type) -> bool {
1530
    match ty {
1531
        case Type::Optional(_) => return true,
1532
        else => return false,
1533
    }
1534
}
1535
1536
/// Check if a type uses null pointer optimization.
1537
/// This applies to optional pointers `?*T` and optional slices `?*[T]`,
1538
/// where `nil` is represented as a null data pointer with no tag byte.
1539
export fn isOptionalPointer(ty: Type) -> bool {
1540
    if let case Type::Optional(inner) = ty {
1541
        return isNullableType(*inner);
1542
    }
1543
    return false;
1544
}
1545
1546
/// Check if a type uses the optional aggregate representation.
1547
export fn isOptionalAggregate(ty: Type) -> bool {
1548
    if let case Type::Optional(inner) = ty {
1549
        return not isNullableType(*inner);
1550
    }
1551
    return false;
1552
}
1553
1554
/// Check if a type can use null to represent `nil`.
1555
/// Pointers and slices have a data pointer that is never null when valid.
1556
export fn isNullableType(ty: Type) -> bool {
1557
    match ty {
1558
        case Type::Pointer { .. }, Type::Slice { .. } => return true,
1559
        else => return false,
1560
    }
1561
}
1562
1563
/// Get the layout of a nominal type.
1564
export fn getNominalLayout(info: NominalType) -> Layout {
1565
    match info {
1566
        case NominalType::Placeholder(_) => {
1567
            panic "getNominalLayout: placeholder type";
1568
        }
1569
        case NominalType::Record(recordType) => {
1570
            return recordType.layout;
1571
        }
1572
        case NominalType::Union(unionType) => {
1573
            return unionType.layout;
1574
        }
1575
    }
1576
}
1577
1578
/// Get the layout of a result aggregate with a tag and the larger payload.
1579
export fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout {
1580
    let payloadLayout = getTypeLayout(payload);
1581
    let mut maxSize = payloadLayout.size;
1582
    let mut maxAlign = payloadLayout.alignment;
1583
1584
    for errType in throwList {
1585
        let errLayout = getTypeLayout(*errType);
1586
        set maxSize = max(maxSize, errLayout.size);
1587
        set maxAlign = max(maxAlign, errLayout.alignment);
1588
    }
1589
    return Layout {
1590
        size: PTR_SIZE + maxSize,
1591
        alignment: max(PTR_SIZE, maxAlign),
1592
    };
1593
}
1594
1595
/// Compute the layout for a union given its resolved variants.
1596
fn computeUnionLayout(variants: *[UnionVariant]) -> UnionLayoutInfo {
1597
    let tagSize: u32 = 1;
1598
    let mut maxVarSize: u32 = 0;
1599
    let mut maxVarAlign: u32 = 1;
1600
    let mut isAllVoid: bool = true;
1601
1602
    for variant in variants {
1603
        if variant.valueType <> Type::Void {
1604
            set isAllVoid = false;
1605
            let payloadLayout = getTypeLayout(variant.valueType);
1606
            set maxVarSize = max(maxVarSize, payloadLayout.size);
1607
            set maxVarAlign = max(maxVarAlign, payloadLayout.alignment);
1608
        }
1609
    }
1610
    let unionAlignment: u32 = max(1, maxVarAlign);
1611
    let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign);
1612
    let unionLayout = Layout {
1613
        size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment),
1614
        alignment: unionAlignment,
1615
    };
1616
    return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid };
1617
}
1618
1619
/// Compute the discriminant tag for a variant, advancing the iota counter.
1620
/// If the variant has an explicit `= N` value, uses that; otherwise uses iota.
1621
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: *mut u32) -> u32 {
1622
    let mut tag: u32 = *iota;
1623
    if let valueNode = variantDecl.value {
1624
        let case ast::NodeValue::Number(lit) = valueNode.value
1625
            else panic "variantTag: expected number literal";
1626
        set tag = lit.magnitude as u32;
1627
    }
1628
    set *iota = tag + 1;
1629
    return tag;
1630
}
1631
1632
/// Check if a type is a union without payloads.
1633
export fn isVoidUnion(ty: Type) -> bool {
1634
    let case Type::Nominal(NominalType::Union(unionType)) = ty
1635
        else return false;
1636
    return unionType.isAllVoid;
1637
}
1638
1639
/// Check if a type should be treated as an address-like value.
1640
fn isAddressType(ty: Type) -> bool {
1641
    if isNullableType(ty) {
1642
        return true;
1643
    }
1644
    match ty {
1645
        case Type::Fn(_) => return true,
1646
        else => return false,
1647
    }
1648
}
1649
1650
/// Return the representable range for an integer type.
1651
fn integerRange(ty: Type) -> ?IntegerRange {
1652
    match ty {
1653
        case Type::I8 => return IntegerRange::Signed {
1654
            bits: 8,
1655
            min: I8_MIN as i64,
1656
            max: I8_MAX as i64,
1657
            lim: (I8_MAX as u64) + 1,
1658
        },
1659
        case Type::I16 => return IntegerRange::Signed {
1660
            bits: 16,
1661
            min: I16_MIN as i64,
1662
            max: I16_MAX as i64,
1663
            lim: (I16_MAX as u64) + 1,
1664
        },
1665
        case Type::I32 => return IntegerRange::Signed {
1666
            bits: 32,
1667
            min: I32_MIN as i64,
1668
            max: I32_MAX as i64,
1669
            lim: (I32_MAX as u64) + 1,
1670
        },
1671
        case Type::I64, Type::Int => return IntegerRange::Signed {
1672
            bits: 64,
1673
            min: I64_MIN,
1674
            max: I64_MAX,
1675
            lim: (I64_MAX as u64) + 1,
1676
        },
1677
        case Type::U8 => return IntegerRange::Unsigned { bits: 8, max: U8_MAX as u64 },
1678
        case Type::U16 => return IntegerRange::Unsigned { bits: 16, max: U16_MAX as u64 },
1679
        case Type::U32 => return IntegerRange::Unsigned { bits: 32, max: parser::U32_MAX as u64 },
1680
        case Type::U64 => return IntegerRange::Unsigned { bits: 64, max: parser::U64_MAX },
1681
        else => return nil,
1682
    }
1683
}
1684
1685
/// Validate that an integer constant fits within the target type's range.
1686
fn validateConstIntRange(value: ConstValue, target: Type) -> bool {
1687
    let range = integerRange(target)
1688
        else panic "validateConstIntRange: expected integer type";
1689
    let case ConstValue::Int(int) = value
1690
        else panic "validateConstIntRange: expected integer constant";
1691
1692
    match range {
1693
        case IntegerRange::Signed { lim, .. } => {
1694
            if int.negative {
1695
                if int.magnitude > lim {
1696
                    return false;
1697
                }
1698
                return true;
1699
            }
1700
            if int.magnitude > lim - 1 {
1701
                return false;
1702
            }
1703
            return true;
1704
        }
1705
        case IntegerRange::Unsigned { max, .. } => {
1706
            if int.negative or int.magnitude > max {
1707
                return false;
1708
            }
1709
            return true;
1710
        }
1711
    }
1712
}
1713
1714
/// Ensure all nested nominal types in a type are resolved.
1715
fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
1716
    match ty {
1717
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1718
        case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site),
1719
        case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse.
1720
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1721
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
1722
        else => {},
1723
    }
1724
}
1725
1726
/// Ensure a nominal type has its body resolved.
1727
fn ensureNominalResolved(self: *mut Resolver, tyInfo: *NominalType, site: *ast::Node)
1728
    throws (ResolveError)
1729
{
1730
    if let case NominalType::Placeholder(declNode) = *tyInfo {
1731
        // When resolving on-demand (e.g. from a child module), switch to the
1732
        // declaring module's scope so field type lookups find the right symbols.
1733
        let prevScope = self.scope;
1734
        let prevMod = self.currentMod;
1735
1736
        if let sym = symbolFor(self, declNode) {
1737
            if let mid = sym.moduleId {
1738
                if (mid as u32) < self.moduleScopes.len {
1739
                    if let ms = self.moduleScopes[mid as u32] {
1740
                        set self.scope = ms;
1741
                        set self.currentMod = mid;
1742
                    }
1743
                }
1744
            }
1745
        }
1746
1747
        match declNode.value {
1748
            case ast::NodeValue::RecordDecl(decl) => {
1749
                try resolveRecordBody(self, declNode, decl);
1750
            }
1751
            case ast::NodeValue::UnionDecl(decl) => {
1752
                try resolveUnionBody(self, declNode, decl);
1753
            }
1754
            else => {},
1755
        }
1756
        set self.scope = prevScope;
1757
        set self.currentMod = prevMod;
1758
    }
1759
}
1760
1761
/// Check if all elements in a node list are assignable to the target type.
1762
fn isListAssignable(self: *mut Resolver, targetType: Type, items: *mut [*ast::Node]) -> bool {
1763
    for itemNode in items {
1764
        let elemTy = typeFor(self, itemNode)
1765
            else return false;
1766
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
1767
            // Do nothing.
1768
        } else {
1769
            return false;
1770
        }
1771
    }
1772
    return true;
1773
}
1774
1775
/// Return whether pointer classes are compatible.
1776
fn pointerClassesAssignable(
1777
    to: types::PointerClass,
1778
    from: types::PointerClass,
1779
) -> bool {
1780
    return to == from;
1781
}
1782
1783
/// Check if the `from` type is assignable to the `to` type, and return a
1784
/// coercion plan if so.
1785
fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
1786
    if to == Type::Unknown or from == Type::Unknown {
1787
        return nil;
1788
    }
1789
    if from == Type::Undefined {
1790
        // TODO: Don't let `undefined` be used in place of functions and other
1791
        // non-data types.
1792
        return Coercion::Identity;
1793
    }
1794
    // The "never" type can always be assigned, since the code path is never
1795
    // executed.
1796
    if from == Type::Never {
1797
        return Coercion::Identity;
1798
    }
1799
    if to == from {
1800
        return Coercion::Identity;
1801
    }
1802
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
1803
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
1804
            else return nil;
1805
        if not pointerClassesAssignable(lhsClass, rhsClass) {
1806
            return nil;
1807
        }
1808
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
1809
        if *lhsTarget == Type::Opaque {
1810
            if lhsMutable and not rhsMutable {
1811
                return nil;
1812
            }
1813
            return Coercion::Identity;
1814
        }
1815
        if lhsMutable and not rhsMutable {
1816
            return nil;
1817
        }
1818
        return isAssignable(self, *lhsTarget, *rhsTarget, rval);
1819
    }
1820
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
1821
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
1822
            if not pointerClassesAssignable(lhsClass, rhsClass)
1823
                or (lhsMutable and not rhsMutable)
1824
            {
1825
                return nil;
1826
            }
1827
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
1828
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
1829
            }
1830
        }
1831
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
1832
            if not pointerClassesAssignable(lhsClass, rhsClass)
1833
                or lhsTraitInfo <> rhsTraitInfo
1834
            {
1835
                return nil;
1836
            }
1837
            if lhsMutable and not rhsMutable {
1838
                return nil;
1839
            }
1840
            return Coercion::Identity;
1841
        }
1842
        return nil;
1843
    }
1844
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
1845
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
1846
            else return nil;
1847
        if not pointerClassesAssignable(lhsClass, rhsClass)
1848
            or (lhsMutable and not rhsMutable)
1849
        {
1850
            return nil;
1851
        }
1852
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
1853
        if *lhsItem == Type::Opaque {
1854
            return Coercion::Identity;
1855
        }
1856
        return isAssignable(self, *lhsItem, *rhsItem, rval);
1857
    }
1858
    match to {
1859
        case Type::Array(lhs) => {
1860
            let case Type::Array(rhs) = from
1861
                else return nil;
1862
1863
            if lhs.length <> rhs.length {
1864
                return nil;
1865
            }
1866
            // For array literals, check each element individually for
1867
            // assignability.
1868
            match rval.value {
1869
                case ast::NodeValue::ArrayLit(items) => {
1870
                    if rhs.length == 0 and lhs.length == 0 {
1871
                        return Coercion::Identity;
1872
                    }
1873
                    // TODO: This won't work, because we should be setting coercions
1874
                    // for every list item, but we don't. It's best to not have an
1875
                    // `isAssignable` function and just have one that records coercions.
1876
                    if isListAssignable(self, *lhs.item, items) {
1877
                        return Coercion::Identity;
1878
                    }
1879
                    return nil;
1880
                }
1881
                case ast::NodeValue::ArrayRepeatLit(repeat) => {
1882
                    return isAssignable(self, *lhs.item, *rhs.item, repeat.item);
1883
                }
1884
                else => {
1885
                    if typesEqual(*lhs.item, *rhs.item) {
1886
                        return Coercion::Identity;
1887
                    }
1888
                    return nil;
1889
                }
1890
            }
1891
        }
1892
1893
        case Type::Optional(inner) => {
1894
            if from == Type::Nil {
1895
                return Coercion::OptionalLift(to);
1896
            }
1897
            if let _ = isAssignable(self, *inner, from, rval) {
1898
                return Coercion::OptionalLift(to);
1899
            }
1900
            if let case Type::Optional(fromInner) = from {
1901
                return isAssignable(self, *inner, *fromInner, rval);
1902
            }
1903
            return nil;
1904
        }
1905
1906
        case Type::Fn(toInfo) => {
1907
            // Allow function type structural matching.
1908
            if let case Type::Fn(fromInfo) = from {
1909
                if fnTypeEqual(toInfo, fromInfo) {
1910
                    return Coercion::Identity;
1911
                }
1912
            }
1913
            return nil;
1914
        }
1915
        else => {
1916
            if isNumericType(to) and isNumericType(from) {
1917
                // Perform range validation at compile time if possible.
1918
                // For unsuffixed integer expressions (`Type::Int`), only
1919
                // validate literals directly written by the programmer.
1920
                // Folded results (e.g. `0 - 65`) may not fit the target
1921
                // type but are valid wrapping arithmetic at runtime.
1922
                if let value = constValueEntry(self, rval) {
1923
                    if from <> Type::Int or isIntegerLiteralExpr(rval) {
1924
                        if validateConstIntRange(value, to) {
1925
                            return Coercion::Identity;
1926
                        }
1927
                        return nil;
1928
                    }
1929
                    // Folded constant expression (e.g. `1 + 2`): if the
1930
                    // result fits the target, use identity. Otherwise allow
1931
                    // wrapping via numeric cast.
1932
                    if validateConstIntRange(value, to) {
1933
                        return Coercion::Identity;
1934
                    }
1935
                }
1936
                // Allow unsuffixed integer expressions to be inferred from context.
1937
                if from == Type::Int {
1938
                    return Coercion::NumericCast { from, to };
1939
                }
1940
                // Non-constant numeric values require an explicit cast.
1941
                return nil;
1942
            }
1943
        }
1944
    }
1945
    return nil;
1946
}
1947
1948
/// Check if two function type descriptors are structurally equivalent.
1949
fn fnTypeEqual(a: *FnType, b: *FnType) -> bool {
1950
    if a.isUnsafe <> b.isUnsafe {
1951
        return false;
1952
    }
1953
    if a.paramTypes.len <> b.paramTypes.len {
1954
        return false;
1955
    }
1956
    if a.throwList.len <> b.throwList.len {
1957
        return false;
1958
    }
1959
    if not typesEqual(*a.returnType, *b.returnType) {
1960
        return false;
1961
    }
1962
    for i in 0..a.paramTypes.len {
1963
        if not typesEqual(*a.paramTypes[i], *b.paramTypes[i]) {
1964
            return false;
1965
        }
1966
    }
1967
    for i in 0..a.throwList.len {
1968
        if not typesEqual(*a.throwList[i], *b.throwList[i]) {
1969
            return false;
1970
        }
1971
    }
1972
    return true;
1973
}
1974
1975
/// Check if two types are structurally equal.
1976
export fn typesEqual(a: Type, b: Type) -> bool {
1977
    if a == b {
1978
        return true;
1979
    }
1980
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
1981
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
1982
            else return false;
1983
        return aClass == bClass and aMutable == bMutable
1984
            and typesEqual(*aTarget, *bTarget);
1985
    }
1986
    if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a {
1987
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
1988
            else return false;
1989
        return aClass == bClass and aMutable == bMutable
1990
            and typesEqual(*aItem, *bItem);
1991
    }
1992
    if let case Type::TraitObject { class: aClass, traitInfo: aTraitInfo, mutable: aMutable } = a {
1993
        let case Type::TraitObject { class: bClass, traitInfo: bTraitInfo, mutable: bMutable } = b
1994
            else return false;
1995
        return aClass == bClass and aMutable == bMutable
1996
            and aTraitInfo == bTraitInfo;
1997
    }
1998
    match a {
1999
        case Type::Array(aa) => {
2000
            let case Type::Array(ab) = b else return false;
2001
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
2002
        }
2003
        case Type::Optional(oa) => {
2004
            let case Type::Optional(ob) = b else return false;
2005
            return typesEqual(*oa, *ob);
2006
        }
2007
        case Type::Fn(fa) => {
2008
            let case Type::Fn(fb) = b else return false;
2009
            return fnTypeEqual(fa, fb);
2010
        }
2011
        else => return false,
2012
    }
2013
}
2014
2015
/// Return whether `ty` is a direct reference.
2016
export fn isRefType(ty: Type) -> bool {
2017
    match ty {
2018
        case Type::Pointer { class: types::PointerClass::Ref, .. },
2019
             Type::Slice { class: types::PointerClass::Ref, .. },
2020
             Type::TraitObject { class: types::PointerClass::Ref, .. } => return true,
2021
        else => return false,
2022
    }
2023
}
2024
2025
/// Return whether a type contains a reference.
2026
fn containsRef(ty: Type) -> bool {
2027
    if isRefType(ty) {
2028
        return true;
2029
    }
2030
    if let case Type::Pointer { target, .. } = ty {
2031
        return containsRef(*target);
2032
    }
2033
    if let case Type::Slice { item, .. } = ty {
2034
        return containsRef(*item);
2035
    }
2036
    match ty {
2037
        case Type::Array(array) => return containsRef(*array.item),
2038
        case Type::Optional(inner) => return containsRef(*inner),
2039
        // Nominal declarations validate their own fields and variants.
2040
        // Treating them as leaves also terminates recursive pointer types.
2041
        case Type::Nominal(_) => return false,
2042
        else => return false,
2043
    }
2044
}
2045
2046
/// Return whether a type is exact-linear.
2047
export fn isLinear(ty: Type) -> bool {
2048
    match ty {
2049
        case Type::Pointer { class: types::PointerClass::Owned, .. },
2050
             Type::Slice { class: types::PointerClass::Owned, .. },
2051
             Type::TraitObject { class: types::PointerClass::Owned, .. } => return true,
2052
        case Type::Pointer { class: types::PointerClass::Ref, .. },
2053
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
2054
             Type::Slice { class: types::PointerClass::Ref, .. },
2055
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2056
             Type::TraitObject { class: types::PointerClass::Ref, .. },
2057
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return false,
2058
2059
        case Type::Array(array) => return isLinear(*array.item),
2060
        case Type::Optional(inner) => return isLinear(*inner),
2061
        case Type::Nominal(NominalType::Record(recInfo)) => {
2062
            if recInfo.declaredLinear {
2063
                return true;
2064
            }
2065
            for field in recInfo.fields {
2066
                if isLinear(field.fieldType) {
2067
                    return true;
2068
                }
2069
            }
2070
            return false;
2071
        }
2072
        case Type::Nominal(NominalType::Union(unionType)) => {
2073
            if unionType.declaredLinear {
2074
                return true;
2075
            }
2076
            for variant in unionType.variants {
2077
                if isLinear(variant.valueType) {
2078
                    return true;
2079
                }
2080
            }
2081
            return false;
2082
        }
2083
        else => return false,
2084
    }
2085
}
2086
2087
/// Return whether `ty` is a direct unsafe pointer-like value.
2088
fn isUnsafePointerType(ty: Type) -> bool {
2089
    match ty {
2090
        case Type::Pointer { class: types::PointerClass::Unsafe, .. },
2091
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2092
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
2093
        else => return false,
2094
    }
2095
}
2096
2097
/// Get the record info from a record type.
2098
export fn getRecord(ty: Type) -> ?RecordType {
2099
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
2100
    return recInfo;
2101
}
2102
2103
/// Auto-dereference a type: if it's a pointer, return the target type.
2104
export fn autoDeref(ty: Type) -> Type {
2105
    if let case Type::Pointer { target, .. } = ty {
2106
        return *target;
2107
    }
2108
    return ty;
2109
}
2110
2111
/// Get field info for a record-like type (records, slices) by field index.
2112
export fn getRecordField(ty: Type, index: u32) -> ?RecordField {
2113
    if let case Type::Slice { class, item, mutable } = ty {
2114
        match index {
2115
            case 0 => return RecordField {
2116
                name: PTR_FIELD,
2117
                fieldType: Type::Pointer { class, target: item, mutable },
2118
                offset: 0,
2119
            },
2120
            case 1 => return RecordField {
2121
                name: LEN_FIELD,
2122
                fieldType: Type::U32,
2123
                offset: PTR_SIZE as i32,
2124
            },
2125
            case 2 => return RecordField {
2126
                name: CAP_FIELD,
2127
                fieldType: Type::U32,
2128
                offset: PTR_SIZE as i32 + 4,
2129
            },
2130
            else => return nil,
2131
        }
2132
    }
2133
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
2134
        index < recInfo.fields.len
2135
    {
2136
        return recInfo.fields[index];
2137
    }
2138
    return nil;
2139
}
2140
2141
/// Check if the two types can be compared for equality.
2142
fn isComparable(left: Type, right: Type) -> bool {
2143
    if left == Type::Unknown or right == Type::Unknown {
2144
        return false;
2145
    }
2146
    if left == right {
2147
        return true;
2148
    }
2149
    // Comparisons with optionals.
2150
    if let case Type::Optional(l) = left {
2151
        if let case Type::Optional(r) = right {
2152
            return isComparable(*l, *r);
2153
        } else if right == Type::Nil {
2154
            return true;
2155
        }
2156
        return isComparable(*l, right);
2157
    } else if let case Type::Optional(_) = right {
2158
        return isComparable(right, left); // Flip order.
2159
    }
2160
    // Pointer comparisons ignore mutability.
2161
    if let case Type::Pointer { target: lTarget, .. } = left {
2162
        if let case Type::Pointer { target: rTarget, .. } = right {
2163
            return typesEqual(*lTarget, *rTarget);
2164
        }
2165
    }
2166
    // Numeric types.
2167
    if isNumericType(left) and isNumericType(right) {
2168
        return true;
2169
    }
2170
    return false;
2171
}
2172
2173
/// Check if the `from` type is assignable to the `to` type, and return a
2174
/// coercion plan if so, or throw an error if not.
2175
fn expectAssignable(self: *mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
2176
    // Ensure any nested nominal types are resolved before checking assignability.
2177
    try ensureTypeResolved(self, to, site);
2178
    if let coercion = isAssignable(self, to, from, site) {
2179
        return setNodeCoercion(self, site, coercion);
2180
    }
2181
    throw emitTypeMismatch(self, site, TypeMismatch {
2182
        expected: to,
2183
        actual: from,
2184
    });
2185
}
2186
2187
/// Check that a type is optional, otherwise throw an error.
2188
fn checkOptional(self: *mut Resolver, node: *ast::Node) -> *Type
2189
    throws (ResolveError)
2190
{
2191
    if let case Type::Optional(inner) = try infer(self, node) {
2192
        return inner;
2193
    }
2194
    throw emitError(self, node, ErrorKind::ExpectedOptional);
2195
}
2196
2197
/// Check that a node's type is equal to the expected type.
2198
fn checkEqual(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type
2199
    throws (ResolveError)
2200
{
2201
    let actualTy = try visit(self, node, expected);
2202
    if actualTy <> expected {
2203
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy });
2204
    }
2205
    return actualTy;
2206
}
2207
2208
/// Bind an identifier in the given scope.
2209
fn bindIdent(
2210
    self: *mut Resolver,
2211
    name: *[u8],
2212
    owner: *ast::Node,
2213
    data: SymbolData,
2214
    attrs: u32,
2215
    scope: *mut Scope
2216
) -> *mut Symbol throws (ResolveError) {
2217
    let sym = allocSymbol(self, data, name, owner, attrs);
2218
    try addSymbolToScope(self, sym, scope, owner);
2219
    setNodeSymbol(self, owner, sym);
2220
2221
    return sym;
2222
}
2223
2224
/// Add a symbol to the given scope.
2225
fn addSymbolToScope(self: *mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) {
2226
    for i in 0..scope.symbolsLen {
2227
        if scope.symbols[i].name == sym.name {
2228
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
2229
        }
2230
    }
2231
    if scope.symbolsLen >= scope.symbols.len {
2232
        throw emitError(self, site, ErrorKind::SymbolOverflow);
2233
    }
2234
    // Preserve the defining module when importing an existing symbol into
2235
    // another module's scope.
2236
    if sym.moduleId == nil {
2237
        if let modId = scope.moduleId {
2238
            set sym.moduleId = modId;
2239
        }
2240
    }
2241
    set scope.symbols[scope.symbolsLen] = sym;
2242
    set scope.symbolsLen += 1;
2243
}
2244
2245
/// Bind a value identifier in the current scope.
2246
/// Returns `nil` if the identifier is a placeholder (`_`).
2247
fn bindValueIdent(
2248
    self: *mut Resolver,
2249
    ident: *ast::Node,
2250
    owner: *ast::Node,
2251
    type: Type,
2252
    mutable: bool,
2253
    alignment: u32,
2254
    attrs: u32
2255
) -> ?*mut Symbol throws (ResolveError) {
2256
    if let case ast::NodeValue::Placeholder = ident.value {
2257
        setNodeType(self, owner, type);
2258
        return nil;
2259
    }
2260
    let name = try nodeName(self, ident);
2261
    let data = SymbolData::Value { mutable, alignment, type, addressTaken: false };
2262
    let sym = try bindIdent(self, name, owner, data, attrs, self.scope);
2263
    setNodeType(self, owner, type);
2264
    setNodeType(self, ident, type);
2265
2266
    // Track number of local bindings for lowering stage.
2267
    if let mut fnType = self.currentFn {
2268
        set fnType.localCount += 1;
2269
    }
2270
    return sym;
2271
}
2272
2273
/// Bind a constant identifier in the current scope.
2274
fn bindConstIdent(
2275
    self: *mut Resolver,
2276
    ident: *ast::Node,
2277
    owner: *ast::Node,
2278
    type: Type,
2279
    val: ?ConstValue,
2280
    attrs: u32
2281
) -> *mut Symbol throws (ResolveError) {
2282
    let name = try nodeName(self, ident);
2283
    let data = SymbolData::Constant { type, value: val };
2284
    let sym = try bindIdent(self, name, owner, data, attrs, self.scope);
2285
    setNodeType(self, owner, type);
2286
    setNodeType(self, ident, type);
2287
2288
    return sym;
2289
}
2290
2291
/// Bind a module identifier in the given scope.
2292
/// This is used when declaring modules with `mod` or
2293
/// importing modules with `use`.
2294
fn bindModuleIdent(
2295
    self: *mut Resolver,
2296
    entry: *module::ModuleEntry,
2297
    scope: *mut Scope,
2298
    owner: *ast::Node,
2299
    attrs: u32,
2300
    bindingScope: *mut Scope
2301
) -> *mut Symbol throws (ResolveError) {
2302
    let data = SymbolData::Module { entry, scope };
2303
    let name = entry.name;
2304
2305
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
2306
}
2307
2308
/// Bind a type identifier in the current scope.
2309
fn bindTypeIdent(
2310
    self: *mut Resolver,
2311
    ident: *ast::Node,
2312
    owner: *ast::Node,
2313
    type: *mut NominalType,
2314
    attrs: u32
2315
) -> *mut Symbol throws (ResolveError) {
2316
    let name = try nodeName(self, ident);
2317
    let data = SymbolData::Type(type);
2318
    return try bindIdent(self, name, owner, data, attrs, self.scope);
2319
}
2320
2321
/// Predicate that matches any symbol.
2322
fn isAnySymbol(_sym: *mut Symbol) -> bool {
2323
    return true;
2324
}
2325
2326
/// Predicate that matches value or constant symbols.
2327
fn isValueSymbol(sym: *mut Symbol) -> bool {
2328
    if let case SymbolData::Value { .. } = sym.data {
2329
        return true;
2330
    }
2331
    if let case SymbolData::Constant { .. } = sym.data {
2332
        return true;
2333
    }
2334
    return false;
2335
}
2336
2337
/// Predicate that matches type symbols.
2338
fn isTypeSymbol(sym: *mut Symbol) -> bool {
2339
    if let case SymbolData::Type(_) = sym.data {
2340
        return true;
2341
    }
2342
    return false;
2343
}
2344
2345
/// Find a symbol by name in a specific scope, filtered by a predicate.
2346
fn findInScope(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol {
2347
    for i in 0..scope.symbolsLen {
2348
        let sym = scope.symbols[i];
2349
        if sym.name == name and predicate(sym) {
2350
            return sym;
2351
        }
2352
    }
2353
    return nil;
2354
}
2355
2356
/// Find a symbol by name, traversing scopes upwards, filtered by a predicate.
2357
fn findInScopeRecursive(scope: *Scope, name: *[u8], predicate: fn(*mut Symbol) -> bool) -> ?*mut Symbol {
2358
    let mut curr = scope;
2359
    loop {
2360
        if let sym = findInScope(curr, name, predicate) {
2361
            return sym;
2362
        }
2363
        if let parent = curr.parent {
2364
            set curr = parent;
2365
        } else {
2366
            break;
2367
        }
2368
    }
2369
    return nil;
2370
}
2371
2372
/// Find a symbol by name in a specific scope (matches any symbol kind).
2373
export fn findSymbolInScope(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2374
    return findInScope(scope, name, isAnySymbol);
2375
}
2376
2377
/// Look up a value symbol by name, searching from the given scope outward.
2378
fn findValueSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2379
    return findInScopeRecursive(scope, name, isValueSymbol);
2380
}
2381
2382
/// Look up a type symbol by name, searching from the given scope outward.
2383
fn findTypeSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2384
    return findInScopeRecursive(scope, name, isTypeSymbol);
2385
}
2386
2387
/// Like `findValueSymbol`, but finds symbols of any kinds.
2388
fn findAnySymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol {
2389
    return findInScopeRecursive(scope, name, isAnySymbol);
2390
}
2391
2392
/// Flatten an identifier or scope access chain into an array of name segments.
2393
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
2394
/// Returns a slice of the segments that were written.
2395
fn flattenPath(
2396
    self: *mut Resolver,
2397
    node: *ast::Node,
2398
    buf: *mut [*[u8]]
2399
) -> *[*[u8]] throws (ResolveError) {
2400
    let mut out: *[*[u8]] = &[];
2401
2402
    match node.value {
2403
        case ast::NodeValue::Ident(name) if name.len > 0 => {
2404
            assert buf.len >= 1, "flattenPath: invalid output buffer size";
2405
            set buf[0] = name;
2406
            set out = &buf[..1];
2407
        }
2408
        case ast::NodeValue::ScopeAccess(access) => {
2409
            // Recursively flatten parent path.
2410
            let parent = try flattenPath(self, access.parent, buf);
2411
            assert parent.len < buf.len, "flattenPath: invalid output buffer size";
2412
            let child = try nodeName(self, access.child);
2413
            set buf[parent.len] = child;
2414
            set out = &buf[..parent.len + 1];
2415
        }
2416
        case ast::NodeValue::Super => {
2417
            // `super` is handled by scope adjustment in `checkSuperAccess`.
2418
            // Return empty prefix so the path continues from the next segment.
2419
            set out = &buf[..0];
2420
            return out;
2421
        }
2422
        else => {
2423
            // Fallthrough to error.
2424
        }
2425
    }
2426
    if out.len < 1 {
2427
        throw emitError(self, node, ErrorKind::InvalidIdentifier(node));
2428
    }
2429
    return out;
2430
}
2431
2432
/// Find the module ID for a given scope by walking up the scope chain until
2433
/// we hit the module's scope.
2434
fn findModuleForScope(scope: *Scope) -> ?u16 {
2435
    let mut s = scope;
2436
    loop {
2437
        if let id = s.moduleId {
2438
            return id;
2439
        }
2440
        if let parent = s.parent {
2441
            set s = parent;
2442
        } else {
2443
            return nil;
2444
        }
2445
    }
2446
}
2447
2448
/// Get the parent module scope for the current module.
2449
/// Returns the scope of the parent module, or `nil` if this is a root module.
2450
fn getParentModuleScope(self: *mut Resolver, node: *ast::Node) -> ?*mut Scope throws (ResolveError) {
2451
    let currentMod = module::get(self.moduleGraph, self.currentMod)
2452
        else throw emitError(self, node, ErrorKind::Internal);
2453
    let parentId = currentMod.parent
2454
        else return nil; // No parent module.
2455
2456
    return self.moduleScopes[parentId as u32];
2457
}
2458
2459
/// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`).
2460
/// Returns the parent scope and the original node so `flattenPath` can strip `super`.
2461
fn checkSuperAccess(
2462
    self: *mut Resolver,
2463
    node: *ast::Node
2464
) -> ?SuperAccessResult throws (ResolveError) {
2465
    // TODO: Maybe we should deal with `super` after the path is flattened.
2466
    if let case ast::NodeValue::ScopeAccess(access) = node.value {
2467
        // Direct super access: `super::x`.
2468
        if let case ast::NodeValue::Super = access.parent.value {
2469
            let parentScope = try getParentModuleScope(self, node)
2470
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
2471
            return SuperAccessResult { scope: parentScope, child: node };
2472
        }
2473
        // Nested super access: `super::x::y`, check if parent path contains `super`.
2474
        if let _ = try checkSuperAccess(self, access.parent) {
2475
            let parentScope = try getParentModuleScope(self, node)
2476
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
2477
            return SuperAccessResult { scope: parentScope, child: node };
2478
        }
2479
    }
2480
    return nil;
2481
}
2482
2483
/// Check if a symbol is accessible from the given scope.
2484
/// A symbol is accessible if:
2485
/// * It has the `export` attribute, OR
2486
/// * It's being accessed from within the module where it was defined.
2487
fn isSymbolVisible(sym: *Symbol, symScope: *Scope, fromScope: *Scope) -> bool {
2488
    // Public symbols are visible from anywhere.
2489
    if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
2490
        return true;
2491
    }
2492
    // In test mode, @test symbols are visible from anywhere
2493
    // so the test runner can reference them.
2494
    if ast::hasAttribute(sym.attrs, ast::Attribute::Test) {
2495
        return true;
2496
    }
2497
    // Private symbols are only visible from the same module.
2498
    let symModuleId = findModuleForScope(symScope);
2499
    let currentModuleId = findModuleForScope(fromScope);
2500
2501
    return symModuleId == currentModuleId;
2502
}
2503
2504
/// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol,
2505
/// starting from the given scope.
2506
fn resolveAccess(
2507
    self: *mut Resolver,
2508
    node: *ast::Node,
2509
    access: ast::Access,
2510
    scope: *Scope
2511
) -> *mut Symbol throws (ResolveError) {
2512
    // Handle `super` access by adjusting scope and node.
2513
    let mut startScope = scope;
2514
    let mut pathNode = node;
2515
    if let superAccess = try checkSuperAccess(self, node) {
2516
        set startScope = superAccess.scope;
2517
        set pathNode = superAccess.child;
2518
    }
2519
    // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access,
2520
    // while this function requires a scope access.
2521
    let mut buffer: [*[u8]; 32] = undefined;
2522
    let path = try flattenPath(self, pathNode, &mut buffer[..]);
2523
2524
    return try resolvePath(self, node, access, path, startScope);
2525
}
2526
2527
/// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol,
2528
/// starting from the given scope.
2529
fn resolvePath(
2530
    self: *mut Resolver,
2531
    node: *ast::Node,
2532
    access: ast::Access,
2533
    path: *[*[u8]],
2534
    scope: *Scope
2535
) -> *mut Symbol throws (ResolveError) {
2536
    assert path.len <> 0, "resolvePath: empty path";
2537
    // Start by finding the root of the path.
2538
    let root = path[0];
2539
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
2540
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2541
    let suffix = &path[1..];
2542
2543
    // Check visibility for symbol.
2544
    if not isSymbolVisible(sym, scope, self.scope) {
2545
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2546
    }
2547
    // End condition.
2548
    if suffix.len == 0 {
2549
        return sym;
2550
    }
2551
    // Otherwise, we need to enter the next scope with the path suffix.
2552
    match sym.data {
2553
        case SymbolData::Module { scope, .. } => {
2554
            return try resolvePath(self, node, access, suffix, scope);
2555
        }
2556
        case SymbolData::Type(ty) => {
2557
            // Lazily resolve union body if not yet done.
2558
            try ensureNominalResolved(self, ty, node);
2559
2560
            if let case NominalType::Union(unionType) = *ty {
2561
                // TODO: Recurse with variant so we consolidate everything.
2562
                if suffix.len > 1 {
2563
                    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
2564
                }
2565
                let variantName = suffix[0];
2566
                let variantSym = try resolveUnionVariantAccess(
2567
                    self, node, access, unionType, variantName
2568
                );
2569
                // TODO: This shouldn't be here.
2570
                setNodeType(self, node, Type::Nominal(ty));
2571
                return variantSym;
2572
            }
2573
        }
2574
        else => {} // Fallthrough.
2575
    }
2576
    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
2577
}
2578
2579
/// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope.
2580
/// This traverses the module hierarchy, checking visibility at each step.
2581
fn resolveModulePath(
2582
    self: *mut Resolver,
2583
    module: *ast::Node
2584
) -> ResolvedModule throws (ResolveError) {
2585
    let mut startScope = self.scope;
2586
    let mut pathNode = module;
2587
2588
    // Handle `super` access.
2589
    if let superAccess = try checkSuperAccess(self, module) {
2590
        set startScope = superAccess.scope;
2591
        set pathNode = superAccess.child;
2592
    }
2593
    let mut pathBuf: [*[u8]; 16] = undefined;
2594
    let path = try flattenPath(self, pathNode, &mut pathBuf[..]);
2595
    if path.len == 0 {
2596
        throw emitError(self, module, ErrorKind::UnresolvedSymbol(""));
2597
    }
2598
    let parentName = path[0];
2599
2600
    // First, check if this is a sub-module of the start scope.
2601
    if let sym = findSymbolInScope(startScope, parentName) {
2602
        return try resolveModulePathRecursive(self, module, &path[1..], sym);
2603
    }
2604
    // Not a sub-module, so look in the global scope for a package root.
2605
    let sym = findSymbolInScope(self.pkgScope, parentName)
2606
        else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName));
2607
2608
    return try resolveModulePathRecursive(self, module, &path[1..], sym);
2609
}
2610
2611
/// Recursively resolve the remaining path segments by traversing child modules.
2612
fn resolveModulePathRecursive(
2613
    self: *mut Resolver,
2614
    node: *ast::Node,
2615
    path: *[*[u8]],
2616
    sym: *Symbol
2617
) -> ResolvedModule throws (ResolveError) {
2618
    let case SymbolData::Module { entry, scope } = sym.data
2619
        else throw emitError(self, node, ErrorKind::Internal);
2620
2621
    if path.len == 0 {
2622
        return ResolvedModule { entry, scope };
2623
    }
2624
    let childName = path[0];
2625
    let childSym = findSymbolInScope(scope, childName)
2626
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
2627
2628
    if not isSymbolVisible(childSym, scope, self.scope) {
2629
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
2630
    }
2631
    return try resolveModulePathRecursive(
2632
        self,
2633
        node,
2634
        &path[1..],
2635
        childSym
2636
    );
2637
}
2638
2639
/// Resolve a type name, which could be an identifier or scoped path.
2640
fn resolveTypeName(self: *mut Resolver, node: *ast::Node) -> *NominalType throws (ResolveError) {
2641
    match node.value {
2642
        case ast::NodeValue::Ident(name) => {
2643
            let sym = findTypeSymbol(self.scope, name)
2644
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2645
            let case SymbolData::Type(ty) = sym.data
2646
                else throw emitError(self, node, ErrorKind::Internal);
2647
2648
            setNodeSymbol(self, node, sym);
2649
2650
            return ty;
2651
        }
2652
        case ast::NodeValue::ScopeAccess(access) => {
2653
            let sym = try resolveAccess(self, node, access, self.scope);
2654
            let case SymbolData::Type(ty) = sym.data
2655
                else throw emitError(self, node, ErrorKind::Internal);
2656
2657
            setNodeSymbol(self, node, sym);
2658
2659
            return ty;
2660
        }
2661
        else => panic "resolveTypeName: unsupported node value",
2662
    }
2663
}
2664
2665
/// Visit a top-level declaration in the declaration phase.
2666
/// This binds all names and analyzes signatures, types, and initializers.
2667
/// Function bodies are deferred to the definition phase.
2668
///
2669
/// Nb. User-defined types are already handled by this point.
2670
fn visitDecl(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2671
    match node.value {
2672
        case ast::NodeValue::FnDecl(_),
2673
             ast::NodeValue::ConstDecl(_),
2674
             ast::NodeValue::Mod(_),
2675
             ast::NodeValue::Use(_) => {
2676
            // Handled in previous passes.
2677
        }
2678
        case ast::NodeValue::StaticDecl(_) => {
2679
            try infer(self, node);
2680
        }
2681
        case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => {
2682
            try resolveInstanceDecl(self, node, traitName, targetType, methods);
2683
        }
2684
        case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, attrs } => {
2685
            try resolveMethodDecl(self, node, name, receiverName, receiverType, sig, attrs);
2686
        }
2687
        else => {
2688
            // Ignore non-declaration nodes.
2689
        }
2690
    }
2691
}
2692
2693
/// Require the current declaration to be unsafe.
2694
fn requireUnsafe(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2695
    if self.unsafeDepth == 0 {
2696
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2697
    }
2698
}
2699
2700
/// Require unsafe context when reading an unsafe binding that is not a function declaration.
2701
fn checkUnsafeBindingAccess(
2702
    self: *mut Resolver,
2703
    node: *ast::Node,
2704
    sym: *mut Symbol,
2705
) throws (ResolveError) {
2706
    if not ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) {
2707
        return;
2708
    }
2709
    if let case ast::NodeValue::FnDecl(_) = sym.node.value {
2710
        return;
2711
    }
2712
    match sym.data {
2713
        case SymbolData::Value { .. },
2714
             SymbolData::Constant { .. } => try requireUnsafe(self, node),
2715
        else => {}
2716
    }
2717
}
2718
2719
/// Reject calls from safe code through unsafe function types.
2720
fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType)
2721
    throws (ResolveError)
2722
{
2723
    if info.isUnsafe and self.unsafeDepth == 0 {
2724
        throw emitError(self, node, ErrorKind::UnsafeCall);
2725
    }
2726
}
2727
2728
/// Visit a top-level definition, recursing into sub-modules.
2729
fn visitDef(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2730
    match node.value {
2731
        case ast::NodeValue::FnDecl(decl) => {
2732
            try resolveFnDeclBody(self, node, decl) catch {
2733
                return;
2734
            };
2735
        }
2736
        case ast::NodeValue::Mod(decl) => {
2737
            if not shouldAnalyzeModule(self, decl.attrs) {
2738
                return;
2739
            }
2740
            let modName = try nodeName(self, decl.name);
2741
            let submod = try enterSubModule(self, modName, node);
2742
            let case ast::NodeValue::Block(block) = submod.root.value
2743
                else panic "visitDef: expected block for module root";
2744
            let mut isUnsafe = false;
2745
            if let attrs = decl.attrs {
2746
                set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe);
2747
            }
2748
            if isUnsafe {
2749
                set self.unsafeDepth += 1;
2750
            }
2751
            try resolveModuleDefs(self, &block) catch e {
2752
                if isUnsafe { set self.unsafeDepth -= 1; }
2753
                exitModuleScope(self, submod);
2754
                throw e;
2755
            };
2756
            if isUnsafe {
2757
                set self.unsafeDepth -= 1;
2758
            }
2759
            exitModuleScope(self, submod);
2760
        }
2761
        case ast::NodeValue::RecordDecl(_),
2762
             ast::NodeValue::UnionDecl(_),
2763
             ast::NodeValue::Use(_),
2764
             ast::NodeValue::TraitDecl { .. } => {
2765
            // Skip: already analyzed in declaration phase.
2766
        }
2767
        case ast::NodeValue::InstanceDecl { methods, .. } => {
2768
            try resolveInstanceMethodBodies(self, methods);
2769
        }
2770
        case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } => {
2771
            try resolveMethodBody(self, node, receiverName, sig, body);
2772
        }
2773
        else => {
2774
            // FIXME: This allows module-level statements that should
2775
            // normally only be valid inside function bodies. We currently
2776
            // need this because of how tests are written, but it should
2777
            // be eventually removed.
2778
            try infer(self, node) catch {
2779
                return;
2780
            };
2781
        }
2782
    }
2783
}
2784
2785
/// Try to infer a node's type.
2786
fn infer(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2787
    return try visit(self, node, Type::Unknown);
2788
}
2789
2790
/// Reject nested references while allowing a direct parameter reference.
2791
fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type)
2792
    throws (ResolveError)
2793
{
2794
    if isRefType(ty) {
2795
        if let case Type::Pointer { target, .. } = ty {
2796
            if containsRef(*target) {
2797
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2798
            }
2799
        } else if let case Type::Slice { item, .. } = ty {
2800
            if containsRef(*item) {
2801
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2802
            }
2803
        }
2804
    } else if containsRef(ty) {
2805
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2806
    }
2807
}
2808
2809
/// Require a type that may be stored or escape a call.
2810
fn ensureStorableType(self: *mut Resolver, node: *ast::Node, ty: Type)
2811
    throws (ResolveError)
2812
{
2813
    if containsRef(ty) {
2814
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2815
    }
2816
}
2817
2818
/// Resolve a type signature node.
2819
fn resolveValueType(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2820
    let ty = try visit(self, node, Type::Unknown);
2821
    // Opaque value types are not allowed.
2822
    if ty == Type::Opaque {
2823
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
2824
    }
2825
    try validateValueTypeReferences(self, node, ty);
2826
    return ty;
2827
}
2828
2829
/// Analyze a node's type and check that it can be assigned to the expected type.
2830
fn checkAssignable(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2831
    let actual = try visit(self, node, expected);
2832
    let _ = try expectAssignable(self, expected, actual, node);
2833
    return actual;
2834
}
2835
2836
/// Analyze a node and propagate the resolved type.
2837
/// The `hint` parameter provides type context for inference and validation.
2838
/// When `nil`, the type must be inferred from the expression itself.
2839
fn visit(self: *mut Resolver, node: *ast::Node, hint: Type) -> Type
2840
    throws (ResolveError)
2841
{
2842
    if let ty = typeFor(self, node) {
2843
        return ty;
2844
    }
2845
    match node.value {
2846
        case ast::NodeValue::Ident(name) => {
2847
            let sym = findAnySymbol(self.scope, name)
2848
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
2849
            try checkUnsafeBindingAccess(self, node, sym);
2850
            setNodeSymbol(self, node, sym);
2851
            match sym.data {
2852
                case SymbolData::Value { type, .. } =>
2853
                    return setNodeType(self, node, type),
2854
                case SymbolData::Constant { type, value } => {
2855
                    if let val = value {
2856
                        setNodeConstValue(self, node, val);
2857
                    }
2858
                    return setNodeType(self, node, type);
2859
                },
2860
                case SymbolData::Type(t) =>
2861
                    return setNodeType(self, node, Type::Nominal(t)),
2862
                case SymbolData::Variant { .. } =>
2863
                    return Type::Void,
2864
                case SymbolData::Module { .. } =>
2865
                    throw emitError(self, node, ErrorKind::UnexpectedModuleName),
2866
                case SymbolData::Trait(_) =>
2867
                    throw emitError(self, node, ErrorKind::UnexpectedTraitName),
2868
            }
2869
        },
2870
        case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal),
2871
        case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access),
2872
        case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop),
2873
        case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block),
2874
        case ast::NodeValue::Unsafe(body) => {
2875
            set self.unsafeDepth += 1;
2876
            let bodyTy = try visit(self, body, hint) catch e {
2877
                set self.unsafeDepth -= 1;
2878
                throw e;
2879
            };
2880
            set self.unsafeDepth -= 1;
2881
            return setNodeType(self, node, bodyTy);
2882
        },
2883
        case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl),
2884
        case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic(
2885
            self, node, decl.ident, decl.type, decl.value, decl.attrs, true
2886
        ),
2887
        case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic(
2888
            self, node, decl.ident, decl.type, decl.value, decl.attrs, false
2889
        ),
2890
        case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param),
2891
        case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond),
2892
        case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond),
2893
        case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond),
2894
        case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode),
2895
        case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode),
2896
        case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode),
2897
        case ast::NodeValue::Loop { body } => {
2898
            let loopType = try visitLoop(self, body);
2899
            return setNodeType(self, node, loopType);
2900
        },
2901
        case ast::NodeValue::Break => {
2902
            try ensureInsideLoop(self, node);
2903
            // Mark that the current loop has a reachable break.
2904
            set self.loopStack[self.loopDepth - 1].hasBreak = true;
2905
2906
            return setNodeType(self, node, Type::Never);
2907
        },
2908
        case ast::NodeValue::Continue => {
2909
            try ensureInsideLoop(self, node);
2910
            return setNodeType(self, node, Type::Never);
2911
        },
2912
        case ast::NodeValue::Match(sw) => return try resolveMatch(self, node, sw),
2913
        case ast::NodeValue::MatchProng(_) => panic "visit: `MatchProng` not handled here",
2914
        case ast::NodeValue::LetElse(letElse) => return try resolveLetElse(self, node, letElse),
2915
        case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, args),
2916
        case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign),
2917
        case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint),
2918
        case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint),
2919
        case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint),
2920
        case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index),
2921
        case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access),
2922
        case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint),
2923
        case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint),
2924
        case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr),
2925
        case ast::NodeValue::Range(range) => return try resolveRange(self, node, range),
2926
        case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint),
2927
        case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value),
2928
        case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr),
2929
        case ast::NodeValue::Panic { message } => {
2930
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2931
                class: types::PointerClass::Owned,
2932
                item: allocType(self, Type::U8),
2933
                mutable: false,
2934
            });
2935
            return setNodeType(self, node, Type::Never);
2936
        },
2937
        case ast::NodeValue::Assert { condition, message } => {
2938
            try visit(self, condition, Type::Bool);
2939
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2940
                class: types::PointerClass::Owned,
2941
                item: allocType(self, Type::U8),
2942
                mutable: false,
2943
            });
2944
            return setNodeType(self, node, Type::Void);
2945
        },
2946
        case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop),
2947
        case ast::NodeValue::ExprStmt(expr) => {
2948
            // Pass `Void` as expected type to indicate value is discarded.
2949
            let exprTy = try visit(self, expr, Type::Void);
2950
            return setNodeType(self, node, unifyBranches(exprTy, Type::Void));
2951
        },
2952
        case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig),
2953
        case ast::NodeValue::Super => {
2954
            // `super` by itself is invalid, must be used in scope access.
2955
            throw emitError(self, node, ErrorKind::InvalidModulePath);
2956
        },
2957
        case ast::NodeValue::Nil => {
2958
            // Use the hint type if it's an optional, otherwise fall back to `Nil`.
2959
            if let case Type::Optional(_) = hint {
2960
                return setNodeType(self, node, hint);
2961
            }
2962
            return setNodeType(self, node, Type::Nil);
2963
        },
2964
        case ast::NodeValue::Undef => {
2965
            try requireUnsafe(self, node);
2966
            return setNodeType(self, node, Type::Undefined);
2967
        },
2968
        case ast::NodeValue::Bool(value) => {
2969
            setNodeConstValue(self, node, ConstValue::Bool(value));
2970
            return setNodeType(self, node, Type::Bool);
2971
        }
2972
        case ast::NodeValue::Char(value) => {
2973
            setNodeConstValue(self, node, ConstValue::Char(value));
2974
            return setNodeType(self, node, Type::U8);
2975
        }
2976
        case ast::NodeValue::String(text) => {
2977
            setNodeConstValue(self, node, ConstValue::String(text));
2978
            let byteTy = allocType(self, Type::U8);
2979
            let sliceTy = allocType(self, Type::Slice {
2980
                class: types::PointerClass::Owned,
2981
                item: byteTy,
2982
                mutable: false,
2983
            });
2984
            return setNodeType(self, node, *sliceTy);
2985
        },
2986
        case ast::NodeValue::Number(lit) => {
2987
            setNodeConstValue(self, node, ConstValue::Int(ConstInt {
2988
                magnitude: lit.magnitude,
2989
                bits: 64,
2990
                signed: false,
2991
                negative: false,
2992
            }));
2993
            return setNodeType(self, node, Type::Int);
2994
        },
2995
        case ast::NodeValue::Placeholder => {
2996
            return setNodeType(self, node, hint);
2997
        },
2998
        else => {
2999
            throw emitError(self, node, ErrorKind::UnexpectedNode(node));
3000
        }
3001
    }
3002
}
3003
3004
/// Visit an optional node when present.
3005
fn visitOptional(self: *mut Resolver, node: ?*ast::Node, hint: Type) -> ?Type
3006
    throws (ResolveError)
3007
{
3008
    if let n = node {
3009
        return try visit(self, n, hint);
3010
    }
3011
    return nil;
3012
}
3013
3014
/// Visit every node contained in a list, returning the last resolved type.
3015
fn visitList(self: *mut Resolver, list: *mut [*ast::Node]) -> Type
3016
    throws (ResolveError)
3017
{
3018
    let mut diverges = false;
3019
    for item in list {
3020
        if try infer(self, item) == Type::Never {
3021
            set diverges = true;
3022
        }
3023
    }
3024
    if diverges {
3025
        return Type::Never;
3026
    }
3027
    return Type::Void;
3028
}
3029
3030
/// Collect attribute flags applied to a declaration.
3031
fn resolveAttributes(self: *mut Resolver, attrs: ?ast::Attributes) -> u32 {
3032
    let list = attrs else return 0;
3033
    let attrNodes = list.list;
3034
    let mut mask: u32 = 0;
3035
3036
    for node in attrNodes {
3037
        let case ast::NodeValue::Attribute(attr) = node.value
3038
            else panic "resolveAttributes: invalid attribute node";
3039
        set mask |= (attr as u32);
3040
    }
3041
    return mask;
3042
}
3043
3044
/// Ensure the `default` attribute is only applied to functions.
3045
fn ensureDefaultAttrNotAllowed(self: *mut Resolver, node: *ast::Node, attrs: u32)
3046
    throws (ResolveError)
3047
{
3048
    let defaultBit = ast::Attribute::Default as u32;
3049
    if (attrs & defaultBit) <> 0 {
3050
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
3051
    }
3052
}
3053
3054
/// Analyze a block node, allocating a nested lexical scope.
3055
fn resolveBlock(self: *mut Resolver, node: *ast::Node, block: ast::Block) -> Type
3056
    throws (ResolveError)
3057
{
3058
    enterScope(self, node);
3059
    let blockTy = try visitList(self, block.statements) catch {
3060
        // One of the statements in the block failed analysis. We simply proceed
3061
        // without checking the rest of the block statements. Return `Never` to
3062
        // avoid spurious `FnMissingReturn` errors.
3063
        exitScope(self);
3064
        return setNodeType(self, node, Type::Never);
3065
    };
3066
    exitScope(self);
3067
3068
    return setNodeType(self, node, blockTy);
3069
}
3070
3071
/// Analyze a `let` declaration and bind its identifier.
3072
fn resolveLet(self: *mut Resolver, node: *ast::Node, decl: ast::Let) -> Type
3073
    throws (ResolveError)
3074
{
3075
    let mut alignment: u32 = 0; // Zero is default.
3076
    let mut bindingTy = Type::Unknown;
3077
3078
    // Check type.
3079
    if let declTy = try visitOptional(self, decl.type, Type::Unknown) {
3080
        let _coercion = try checkAssignable(self, decl.value, declTy);
3081
        set bindingTy = declTy;
3082
    } else {
3083
        set bindingTy = try infer(self, decl.value);
3084
3085
        if not isTypeInferrable(bindingTy) {
3086
            throw emitError(self, decl.value, ErrorKind::CannotInferType);
3087
        }
3088
    }
3089
    // Variables cannot have void type.
3090
    if containsRef(bindingTy) {
3091
        throw emitError(self, node, ErrorKind::RefBinding);
3092
    }
3093
    if bindingTy == Type::Void {
3094
        throw emitError(self, decl.value, ErrorKind::CannotAssignVoid);
3095
    }
3096
    // Variables cannot have opaque type directly.
3097
    if bindingTy == Type::Opaque {
3098
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
3099
    }
3100
    // Check alignment.
3101
    if let a = decl.alignment {
3102
        let case ast::NodeValue::Align { value } = a.value
3103
            else panic "resolveLet: expected Align node";
3104
        set alignment = try checkSizeInt(self, value);
3105
    }
3106
    assert bindingTy <> Type::Unknown;
3107
3108
    // Alignment must be zero or a power of two.
3109
    if alignment <> 0 and (alignment & (alignment - 1)) <> 0 {
3110
        throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment));
3111
    }
3112
    let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0);
3113
    setNodeType(self, decl.value, bindingTy);
3114
3115
    return Type::Void;
3116
}
3117
3118
/// Check whether a node is an integer literal, optionally under unary negation.
3119
fn isIntegerLiteralExpr(node: *ast::Node) -> bool {
3120
    match node.value {
3121
        case ast::NodeValue::Number(_) => return true,
3122
        case ast::NodeValue::UnOp(unop) => {
3123
            if unop.op == ast::UnaryOp::Neg {
3124
                return isIntegerLiteralExpr(unop.value);
3125
            }
3126
            return false;
3127
        },
3128
        else => return false,
3129
    }
3130
}
3131
3132
/// Determine whether a node represents a compile-time constant expression.
3133
export fn isConstExpr(self: *Resolver, node: *ast::Node) -> bool {
3134
    match node.value {
3135
        case ast::NodeValue::Bool(_),
3136
             ast::NodeValue::Char(_),
3137
             ast::NodeValue::Number(_),
3138
             ast::NodeValue::String(_),
3139
             ast::NodeValue::Undef,
3140
             ast::NodeValue::Nil => {
3141
            return true;
3142
        },
3143
        case ast::NodeValue::ArrayLit(items) => {
3144
            for item in items {
3145
                if not isConstExpr(self, item) {
3146
                    return false;
3147
                }
3148
            }
3149
            return true;
3150
        },
3151
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
3152
            return isConstExpr(self, repeat.item);
3153
        },
3154
        case ast::NodeValue::AddressOf(addr) => {
3155
            let ty = typeFor(self, node) else {
3156
                return false;
3157
            };
3158
            if let case Type::Slice { .. } = ty {
3159
                return isConstExpr(self, addr.target);
3160
            }
3161
            return false;
3162
        },
3163
        case ast::NodeValue::RecordLit(lit) => {
3164
            // Record literals are constant if all field values are constant.
3165
            for field in lit.fields {
3166
                if let case ast::NodeValue::RecordLitField(fieldLit) = field.value {
3167
                    if not isConstExpr(self, fieldLit.value) {
3168
                        return false;
3169
                    }
3170
                }
3171
            }
3172
            return true;
3173
        },
3174
        case ast::NodeValue::Ident(_),
3175
             ast::NodeValue::ScopeAccess(_) => {
3176
            // Identifiers and scope accesses referencing constants, union
3177
            // variants, or function values are constant expressions.
3178
            if let sym = symbolFor(self, node) {
3179
                match sym.data {
3180
                    case SymbolData::Variant { .. },
3181
                         SymbolData::Constant { .. } => return true,
3182
                    case SymbolData::Value { type, .. } => {
3183
                        if let case Type::Fn(_) = type {
3184
                            return true;
3185
                        }
3186
                    }
3187
                    else => {}
3188
                }
3189
            }
3190
            return false;
3191
        },
3192
        case ast::NodeValue::Call(call) => {
3193
            // Constructor calls (union variants, unlabeled records) are constant
3194
            // if all payload args are themselves constant.
3195
            if let sym = symbolFor(self, call.callee) {
3196
                match sym.data {
3197
                    case SymbolData::Variant { .. } => {}
3198
                    case SymbolData::Type(NominalType::Record(recInfo)) => {
3199
                        if recInfo.labeled {
3200
                            return false;
3201
                        }
3202
                    },
3203
                    else => return false,
3204
                }
3205
                for arg in call.args {
3206
                    if not isConstExpr(self, arg) {
3207
                        return false;
3208
                    }
3209
                }
3210
                return true;
3211
            }
3212
            return false;
3213
        },
3214
        case ast::NodeValue::BinOp(binop) => {
3215
            // Binary expressions are constant if both operands are constant.
3216
            return isConstExpr(self, binop.left) and isConstExpr(self, binop.right);
3217
        },
3218
        case ast::NodeValue::UnOp(unop) => {
3219
            // Unary expressions are constant if the operand is constant.
3220
            return isConstExpr(self, unop.value);
3221
        },
3222
        case ast::NodeValue::As(expr) => {
3223
            // Cast expressions are constant if the source value is constant.
3224
            return isConstExpr(self, expr.value);
3225
        },
3226
        case ast::NodeValue::Unsafe(body) => {
3227
            return isConstExpr(self, body);
3228
        },
3229
        else => {
3230
            return false;
3231
        }
3232
    }
3233
}
3234
3235
/// Construct an integer constant descriptor.
3236
fn constInt(magnitude: u64, bits: u8, signed: bool, negative: bool) -> ConstValue {
3237
    return ConstValue::Int(ConstInt { magnitude, bits, signed, negative });
3238
}
3239
3240
/// Apply an integer cast to a constant value, including target-width
3241
/// truncation and signed interpretation.
3242
fn castConstInt(value: ConstInt, target: Type) -> ConstValue {
3243
    let raw = constIntToBits(value);
3244
    let range = integerRange(target)
3245
        else panic "castConstInt: expected integer type";
3246
3247
    match range {
3248
        case IntegerRange::Unsigned { bits, .. } =>
3249
            return ConstValue::Int(constIntFromBits(raw, bits, false)),
3250
        case IntegerRange::Signed { bits, .. } =>
3251
            return ConstValue::Int(constIntFromBits(raw, bits, true)),
3252
    }
3253
}
3254
3255
/// Return the constant `u32` value for a slice bound when known.
3256
fn constSliceIndex(self: *mut Resolver, node: *ast::Node) -> ?u32 {
3257
    let value = constValueEntry(self, node)
3258
        else return nil;
3259
    let case ConstValue::Int(int) = value
3260
        else return nil;
3261
    if int.negative {
3262
        return nil;
3263
    }
3264
    return int.magnitude as u32;
3265
}
3266
3267
/// Validates and extracts a non-negative integer constant from a compile-time expression.
3268
///
3269
/// This function ensures that a node represents a valid, non-negative integer constant
3270
/// that fits within a machine word. It is used for contexts requiring compile-time
3271
/// non-negative integers, such as array sizes and alignment specifications.
3272
///
3273
/// Returns the unsigned magnitude of the constant as `u32`.
3274
fn checkSizeInt(self: *mut Resolver, node: *ast::Node) -> u32
3275
    throws (ResolveError)
3276
{
3277
    // First traverse the node expect a numeric type.
3278
    let _ = try checkNumeric(self, node);
3279
3280
    // Look up the compile-time constant value associated with this node.
3281
    let value = constValueEntry(self, node)
3282
        else throw emitError(self, node, ErrorKind::ConstExprRequired);
3283
3284
    let case ConstValue::Int(int) = value
3285
        else panic "checkSizeInt: expected integer constant";
3286
3287
    // Validate it fits within u32 range.
3288
    if not validateConstIntRange(value, Type::U32) {
3289
        throw emitError(self, node, ErrorKind::NumericLiteralOverflow);
3290
    }
3291
    assert not int.negative;
3292
    setNodeType(self, node, Type::U32);
3293
3294
    return int.magnitude as u32;
3295
}
3296
3297
/// Check that constructor arguments match record fields.
3298
///
3299
/// Verifies argument count matches field count, and that each argument is
3300
/// assignable to its corresponding field type.
3301
fn checkRecordConstructorArgs(self: *mut Resolver, node: *ast::Node, args: *mut [*ast::Node], recInfo: RecordType)
3302
    throws (ResolveError)
3303
{
3304
    try checkRecordArity(self, args, recInfo, node);
3305
    for arg, i in args {
3306
        let fieldType = recInfo.fields[i].fieldType;
3307
        try checkAssignable(self, arg, fieldType);
3308
    }
3309
}
3310
3311
/// Check that the argument count of a constructor pattern or call matches the record field count.
3312
fn checkRecordArity(self: *mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) {
3313
    if args.len <> recInfo.fields.len {
3314
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch {
3315
            expected: recInfo.fields.len as u32,
3316
            actual: args.len,
3317
        }));
3318
    }
3319
}
3320
3321
/// Helper for analyzing `constant` and `static` declarations.
3322
fn resolveConstOrStatic(
3323
    self: *mut Resolver,
3324
    node: *ast::Node,
3325
    ident: *ast::Node,
3326
    typeNode: *ast::Node,
3327
    valueNode: *ast::Node,
3328
    attrList: ?ast::Attributes,
3329
    isConst: bool
3330
) -> Type throws (ResolveError) {
3331
    let attrs = resolveAttributes(self, attrList);
3332
    let bindingTy = try infer(self, typeNode);
3333
    try ensureStorableType(self, typeNode, bindingTy);
3334
    try ensureTypeResolved(self, bindingTy, typeNode);
3335
    let unsafeGlobal = ast::hasAttribute(attrs, ast::Attribute::Unsafe);
3336
    if isLinear(bindingTy) and not unsafeGlobal {
3337
        throw emitError(self, typeNode, ErrorKind::LinearDiscard);
3338
    }
3339
    let unsafeInitializer = unsafeGlobal;
3340
    if unsafeInitializer {
3341
        set self.unsafeDepth += 1;
3342
    }
3343
    let valueTy = try checkAssignable(self, valueNode, bindingTy) catch e {
3344
        if unsafeInitializer {
3345
            set self.unsafeDepth -= 1;
3346
        }
3347
        throw e;
3348
    };
3349
    if unsafeInitializer {
3350
        set self.unsafeDepth -= 1;
3351
    }
3352
3353
    if isConst {
3354
        let mut constVal = constValueEntry(self, valueNode);
3355
        if constVal == nil and not isConstExpr(self, valueNode) {
3356
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
3357
        }
3358
        if let val = constVal {
3359
            if let case ConstValue::Int(int) = val; isNumericType(bindingTy) {
3360
                set constVal = castConstInt(int, bindingTy);
3361
            }
3362
        }
3363
        try bindConstIdent(self, ident, node, bindingTy, constVal, attrs);
3364
    } else {
3365
        if not isConstExpr(self, valueNode) {
3366
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
3367
        }
3368
        try bindValueIdent(self, ident, node, bindingTy, true, 0, attrs);
3369
    }
3370
    setNodeType(self, valueNode, bindingTy);
3371
3372
    return Type::Void;
3373
}
3374
3375
/// Return whether a function type matches the compiler's ecall ABI.
3376
fn isCanonicalEcallType(info: *FnType) -> bool {
3377
    if not info.isUnsafe
3378
        or info.paramTypes.len <> 5
3379
        or info.throwList.len <> 0
3380
        or *info.returnType <> Type::I64
3381
    {
3382
        return false;
3383
    }
3384
    return *info.paramTypes[0] == Type::U32
3385
        and *info.paramTypes[1] == Type::I64
3386
        and *info.paramTypes[2] == Type::I64
3387
        and *info.paramTypes[3] == Type::I64
3388
        and *info.paramTypes[4] == Type::I64;
3389
}
3390
3391
/// Analyze a function declaration signature and bind the function name.
3392
fn resolveFnDecl(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) -> Type
3393
    throws (ResolveError)
3394
{
3395
    set self.nodeData.entries[node.id].trustedBody = self.unsafeDepth > 0;
3396
    let attrMask = resolveAttributes(self, decl.attrs);
3397
    let mut retTy = Type::Void;
3398
    if let retNode = decl.sig.returnType {
3399
        set retTy = try infer(self, retNode);
3400
        try ensureStorableType(self, retNode, retTy);
3401
    }
3402
    let a = alloc::arenaAllocator(&mut self.arena);
3403
    let mut paramTypes: *mut [*Type] = &mut [];
3404
    let mut throwList: *mut [*Type] = &mut [];
3405
    let mut fnType = FnType {
3406
        paramTypes: &[],
3407
        returnType: allocType(self, retTy),
3408
        throwList: &[],
3409
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3410
        localCount: 0,
3411
    };
3412
    // Enter the function scope to process parameters.
3413
    enterFn(self, node, &fnType);
3414
3415
    if decl.sig.params.len > MAX_FN_PARAMS {
3416
        exitFn(self);
3417
        throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
3418
            expected: MAX_FN_PARAMS,
3419
            actual: decl.sig.params.len,
3420
        }));
3421
    }
3422
    for paramNode in decl.sig.params {
3423
        let paramTy = try infer(self, paramNode) catch e {
3424
            exitFn(self);
3425
            throw e;
3426
        };
3427
        paramTypes.append(allocType(self, paramTy), a);
3428
    }
3429
3430
    if decl.sig.throwList.len > MAX_FN_THROWS {
3431
        exitFn(self);
3432
        throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
3433
            expected: MAX_FN_THROWS,
3434
            actual: decl.sig.throwList.len,
3435
        }));
3436
    }
3437
    for throwNode in decl.sig.throwList {
3438
        let throwTy = try infer(self, throwNode) catch e {
3439
            exitFn(self);
3440
            throw e;
3441
        };
3442
        throwList.append(allocType(self, throwTy), a);
3443
        try ensureStorableType(self, throwNode, throwTy);
3444
    }
3445
    exitFn(self);
3446
    set fnType.paramTypes = &paramTypes[..];
3447
    set fnType.throwList = &throwList[..];
3448
    if ast::hasAttribute(attrMask, ast::Attribute::Intrinsic) {
3449
        let name = try nodeName(self, decl.name);
3450
        if mem::eq(name, "ecall") and not isCanonicalEcallType(&fnType) {
3451
            throw emitError(
3452
                self,
3453
                node,
3454
                ErrorKind::InvalidEcallIntrinsicSignature,
3455
            );
3456
        }
3457
    }
3458
3459
    // Bind the function name.
3460
    let ty = Type::Fn(allocFnType(self, fnType));
3461
    let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask)
3462
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
3463
3464
    return ty;
3465
}
3466
3467
/// Analyze a function body.
3468
fn resolveFnDeclBody(self: *mut Resolver, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
3469
    let sym = symbolFor(self, node) else {
3470
        // The function declaration failed to type check, therefore
3471
        // no symbol was associated with it.
3472
        return;
3473
    };
3474
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
3475
        panic "resolveFnDeclBody: unexpected symbol data for function";
3476
    };
3477
    let retTy = *fnType.returnType;
3478
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3479
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3480
    let isUnsafe = fnType.isUnsafe;
3481
    let trustedBody = nodeData(self, node).trustedBody;
3482
3483
    if let body = decl.body {
3484
        if isIntrinsic {
3485
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
3486
        }
3487
        if isExtern {
3488
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
3489
        }
3490
        if isUnsafe or trustedBody {
3491
            set self.unsafeDepth += 1;
3492
        }
3493
        enterFn(self, node, fnType); // Enter function scope for body analysis.
3494
3495
        let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3496
            exitFn(self);
3497
            if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
3498
            throw e;
3499
        };
3500
        if retTy <> Type::Void and bodyTy <> Type::Never {
3501
            exitFn(self);
3502
            if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
3503
            throw emitError(self, body, ErrorKind::FnMissingReturn);
3504
        }
3505
        if self.unsafeDepth == 0 {
3506
            try checkLinearFn(self, nil, decl.sig.params, body) catch e {
3507
                exitFn(self);
3508
                throw e;
3509
            };
3510
        }
3511
        exitFn(self);
3512
        if isUnsafe or trustedBody {
3513
            set self.unsafeDepth -= 1;
3514
        }
3515
    } else if not isExtern {
3516
        throw emitError(self, node, ErrorKind::FnMissingBody);
3517
    }
3518
}
3519
3520
/// Analyze a function parameter and bind its identifier.
3521
fn resolveFnParam(self: *mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
3522
    throws (ResolveError)
3523
{
3524
    let ty = try resolveValueType(self, param.type);
3525
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3526
3527
    return ty;
3528
}
3529
3530
/// Resolve the compiler-known `Linear` marker from a derive list.
3531
fn resolveLinearDerive(self: *mut Resolver, derives: *mut [*ast::Node]) -> bool
3532
    throws (ResolveError)
3533
{
3534
    let mut linear = false;
3535
    for derive in derives {
3536
        let name = try nodeName(self, derive);
3537
        if mem::eq(name, "Linear") {
3538
            if linear {
3539
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3540
            }
3541
            set linear = true;
3542
        } else {
3543
            // Resolve an ordinary trait derive.
3544
            try infer(self, derive);
3545
        }
3546
    }
3547
    return linear;
3548
}
3549
3550
/// Resolve record fields from a node list.
3551
fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3552
    throws (ResolveError)
3553
{
3554
    let a = alloc::arenaAllocator(&mut self.arena);
3555
    let mut result: *mut [RecordField] = &mut [];
3556
    let mut currentOffset: u32 = 0;
3557
    let mut maxAlignment: u32 = 1;
3558
3559
    if fields.len > parser::MAX_RECORD_FIELDS {
3560
        throw emitError(self, node, ErrorKind::Internal);
3561
    }
3562
    // TODO: Add cycle detection to catch invalid recursive types like `record A { a: A }`.
3563
    for field in fields {
3564
        let case ast::NodeValue::RecordField {
3565
            field: fieldNode,
3566
            type: typeNode,
3567
            value: valueNode
3568
        } = field.value else panic "resolveRecordFields: invalid record field";
3569
        let fieldTy = try resolveValueType(self, typeNode);
3570
        try ensureStorableType(self, typeNode, fieldTy);
3571
3572
        if let v = valueNode {
3573
            let _valTy = try checkAssignable(self, v, fieldTy);
3574
        }
3575
        // Get field name for labeled records.
3576
        let mut fieldName: ?*[u8] = nil;
3577
        if labeled {
3578
            let n = fieldNode
3579
                else panic "resolveRecordFields: labeled record field missing name";
3580
            set fieldName = try nodeName(self, n);
3581
        }
3582
        let fieldType = typeFor(self, typeNode)
3583
            else throw emitError(self, typeNode, ErrorKind::CannotInferType);
3584
3585
        // Ensure field type is fully resolved before computing layout.
3586
        try ensureTypeResolved(self, fieldType, typeNode);
3587
3588
        // Compute field offset by aligning to field's alignment.
3589
        let fieldLayout = getTypeLayout(fieldType);
3590
        set currentOffset = mem::alignUp(currentOffset, fieldLayout.alignment);
3591
3592
        result.append(RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }, a);
3593
3594
        // Advance offset past this field.
3595
        set currentOffset += fieldLayout.size;
3596
3597
        // Track max alignment for record layout.
3598
        set maxAlignment = max(maxAlignment, fieldLayout.alignment);
3599
    }
3600
    // Compute cached layout.
3601
    let recordLayout = Layout {
3602
        size: mem::alignUp(currentOffset, maxAlignment),
3603
        alignment: maxAlignment
3604
    };
3605
    return RecordType {
3606
        fields: &result[..],
3607
        labeled,
3608
        layout: recordLayout,
3609
        declaredLinear: false,
3610
    };
3611
}
3612
3613
/// Resolve record field types for a named record declaration.
3614
fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3615
    throws (ResolveError)
3616
{
3617
    // Get the type symbol that was bound to this declaration node.
3618
    // If there's no symbol, it's because an earlier phase failed.
3619
    let sym = symbolFor(self, node)
3620
        else return;
3621
    let case SymbolData::Type(nominalTy) = sym.data
3622
        else panic "resolveRecordBody: unexpected type symbol data";
3623
3624
    // Skip if already resolved.
3625
    if let case NominalType::Record(_) = *nominalTy {
3626
        return;
3627
    }
3628
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
3629
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3630
    set recordType.declaredLinear = declaredLinear;
3631
3632
    set *nominalTy = NominalType::Record(recordType);
3633
}
3634
3635
/// Bind a type name.
3636
fn bindTypeName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3637
    throws (ResolveError)
3638
{
3639
    let attrMask = resolveAttributes(self, attrs);
3640
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3641
3642
    // Create a placeholder nominal type that will be replaced in
3643
    // the next phase.
3644
    let nominalTy = allocNominalType(self, NominalType::Placeholder(node));
3645
3646
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3647
}
3648
3649
/// Allocate a trait type descriptor and return a pointer to it.
3650
fn allocTraitType(self: *mut Resolver, name: *[u8]) -> *mut TraitType {
3651
    let p = try! alloc::alloc(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3652
    let entry = p as *mut TraitType;
3653
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
3654
3655
    return entry;
3656
}
3657
3658
/// Bind a trait name in the current scope.
3659
fn bindTraitName(self: *mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *mut Symbol
3660
    throws (ResolveError)
3661
{
3662
    let attrMask = resolveAttributes(self, attrs);
3663
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3664
3665
    let traitName = try nodeName(self, name);
3666
    let traitType = allocTraitType(self, traitName);
3667
    let data = SymbolData::Trait(traitType);
3668
    let sym = try bindIdent(self, traitName, node, data, attrMask, self.scope);
3669
3670
    setNodeType(self, node, Type::Void);
3671
    setNodeType(self, name, Type::Void);
3672
3673
    return sym;
3674
}
3675
3676
/// Find a trait method by name.
3677
export fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod {
3678
    for i in 0..traitType.methods.len {
3679
        if traitType.methods[i].name == name {
3680
            return &traitType.methods[i];
3681
        }
3682
    }
3683
    return nil;
3684
}
3685
3686
/// Resolve a trait declaration body: supertrait methods, then own methods.
3687
fn resolveTraitBody(self: *mut Resolver, node: *ast::Node, supertraits: *mut [*ast::Node], methods: *mut [*ast::Node])
3688
    throws (ResolveError)
3689
{
3690
    let sym = symbolFor(self, node)
3691
        else return;
3692
    let case SymbolData::Trait(traitType) = sym.data
3693
        else return;
3694
    if traitType.methods.len > 0 {
3695
        return;
3696
    }
3697
3698
    // Resolve supertrait bounds and copy their methods into this trait.
3699
    for superNode in supertraits {
3700
        let superSym = try resolveNamePath(self, superNode);
3701
        let case SymbolData::Trait(superTrait) = superSym.data
3702
            else throw emitError(self, superNode, ErrorKind::Internal);
3703
        // Trait bodies are otherwise resolved in source order. Recursively
3704
        // resolve a supertrait only when it is declared later.
3705
        if superSym.node.id > node.id {
3706
            let case ast::NodeValue::TraitDecl {
3707
                supertraits: inheritedTraits, methods: inheritedMethods, ..
3708
            } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal);
3709
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
3710
        }
3711
3712
        setNodeSymbol(self, superNode, superSym);
3713
3714
        let a = alloc::arenaAllocator(&mut self.arena);
3715
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
3716
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3717
                expected: ast::MAX_TRAIT_METHODS,
3718
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
3719
            }));
3720
        }
3721
        // Copy inherited methods into this trait's method table.
3722
        for inherited in superTrait.methods {
3723
            if let _ = findTraitMethod(traitType, inherited.name) {
3724
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
3725
            }
3726
            traitType.methods.append(TraitMethod {
3727
                name: inherited.name,
3728
                fnType: inherited.fnType,
3729
                mutable: inherited.mutable,
3730
                receiverClass: inherited.receiverClass,
3731
                index: traitType.methods.len as u32,
3732
            }, a);
3733
        }
3734
        traitType.supertraits.append(superTrait, a);
3735
    }
3736
3737
    if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS {
3738
        throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3739
            expected: ast::MAX_TRAIT_METHODS,
3740
            actual: traitType.methods.len as u32 + methods.len as u32,
3741
        }));
3742
    }
3743
3744
    for methodNode in methods {
3745
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value
3746
            else continue;
3747
        let methodName = try nodeName(self, name);
3748
        let attrMask = resolveAttributes(self, attrs);
3749
3750
        // Reject duplicate method names.
3751
        if let _ = findTraitMethod(traitType, methodName) {
3752
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3753
        }
3754
        // Determine the receiver class and mutability, and validate that it
3755
        // points to the declaring trait.
3756
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3757
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3758
        let case ast::TypeSig::Pointer {
3759
            class: receiverClass, valueType: receiverValueType, mutable,
3760
        } = typeSig
3761
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3762
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
3763
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3764
        let case ast::TypeSig::Nominal(nameNode) = innerSig
3765
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3766
        let receiverTargetName = try nodeName(self, nameNode);
3767
3768
        if receiverTargetName <> traitType.name {
3769
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3770
        }
3771
        // Resolve parameter types and return type.
3772
        let a = alloc::arenaAllocator(&mut self.arena);
3773
        let mut paramTypes: *mut [*Type] = &mut [];
3774
        let mut throwList: *mut [*Type] = &mut [];
3775
        let mut retType = allocType(self, Type::Void);
3776
3777
        if sig.params.len > MAX_FN_PARAMS {
3778
            throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch {
3779
                expected: MAX_FN_PARAMS,
3780
                actual: sig.params.len,
3781
            }));
3782
        }
3783
        for paramNode in sig.params {
3784
            let paramTy = try infer(self, paramNode);
3785
            paramTypes.append(allocType(self, paramTy), a);
3786
        }
3787
        if let ret = sig.returnType {
3788
            set retType = allocType(self, try infer(self, ret));
3789
        }
3790
        // Resolve throws list.
3791
        if sig.throwList.len > MAX_FN_THROWS {
3792
            throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch {
3793
                expected: MAX_FN_THROWS,
3794
                actual: sig.throwList.len,
3795
            }));
3796
        }
3797
        for throwNode in sig.throwList {
3798
            let throwTy = try infer(self, throwNode);
3799
            throwList.append(allocType(self, throwTy), a);
3800
        }
3801
        let fnType = FnType {
3802
            paramTypes: &paramTypes[..],
3803
            returnType: retType,
3804
            throwList: &throwList[..],
3805
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3806
            localCount: 0,
3807
        };
3808
        traitType.methods.append(TraitMethod {
3809
            name: methodName,
3810
            fnType: allocFnType(self, fnType),
3811
            mutable,
3812
            receiverClass,
3813
            index: traitType.methods.len as u32,
3814
        }, a);
3815
3816
        setNodeType(self, methodNode, Type::Void);
3817
    }
3818
}
3819
3820
/// Resolve a name path node to a symbol.
3821
/// Used for trait and type references in instance declarations and trait objects.
3822
fn resolveNamePath(self: *mut Resolver, node: *ast::Node) -> *mut Symbol
3823
    throws (ResolveError)
3824
{
3825
    match node.value {
3826
        case ast::NodeValue::Ident(name) => {
3827
            let sym = findAnySymbol(self.scope, name)
3828
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
3829
            return sym;
3830
        }
3831
        case ast::NodeValue::ScopeAccess(access) => {
3832
            return try resolveAccess(self, node, access, self.scope);
3833
        }
3834
        else => {
3835
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
3836
        }
3837
    }
3838
}
3839
3840
/// Resolve an instance declaration.
3841
/// Validates that the trait exists, the target type exists, and all methods
3842
/// match the trait's signatures.
3843
fn resolveInstanceDecl(
3844
    self: *mut Resolver,
3845
    node: *ast::Node,
3846
    traitName: *ast::Node,
3847
    targetType: *ast::Node,
3848
    methods: *mut [*ast::Node]
3849
) throws (ResolveError) {
3850
    // Look up the trait.
3851
    let traitSym = try resolveNamePath(self, traitName);
3852
    let case SymbolData::Trait(traitInfo) = traitSym.data
3853
        else throw emitError(self, traitName, ErrorKind::Internal);
3854
3855
    setNodeSymbol(self, traitName, traitSym);
3856
3857
    // Look up the target type.
3858
    let typeSym = try resolveNamePath(self, targetType);
3859
    let case SymbolData::Type(nominalTy) = typeSym.data
3860
        else throw emitError(self, targetType, ErrorKind::Internal);
3861
    setNodeSymbol(self, targetType, typeSym);
3862
    // Ensure the concrete type body is resolved.
3863
    try ensureNominalResolved(self, nominalTy, targetType);
3864
3865
    // Reject duplicate instance for the same (trait, type) pair.
3866
    let concreteType = Type::Nominal(nominalTy);
3867
    if let _ = findInstance(self, traitInfo, concreteType) {
3868
        throw emitError(self, node, ErrorKind::DuplicateInstance);
3869
    }
3870
3871
    // Build the instance entry.
3872
    if self.instancesLen >= MAX_INSTANCES {
3873
        throw emitError(self, node, ErrorKind::Internal);
3874
    }
3875
    let methodSlice = try! alloc::allocSlice(
3876
        &mut self.arena, @sizeOf(*mut Symbol), @alignOf(*mut Symbol), traitInfo.methods.len as u32
3877
    ) as *mut [*mut Symbol];
3878
    let mut entry = InstanceEntry {
3879
        traitType: traitInfo,
3880
        concreteType,
3881
        concreteTypeName: typeSym.name,
3882
        moduleId: self.currentMod,
3883
        methods: methodSlice,
3884
    };
3885
    // Track which trait methods are covered by the instance.
3886
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
3887
3888
    // Match each instance method to a trait method.
3889
    for methodNode in methods {
3890
        let case ast::NodeValue::MethodDecl {
3891
            name, receiverName, receiverType, sig, body, attrs,
3892
        } = methodNode.value else continue;
3893
        set self.nodeData.entries[methodNode.id].trustedBody = self.unsafeDepth > 0;
3894
3895
        let methodName = try nodeName(self, name);
3896
        let attrMask = resolveAttributes(self, attrs);
3897
3898
        // Find the matching trait method.
3899
        let tm = findTraitMethod(traitInfo, methodName)
3900
            else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
3901
        let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
3902
        if instanceUnsafe <> tm.fnType.isUnsafe {
3903
            throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
3904
        }
3905
3906
        // Determine receiver mutability and validate receiver type.
3907
        // The receiver must be `*Type` or `*mut Type`.
3908
        let case ast::NodeValue::TypeSig(typeSig) = receiverType.value
3909
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3910
        let case ast::TypeSig::Pointer {
3911
            class: receiverClass, valueType, mutable: receiverMut,
3912
        } = typeSig
3913
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3914
        if receiverClass <> tm.receiverClass {
3915
            throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3916
        }
3917
3918
        // Validate that the receiver type annotation matches the
3919
        // concrete type from the instance declaration.
3920
        let annotatedTy = try infer(self, valueType);
3921
        if not typesEqual(annotatedTy, concreteType) {
3922
            throw emitTypeMismatch(self, receiverType, TypeMismatch {
3923
                expected: concreteType,
3924
                actual: annotatedTy,
3925
            });
3926
        }
3927
3928
        // Check receiver mutability matches in both directions.
3929
        if tm.mutable and not receiverMut {
3930
            throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
3931
        }
3932
        if receiverMut and not tm.mutable {
3933
            throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
3934
        }
3935
3936
        // Build the function type for the instance method.
3937
        // The receiver becomes the first parameter.
3938
        let receiverPtrType = Type::Pointer {
3939
            class: receiverClass,
3940
            target: allocType(self, concreteType),
3941
            mutable: receiverMut,
3942
        };
3943
3944
        // Validate that the instance method's signature matches the
3945
        // trait method's signature exactly (params, return type, throws).
3946
        if sig.params.len <> tm.fnType.paramTypes.len {
3947
            throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
3948
                expected: tm.fnType.paramTypes.len as u32,
3949
                actual: sig.params.len,
3950
            }));
3951
        }
3952
        for paramNode, j in sig.params {
3953
            let case ast::NodeValue::FnParam(param) = paramNode.value
3954
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
3955
            let instanceParamTy = try resolveValueType(self, param.type);
3956
            if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) {
3957
                throw emitTypeMismatch(self, paramNode, TypeMismatch {
3958
                    expected: *tm.fnType.paramTypes[j],
3959
                    actual: instanceParamTy,
3960
                });
3961
            }
3962
        }
3963
        let mut instanceRetTy = Type::Void;
3964
        if let retNode = sig.returnType {
3965
            set instanceRetTy = try resolveValueType(self, retNode);
3966
        }
3967
        if not typesEqual(instanceRetTy, *tm.fnType.returnType) {
3968
            throw emitTypeMismatch(self, methodNode, TypeMismatch {
3969
                expected: *tm.fnType.returnType,
3970
                actual: instanceRetTy,
3971
            });
3972
        }
3973
        if sig.throwList.len <> tm.fnType.throwList.len {
3974
            throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
3975
                expected: tm.fnType.throwList.len as u32,
3976
                actual: sig.throwList.len,
3977
            }));
3978
        }
3979
        for throwNode, j in sig.throwList {
3980
            let instanceThrowTy = try resolveValueType(self, throwNode);
3981
            if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) {
3982
                throw emitTypeMismatch(self, throwNode, TypeMismatch {
3983
                    expected: *tm.fnType.throwList[j],
3984
                    actual: instanceThrowTy,
3985
                });
3986
            }
3987
        }
3988
3989
        // Build final function type: receiver plus trait's canonical types.
3990
        let a = alloc::arenaAllocator(&mut self.arena);
3991
        // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`?
3992
        let mut paramTypes: *mut [*Type] = &mut [];
3993
        paramTypes.append(allocType(self, receiverPtrType), a);
3994
3995
        for ty in tm.fnType.paramTypes {
3996
            paramTypes.append(ty, a);
3997
        }
3998
        let fnType = FnType {
3999
            paramTypes: &paramTypes[..],
4000
            returnType: tm.fnType.returnType,
4001
            throwList: tm.fnType.throwList,
4002
            isUnsafe: tm.fnType.isUnsafe,
4003
            localCount: 0,
4004
        };
4005
4006
        // Create a symbol for the instance method without binding it into the
4007
        // module scope. Instance methods are dispatched via v-table, so they
4008
        // must not pollute the enclosing scope.
4009
        let fnTy = Type::Fn(allocFnType(self, fnType));
4010
        let mName = try nodeName(self, name);
4011
        let sym = allocSymbol(self, SymbolData::Value {
4012
            mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4013
        }, mName, methodNode, attrMask);
4014
4015
        setNodeSymbol(self, methodNode, sym);
4016
        setNodeType(self, methodNode, fnTy);
4017
        setNodeType(self, name, fnTy);
4018
4019
        // Store in instance entry at the matching v-table slot.
4020
        set entry.methods[tm.index] = sym;
4021
        set covered[tm.index] = true;
4022
    }
4023
4024
    // Fill inherited method slots from supertrait instances.
4025
    for superTrait in traitInfo.supertraits {
4026
        let superInst = findInstance(self, superTrait, concreteType)
4027
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
4028
        for superMethod, mi in superTrait.methods {
4029
            let merged = findTraitMethod(traitInfo, superMethod.name)
4030
                else panic "resolveInstanceDecl: inherited method not found";
4031
            if not covered[merged.index] {
4032
                set entry.methods[merged.index] = superInst.methods[mi];
4033
                set covered[merged.index] = true;
4034
            }
4035
        }
4036
    }
4037
4038
    // Check that all trait methods are implemented.
4039
    for method, i in traitInfo.methods {
4040
        if not covered[i] {
4041
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
4042
        }
4043
    }
4044
    set self.instances[self.instancesLen] = entry;
4045
    set self.instancesLen += 1;
4046
4047
    setNodeType(self, node, Type::Void);
4048
}
4049
4050
/// Resolve instance method bodies.
4051
fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *mut [*ast::Node])
4052
    throws (ResolveError)
4053
{
4054
    for methodNode in methods {
4055
        let case ast::NodeValue::MethodDecl {
4056
            name, receiverName, receiverType, sig, body, ..
4057
        } = methodNode.value else continue;
4058
4059
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
4060
        // for this method (eg. unknown method name). Skip gracefully.
4061
        let sym = symbolFor(self, methodNode)
4062
            else continue;
4063
4064
        try resolveMethodBody(self, methodNode, receiverName, sig, body);
4065
    }
4066
}
4067
4068
/// Resolve a method body shared by instance methods and standalone methods.
4069
/// Binds the receiver and parameters, then type-checks the body.
4070
fn resolveMethodBody(
4071
    self: *mut Resolver,
4072
    node: *ast::Node,
4073
    receiverName: *ast::Node,
4074
    sig: ast::FnSig,
4075
    body: *ast::Node,
4076
) throws (ResolveError) {
4077
    let sym = symbolFor(self, node)
4078
        else throw emitError(self, node, ErrorKind::Internal);
4079
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
4080
        else panic "resolveMethodBody: expected value symbol";
4081
    let isUnsafe = fnType.isUnsafe;
4082
    let trustedBody = nodeData(self, node).trustedBody;
4083
    if isUnsafe or trustedBody {
4084
        set self.unsafeDepth += 1;
4085
    }
4086
4087
    // Enter function scope.
4088
    enterFn(self, node, fnType);
4089
4090
    // Bind the receiver parameter.
4091
    let receiverTy = *fnType.paramTypes[0];
4092
    try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch e {
4093
        exitFn(self);
4094
        if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
4095
        throw e;
4096
    };
4097
    // Bind the remaining parameters from the signature.
4098
    for paramNode in sig.params {
4099
        let paramTy = try infer(self, paramNode) catch e {
4100
            exitFn(self);
4101
            if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
4102
            throw e;
4103
        };
4104
    }
4105
4106
    // Resolve the body.
4107
    let retTy = *fnType.returnType;
4108
    let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
4109
        exitFn(self);
4110
        if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
4111
        throw e;
4112
    };
4113
    if retTy <> Type::Void and bodyTy <> Type::Never {
4114
        exitFn(self);
4115
        if isUnsafe or trustedBody { set self.unsafeDepth -= 1; }
4116
        throw emitError(self, body, ErrorKind::FnMissingReturn);
4117
    }
4118
    if self.unsafeDepth == 0 {
4119
        try checkLinearFn(self, receiverName, sig.params, body) catch e {
4120
            exitFn(self);
4121
            throw e;
4122
        };
4123
    }
4124
    exitFn(self);
4125
    if isUnsafe or trustedBody {
4126
        set self.unsafeDepth -= 1;
4127
    }
4128
}
4129
4130
/// Resolve a standalone method declaration (signature only).
4131
/// Validates the receiver type and registers the method in the method table.
4132
4133
/// Extract the type name from a resolved receiver type node.
4134
fn receiverTypeName(
4135
    self: *mut Resolver,
4136
    receiverType: *ast::Node,
4137
) -> *[u8] throws (ResolveError) {
4138
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4139
        receiverType.value
4140
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4141
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
4142
        else throw emitError(self, receiverType, ErrorKind::Internal);
4143
    let sym = symbolFor(self, nameNode)
4144
        else throw emitError(self, receiverType, ErrorKind::Internal);
4145
4146
    return sym.name;
4147
}
4148
4149
/// Resolve and register a standalone method declaration.
4150
fn resolveMethodDecl(
4151
    self: *mut Resolver,
4152
    node: *ast::Node,
4153
    name: *ast::Node,
4154
    receiverName: *ast::Node,
4155
    receiverType: *ast::Node,
4156
    sig: ast::FnSig,
4157
    attrs: ?ast::Attributes,
4158
) throws (ResolveError) {
4159
    set self.nodeData.entries[node.id].trustedBody = self.unsafeDepth > 0;
4160
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
4161
    // nominal type.
4162
    let fullReceiverTy = try infer(self, receiverType);
4163
    let case Type::Pointer {
4164
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
4165
    } = fullReceiverTy
4166
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4167
    let concreteType = *receiverTarget;
4168
    let case Type::Nominal(nominalTy) = concreteType
4169
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
4170
    try ensureNominalResolved(self, nominalTy, receiverType);
4171
4172
    // Get the type name from the inner type node's symbol.
4173
    let typeName = try receiverTypeName(self, receiverType);
4174
    let methodName = try nodeName(self, name);
4175
    let attrMask = resolveAttributes(self, attrs);
4176
4177
    // Reject duplicate method for the same (type, name).
4178
    if let _ = findMethod(self, concreteType, methodName) {
4179
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
4180
    }
4181
4182
    // Resolve parameter types.
4183
    let a = alloc::arenaAllocator(&mut self.arena);
4184
    let mut paramTypes: *mut [*Type] = &mut [];
4185
4186
    // Receiver is the first parameter.
4187
    let receiverPtrType = Type::Pointer {
4188
        class: receiverClass,
4189
        target: allocType(self, concreteType),
4190
        mutable: receiverMut,
4191
    };
4192
    paramTypes.append(allocType(self, receiverPtrType), a);
4193
4194
    for paramNode in sig.params {
4195
        let case ast::NodeValue::FnParam(param) = paramNode.value
4196
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4197
        let paramTy = try resolveValueType(self, param.type);
4198
        paramTypes.append(allocType(self, paramTy), a);
4199
    }
4200
4201
    // Resolve return type.
4202
    let mut returnType = Type::Void;
4203
    if let retNode = sig.returnType {
4204
        set returnType = try resolveValueType(self, retNode);
4205
    }
4206
4207
    // Resolve throw list.
4208
    let mut throwTypes: *mut [*Type] = &mut [];
4209
    for throwNode in sig.throwList {
4210
        let throwTy = try resolveValueType(self, throwNode);
4211
        throwTypes.append(allocType(self, throwTy), a);
4212
    }
4213
4214
    let retTypePtr = allocType(self, returnType);
4215
    let throwList = &throwTypes[..];
4216
4217
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
4218
    // Full function type (receiver + params) for lowering.
4219
    let fullFnType = FnType {
4220
        paramTypes: &paramTypes[..],
4221
        returnType: retTypePtr,
4222
        throwList,
4223
        isUnsafe,
4224
        localCount: 0,
4225
    };
4226
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
4227
4228
    // Function type excluding receiver, for call arg checking.
4229
    let checkFnType = FnType {
4230
        paramTypes: &paramTypes[1..],
4231
        returnType: retTypePtr,
4232
        throwList,
4233
        isUnsafe,
4234
        localCount: 0,
4235
    };
4236
4237
    // Create a symbol for the method without binding it into the module scope.
4238
    let sym = allocSymbol(self, SymbolData::Value {
4239
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4240
    }, methodName, node, attrMask);
4241
4242
    setNodeSymbol(self, node, sym);
4243
    setNodeType(self, node, fnTy);
4244
    setNodeType(self, name, fnTy);
4245
4246
    // Register in the method table.
4247
    if self.methodsLen >= MAX_METHODS {
4248
        throw emitError(self, node, ErrorKind::Internal);
4249
    }
4250
    set self.methods[self.methodsLen] = MethodEntry {
4251
        concreteType,
4252
        concreteTypeName: typeName,
4253
        name: methodName,
4254
        fnType: allocFnType(self, checkFnType),
4255
        mutable: receiverMut,
4256
        receiverClass,
4257
        symbol: sym,
4258
    };
4259
    set self.methodsLen += 1;
4260
}
4261
4262
/// Look up an instance entry by trait and concrete type.
4263
fn findInstance(self: *Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*InstanceEntry {
4264
    for i in 0..self.instancesLen {
4265
        let entry = &self.instances[i];
4266
        if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) {
4267
            return entry;
4268
        }
4269
    }
4270
    return nil;
4271
}
4272
4273
/// Look up a standalone method by concrete type and name.
4274
export fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry {
4275
    for i in 0..self.methodsLen {
4276
        let entry = &self.methods[i];
4277
        if typesEqual(entry.concreteType, concreteType) and entry.name == name {
4278
            return entry;
4279
        }
4280
    }
4281
    return nil;
4282
}
4283
4284
/// Look up a standalone method entry by its symbol.
4285
export fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry {
4286
    for i in 0..self.methodsLen {
4287
        let entry = &self.methods[i];
4288
        if entry.symbol == sym {
4289
            return entry;
4290
        }
4291
    }
4292
    return nil;
4293
}
4294
4295
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
4296
fn resolveUnionBody(self: *mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
4297
    throws (ResolveError)
4298
{
4299
    // Get the type symbol that was bound to this declaration node.
4300
    // If there's no symbol, it's because an earlier phase failed.
4301
    let sym = symbolFor(self, node)
4302
        else return;
4303
    let case SymbolData::Type(nominalTy) = sym.data
4304
        else panic "resolveUnionBody: unexpected symbol data";
4305
4306
    // Check if already resolved, in which case there's no need to
4307
    // do it again.
4308
    if let case NominalType::Union(_) = *nominalTy {
4309
        return;
4310
    }
4311
    let a = alloc::arenaAllocator(&mut self.arena);
4312
    let mut variants: *mut [UnionVariant] = &mut [];
4313
4314
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
4315
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
4316
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4317
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
4318
    set *nominalTy = NominalType::Union(UnionType {
4319
        variants: &[],
4320
        layout: Layout { size: 0, alignment: 0 },
4321
        valOffset: 0,
4322
        isAllVoid: true,
4323
        declaredLinear,
4324
    });
4325
4326
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
4327
    let mut iota: u32 = 0;
4328
    for variantNode, i in decl.variants {
4329
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
4330
            else panic "resolveUnionBody: invalid union variant";
4331
        let variantName = try nodeName(self, variantDecl.name);
4332
        // Resolve the variant's payload type if present.
4333
        let mut variantType = Type::Void;
4334
        if let typeNode = variantDecl.type {
4335
            set variantType = try infer(self, typeNode);
4336
            try ensureStorableType(self, typeNode, variantType);
4337
        }
4338
        // Process the variant's explicit discriminant value if present.
4339
        try visitOptional(self, variantDecl.value, variantType);
4340
        let tag = variantTag(variantDecl, &mut iota);
4341
        // Create a symbol for this variant.
4342
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
4343
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
4344
4345
        variants.append(UnionVariant {
4346
            name: variantName,
4347
            valueType: variantType,
4348
            symbol: variantSym,
4349
        }, a);
4350
    }
4351
    let info = computeUnionLayout(&variants[..]);
4352
4353
    // Update the nominal type with the resolved variants.
4354
    set *nominalTy = NominalType::Union(UnionType {
4355
        variants: &variants[..],
4356
        layout: info.layout,
4357
        valOffset: info.valOffset,
4358
        isAllVoid: info.isAllVoid,
4359
        declaredLinear,
4360
    });
4361
}
4362
4363
/// Check if a module should be analyzed based on its attributes and build configuration.
4364
fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool {
4365
    if let attributes = attrs {
4366
        // Skip test modules unless we're building in test mode.
4367
        if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest {
4368
            return false;
4369
        }
4370
    }
4371
    return true;
4372
}
4373
4374
/// Analyze a module during the graph analysis phase.
4375
fn resolveModGraph(self: *mut Resolver, node: *ast::Node, decl: ast::Mod)
4376
    throws (ResolveError)
4377
{
4378
    if not shouldAnalyzeModule(self, decl.attrs) {
4379
        return;
4380
    }
4381
    let modName = try nodeName(self, decl.name);
4382
    let attrMask = resolveAttributes(self, decl.attrs);
4383
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
4384
    let submod = try enterSubModule(self, modName, node);
4385
4386
    // Bind the module symbol in the outer scope, ie. where the `mod` statement is.
4387
    try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope);
4388
    let case ast::NodeValue::Block(block) = submod.root.value
4389
        else panic "resolveModGraph: expected block for module root";
4390
    try resolveModuleGraph(self, &block);
4391
4392
    exitModuleScope(self, submod);
4393
}
4394
4395
/// Analyze a module in the declaration phase.
4396
fn resolveModDecl(self: *mut Resolver, node: *ast::Node, decl: ast::Mod)
4397
    throws (ResolveError)
4398
{
4399
    if not shouldAnalyzeModule(self, decl.attrs) {
4400
        return;
4401
    }
4402
    // Find module under the current module.
4403
    let modName = try nodeName(self, decl.name);
4404
    let submod = try enterSubModule(self, modName, node);
4405
    let case ast::NodeValue::Block(block) = submod.root.value
4406
        else panic "resolveModDecl: expected block for module root";
4407
    let mut isUnsafe = false;
4408
    if let attrs = decl.attrs {
4409
        set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe);
4410
    }
4411
    if isUnsafe {
4412
        set self.unsafeDepth += 1;
4413
    }
4414
    try resolveModuleDecls(self, &block) catch e {
4415
        if isUnsafe { set self.unsafeDepth -= 1; }
4416
        exitModuleScope(self, submod);
4417
        throw e;
4418
    };
4419
    if isUnsafe {
4420
        set self.unsafeDepth -= 1;
4421
    }
4422
    exitModuleScope(self, submod);
4423
}
4424
4425
/// Analyze a `use` statement and create a symbol for the imported module.
4426
fn resolveUse(self: *mut Resolver, node: *ast::Node, decl: ast::Use) -> Type
4427
    throws (ResolveError)
4428
{
4429
    let resolved = try resolveModulePath(self, decl.path);
4430
    let attrMask = resolveAttributes(self, decl.attrs);
4431
4432
    if decl.wildcard {
4433
        // Import all public symbols from the target module.
4434
        for i in 0..resolved.scope.symbolsLen {
4435
            let sym = resolved.scope.symbols[i];
4436
            if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
4437
                if let existing = findSymbolInScope(self.scope, sym.name) {
4438
                    if existing == sym {
4439
                        continue;
4440
                    }
4441
                }
4442
                try addSymbolToScope(self, sym, self.scope, node);
4443
            }
4444
        }
4445
    } else {
4446
        // Regular module import.
4447
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, self.scope);
4448
    }
4449
    return Type::Void;
4450
}
4451
4452
/// Analyze a standard `if` statement.
4453
fn resolveIf(self: *mut Resolver, node: *ast::Node, cond: ast::If) -> Type
4454
    throws (ResolveError)
4455
{
4456
    try checkBoolean(self, cond.condition);
4457
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4458
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4459
4460
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4461
}
4462
4463
/// Analyze a conditional expression.
4464
fn resolveCondExpr(self: *mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type
4465
    throws (ResolveError)
4466
{
4467
    try checkBoolean(self, cond.condition);
4468
    let thenTy = try infer(self, cond.thenExpr);
4469
    let elseTy = try infer(self, cond.elseExpr);
4470
4471
    // Either branch may supply the concrete type for an otherwise context-
4472
    // dependent expression, such as an unsuffixed integer or `nil`.
4473
    if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) {
4474
        setNodeCoercion(self, cond.elseExpr, coercion);
4475
        return setNodeType(self, node, thenTy);
4476
    }
4477
    if let coercion = isAssignable(self, elseTy, thenTy, cond.thenExpr) {
4478
        setNodeCoercion(self, cond.thenExpr, coercion);
4479
        return setNodeType(self, node, elseTy);
4480
    }
4481
    try expectAssignable(self, thenTy, elseTy, cond.elseExpr);
4482
4483
    return setNodeType(self, node, thenTy);
4484
}
4485
4486
/// Analyze a pattern match structure (used by if-let, while-let).
4487
fn resolvePatternMatch(self: *mut Resolver, node: *ast::Node, pat: *ast::PatternMatch)
4488
    throws (ResolveError)
4489
{
4490
    match pat.kind {
4491
        case ast::PatternKind::Case => {
4492
            // Analyze pattern against scrutinee type.
4493
            let scrutineeTy = try infer(self, pat.scrutinee);
4494
            let subject = unwrapMatchSubject(scrutineeTy);
4495
            try resolveCasePattern(self, pat.pattern, subject.effectiveTy, IdentMode::Compare, subject.by);
4496
        }
4497
        case ast::PatternKind::Binding => {
4498
            // Scrutinee must be optional, bind the payload.
4499
            let scrutineeTy = try checkOptional(self, pat.scrutinee);
4500
            let payloadTy = *scrutineeTy;
4501
4502
            try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
4503
            setNodeType(self, pat.pattern, payloadTy);
4504
        }
4505
    }
4506
    if let guard = pat.guard {
4507
        try checkBoolean(self, guard);
4508
    }
4509
}
4510
4511
/// Analyze an `if let` or `if let case` pattern binding.
4512
fn resolveIfLet(self: *mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type
4513
    throws (ResolveError)
4514
{
4515
    enterScope(self, node);
4516
    try resolvePatternMatch(self, node, &cond.pattern);
4517
4518
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4519
    exitScope(self);
4520
4521
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4522
4523
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4524
}
4525
4526
/// Controls how bare identifiers are handled in case patterns.
4527
union IdentMode {
4528
    /// Identifier is a value to compare against.
4529
    Compare,
4530
    /// Identifier introduces a new binding.
4531
    Bind,
4532
}
4533
4534
/// Check whether a pattern node is a destructuring pattern that looks
4535
/// through structure (union variant, record literal, scope access).
4536
/// Identifiers, placeholders, and plain literals are not destructuring.
4537
export fn isDestructuringPattern(pattern: *ast::Node) -> bool {
4538
    match pattern.value {
4539
        case ast::NodeValue::Call(_),
4540
             ast::NodeValue::RecordLit(_),
4541
             ast::NodeValue::ScopeAccess(_) => return true,
4542
        else => return false,
4543
    }
4544
}
4545
4546
/// Analyze a case pattern for match, if-case, let-case, or while-case.
4547
///
4548
/// At the top level, bare identifiers are compared against existing values.
4549
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
4550
fn resolveCasePattern(
4551
    self: *mut Resolver,
4552
    pattern: *ast::Node,
4553
    scrutineeTy: Type,
4554
    mode: IdentMode,
4555
    matchBy: MatchBy
4556
) throws (ResolveError) {
4557
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
4558
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
4559
        return;
4560
    }
4561
    // TODO: Collapse these nested matches.
4562
    match scrutineeTy {
4563
        case Type::Nominal(info) => {
4564
            try ensureNominalResolved(self, info, pattern);
4565
4566
            match *info {
4567
                case NominalType::Union(unionType) => {
4568
                    try resolveUnionPattern(self, pattern, scrutineeTy, unionType, matchBy);
4569
                    return;
4570
                }
4571
                case NominalType::Record(recInfo) => {
4572
                    match pattern.value {
4573
                        case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => {
4574
                            try bindRecordPatternFields(self, pattern, recInfo, matchBy);
4575
                            return;
4576
                        } else => {}
4577
                    }
4578
                } else => {}
4579
            }
4580
        }
4581
        case Type::Array(arrayInfo) => {
4582
            if let case ast::NodeValue::ArrayLit(items) = pattern.value {
4583
                if items.len as u32 <> arrayInfo.length {
4584
                    throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(
4585
                        CountMismatch { expected: arrayInfo.length, actual: items.len as u32 }
4586
                    ));
4587
                }
4588
                let elemTy = *arrayInfo.item;
4589
                for item in items {
4590
                    try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy);
4591
                }
4592
                setNodeType(self, pattern, scrutineeTy);
4593
                return;
4594
            }
4595
        } else => {}
4596
    }
4597
    // Handle non-binding patterns (literals, placeholders) and bindings.
4598
    match pattern.value {
4599
        case ast::NodeValue::Placeholder => {
4600
            // Placeholder matches without introducing bindings.
4601
        }
4602
        case ast::NodeValue::Ident(_) => {
4603
            match mode {
4604
                case IdentMode::Bind => try bindPatternVar(self, pattern, scrutineeTy, matchBy),
4605
                case IdentMode::Compare => try checkAssignable(self, pattern, scrutineeTy),
4606
            }
4607
        }
4608
        else => {
4609
            // Literals and other expressions: check type compatibility.
4610
            try checkAssignable(self, pattern, scrutineeTy);
4611
        }
4612
    }
4613
}
4614
4615
/// Analyze a traditional `while` loop.
4616
fn resolveWhile(self: *mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type
4617
    throws (ResolveError)
4618
{
4619
    try checkBoolean(self, loopNode.condition);
4620
    try visitLoop(self, loopNode.body);
4621
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4622
4623
    return setNodeType(self, node, Type::Void);
4624
}
4625
4626
/// Analyze a `while let` loop with pattern binding.
4627
fn resolveWhileLet(self: *mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type
4628
    throws (ResolveError)
4629
{
4630
    enterScope(self, node);
4631
    try resolvePatternMatch(self, node, &loopNode.pattern);
4632
4633
    try visitLoop(self, loopNode.body);
4634
    exitScope(self);
4635
4636
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4637
4638
    return setNodeType(self, node, Type::Void);
4639
}
4640
4641
/// Analyze a `for` loop, binding iteration variables.
4642
fn resolveFor(self: *mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type
4643
    throws (ResolveError)
4644
{
4645
    let iterableTy = try infer(self, forStmt.iterable);
4646
4647
    // Extract binding names for the lowerer.
4648
    let mut bindingName: ?*[u8] = nil;
4649
    if let case ast::NodeValue::Ident(name) = forStmt.binding.value {
4650
        set bindingName = name;
4651
    }
4652
    let mut indexName: ?*[u8] = nil;
4653
    if let idx = forStmt.index {
4654
        if let case ast::NodeValue::Ident(name) = idx.value {
4655
            set indexName = name;
4656
        }
4657
    }
4658
    // Extract item type and store pre-computed loop metadata for the lowerer.
4659
    let mut itemTy: Type = undefined;
4660
    match iterableTy {
4661
        case Type::Slice { item, .. } => {
4662
            set itemTy = *item;
4663
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4664
                elemType: item, length: nil, bindingName, indexName
4665
            });
4666
        }
4667
        case Type::Range { start, .. } => {
4668
            // Iterable ranges must have a start, and since we enforce type
4669
            // equality for start and end, that is always the item type.
4670
            let valType = start else {
4671
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4672
            };
4673
            let case ast::NodeValue::Range(range) = forStmt.iterable.value else {
4674
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4675
            };
4676
            set itemTy = *valType;
4677
4678
            setForLoopInfo(self, node, ForLoopInfo::Range {
4679
                valType, range, bindingName, indexName
4680
            });
4681
        }
4682
        case Type::Array(arrayInfo) => {
4683
            set itemTy = *arrayInfo.item;
4684
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4685
                elemType: arrayInfo.item,
4686
                length: arrayInfo.length,
4687
                bindingName,
4688
                indexName,
4689
            });
4690
        }
4691
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
4692
    }
4693
    enterScope(self, node);
4694
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
4695
4696
    if let pat = forStmt.index {
4697
        try bindForLoopPattern(self, pat, Type::U32, false);
4698
    }
4699
    // The lowerer always creates at least one internal variable for iteration,
4700
    // even when the binding is a placeholder or no explicit index is given.
4701
    if let mut fnType = self.currentFn {
4702
        set fnType.localCount += 1;
4703
    }
4704
    try visitLoop(self, forStmt.body);
4705
    exitScope(self);
4706
4707
    try visitOptional(self, forStmt.elseBranch, Type::Void);
4708
4709
    return setNodeType(self, node, Type::Void);
4710
}
4711
4712
/// Get the node within a pattern that carries the `UnionVariant` extra.
4713
/// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the
4714
/// type name, and for `Call` it is the callee.
4715
export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node {
4716
    match pattern.value {
4717
        case ast::NodeValue::ScopeAccess(_) => return pattern,
4718
        case ast::NodeValue::RecordLit(lit) => return lit.typeName,
4719
        case ast::NodeValue::Call(call) => return call.callee,
4720
        else => return nil,
4721
    }
4722
}
4723
4724
/// Get the i-th sub-pattern element from a compound pattern.
4725
/// For `RecordLit` this is the i-th field's value; for `Call` it is the
4726
/// i-th argument.
4727
fn patternSubElement(pattern: *ast::Node, idx: u32) -> ?*ast::Node {
4728
    match pattern.value {
4729
        case ast::NodeValue::RecordLit(lit) => {
4730
            if idx < lit.fields.len as u32 {
4731
                if let case ast::NodeValue::RecordLitField(field) = lit.fields[idx].value {
4732
                    return field.value;
4733
                }
4734
            }
4735
        }
4736
        case ast::NodeValue::Call(call) => {
4737
            if idx < call.args.len as u32 {
4738
                return call.args[idx];
4739
            }
4740
        }
4741
        else => {}
4742
    }
4743
    return nil;
4744
}
4745
4746
/// Get the number of sub-pattern elements in a compound pattern.
4747
fn patternSubCount(pattern: *ast::Node) -> u32 {
4748
    match pattern.value {
4749
        case ast::NodeValue::RecordLit(lit) => return lit.fields.len as u32,
4750
        case ast::NodeValue::Call(call) => return call.args.len as u32,
4751
        else => return 0,
4752
    }
4753
}
4754
4755
/// Check whether a pattern contains nested sub-patterns that further
4756
/// refine the match beyond the outer variant (e.g. nested union variant
4757
/// tests or literal comparisons). Used to allow the same outer variant
4758
/// to appear in multiple match arms.
4759
fn hasNestedRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool {
4760
    for i in 0..patternSubCount(pattern) {
4761
        if let sub = patternSubElement(pattern, i) {
4762
            if isRefiningPattern(self, sub) {
4763
                return true;
4764
            }
4765
        }
4766
    }
4767
    return false;
4768
}
4769
4770
/// Check whether a single pattern node is a refining pattern that tests
4771
/// a value rather than just binding it. Union variants, literals, and
4772
/// scope accesses are refining; identifiers, placeholders, and plain
4773
/// record destructurings are not.
4774
fn isRefiningPattern(self: *Resolver, pattern: *ast::Node) -> bool {
4775
    match pattern.value {
4776
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
4777
            return false,
4778
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
4779
            if let keyNode = patternVariantKeyNode(pattern) {
4780
                if let case NodeExtra::UnionVariant { .. } = self.nodeData.entries[keyNode.id].extra {
4781
                    return true;
4782
                }
4783
            }
4784
            // Plain record destructuring / non-variant call is not directly
4785
            // refining; recurse to check sub-patterns.
4786
            return hasNestedRefiningPattern(self, pattern);
4787
        }
4788
        case ast::NodeValue::ArrayLit(items) => {
4789
            for item in items {
4790
                if isRefiningPattern(self, item) {
4791
                    return true;
4792
                }
4793
            }
4794
            return false;
4795
        }
4796
        case ast::NodeValue::ScopeAccess(_) =>
4797
            return true,
4798
        else =>
4799
            return true,
4800
    }
4801
}
4802
4803
/// Check whether any pattern in a case prong matches unconditionally.
4804
/// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies.
4805
/// Note: top-level identifiers in `case` are comparisons, not bindings,
4806
/// so they do not count as wildcards.
4807
fn hasWildcardPattern(patterns: *mut [*ast::Node]) -> bool {
4808
    for pattern in patterns {
4809
        match pattern.value {
4810
            case ast::NodeValue::Placeholder => return true,
4811
            case ast::NodeValue::ArrayLit(items) => {
4812
                if isIrrefutableArrayPattern(items) {
4813
                    return true;
4814
                }
4815
            }
4816
            else => {}
4817
        }
4818
    }
4819
    return false;
4820
}
4821
4822
/// Check whether all elements of an array pattern are irrefutable.
4823
/// Inside array patterns, identifiers are bindings, not comparisons.
4824
fn isIrrefutableArrayPattern(items: *mut [*ast::Node]) -> bool {
4825
    for item in items {
4826
        match item.value {
4827
            case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {}
4828
            case ast::NodeValue::ArrayLit(inner) => {
4829
                if not isIrrefutableArrayPattern(inner) {
4830
                    return false;
4831
                }
4832
            }
4833
            else => return false,
4834
        }
4835
    }
4836
    return true;
4837
}
4838
4839
/// Analyze a match prong, checking for duplicate catch-alls. Returns the
4840
/// unified match type.
4841
fn resolveMatchProng(
4842
    self: *mut Resolver,
4843
    prongNode: *ast::Node,
4844
    prong: ast::MatchProng,
4845
    subjectTy: Type,
4846
    state: *mut MatchState,
4847
    matchType: Type,
4848
    matchBy: MatchBy
4849
) -> Type throws (ResolveError) {
4850
    // Whether this prong is catch-all.
4851
    let mut isCatchAll = false;
4852
4853
    if prong.guard <> nil {
4854
        set state.isConst = false;
4855
    } else {
4856
        match prong.arm {
4857
            case ast::ProngArm::Binding(_),
4858
                 ast::ProngArm::Else => set isCatchAll = true,
4859
            case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
4860
        }
4861
    }
4862
    if isCatchAll {
4863
        if state.catchAll {
4864
            throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
4865
        }
4866
        set state.catchAll = true;
4867
    }
4868
    setProngCatchAll(self, prongNode, isCatchAll);
4869
4870
    return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
4871
}
4872
4873
/// Analyze a `match` expression. Dispatches to specialized functions based on
4874
/// the subject type.
4875
fn resolveMatch(self: *mut Resolver, node: *ast::Node, sw: ast::Match) -> Type
4876
    throws (ResolveError)
4877
{
4878
    let subjectTy = try infer(self, sw.subject);
4879
    let subject = unwrapMatchSubject(subjectTy);
4880
4881
    if let case Type::Optional(inner) = subject.effectiveTy {
4882
        try resolveMatchOptional(self, node, sw, inner, subject.by);
4883
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
4884
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4885
    } else {
4886
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4887
    }
4888
4889
    // Mark last non-guarded prong as exhaustive.
4890
    let lastProng = sw.prongs[sw.prongs.len - 1];
4891
    let case ast::NodeValue::MatchProng(p) = lastProng.value
4892
        else panic "resolveMatch: expected match prong";
4893
    if p.guard == nil {
4894
        setProngCatchAll(self, lastProng, true);
4895
    }
4896
    let ty = typeFor(self, node) else {
4897
        return Type::Void;
4898
    };
4899
    return ty;
4900
}
4901
4902
/// Analyze a `match` expression on an optional subject.
4903
fn resolveMatchOptional(
4904
    self: *mut Resolver,
4905
    node: *ast::Node,
4906
    sw: ast::Match,
4907
    innerTy: *Type,
4908
    matchBy: MatchBy
4909
) -> Type throws (ResolveError)
4910
{
4911
    let subjectTy = Type::Optional(innerTy);
4912
    let prongs = sw.prongs;
4913
    let mut hasValue = false;
4914
    let mut hasNil = false;
4915
    let mut catchAll = false;
4916
    let mut matchType = Type::Never;
4917
4918
    for prongNode in prongs {
4919
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
4920
            else panic "resolveMatchOptional: expected match prong";
4921
4922
        let mut isCatchAll = false;
4923
        if prong.guard == nil {
4924
            match prong.arm {
4925
                case ast::ProngArm::Else => set isCatchAll = true,
4926
                case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
4927
                case ast::ProngArm::Binding(_) => {
4928
                    // For optionals, a binding does *not* always match.
4929
                }
4930
            }
4931
        }
4932
        if isCatchAll {
4933
            if catchAll {
4934
                throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
4935
            }
4936
            set catchAll = true;
4937
        }
4938
        setProngCatchAll(self, prongNode, isCatchAll);
4939
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
4940
4941
        // Track coverage. Guarded prongs don't count as covering a case.
4942
        if prong.guard == nil {
4943
            if let case ast::ProngArm::Binding(_) = prong.arm {
4944
                if hasValue {
4945
                    throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern);
4946
                }
4947
                set hasValue = true;
4948
            } else if let case ast::ProngArm::Case(patterns) = prong.arm {
4949
                for pat in patterns {
4950
                    if let case ast::NodeValue::Nil = pat.value {
4951
                        if hasNil {
4952
                            throw emitError(self, pat, ErrorKind::DuplicateMatchPattern);
4953
                        }
4954
                        set hasNil = true;
4955
                    }
4956
                }
4957
            }
4958
        }
4959
    }
4960
4961
    // Check exhaustiveness.
4962
    if not catchAll {
4963
        if not hasValue {
4964
            throw emitError(self, node, ErrorKind::OptionalMatchMissingValue);
4965
        }
4966
        if not hasNil {
4967
            throw emitError(self, node, ErrorKind::OptionalMatchMissingNil);
4968
        }
4969
    } else if hasValue and hasNil {
4970
        throw emitError(self, node, ErrorKind::UnreachableElse);
4971
    }
4972
    return setNodeType(self, node, matchType);
4973
}
4974
4975
/// Analyze a `match` expression on a union subject.
4976
fn resolveMatchUnion(
4977
    self: *mut Resolver,
4978
    node: *ast::Node,
4979
    sw: ast::Match,
4980
    subjectTy: Type,
4981
    info: UnionType,
4982
    matchBy: MatchBy
4983
) -> Type throws (ResolveError) {
4984
    let prongs = sw.prongs;
4985
    let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS];
4986
    let mut coveredCount: u32 = 0;
4987
    let mut state = MatchState { catchAll: false, isConst: false };
4988
    let mut matchType = Type::Never;
4989
4990
    for prongNode in prongs {
4991
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
4992
            else panic "resolveMatchUnion: expected match prong";
4993
4994
        set matchType = try resolveMatchProng(self, prongNode, prong, subjectTy, &mut state, matchType, matchBy);
4995
4996
        // Guarded prongs don't count as covering. Patterns with nested
4997
        // refining sub-patterns (e.g. matching different inner union variants)
4998
        // don't count as duplicates or as fully covering.
4999
        if prong.guard == nil {
5000
            if let case ast::ProngArm::Case(patterns) = prong.arm {
5001
                for pattern in patterns {
5002
                    if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra {
5003
                        if not hasNestedRefiningPattern(self, pattern) {
5004
                            if covered[ix] {
5005
                                throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern);
5006
                            }
5007
                            set covered[ix] = true;
5008
                            set coveredCount += 1;
5009
                        }
5010
                    }
5011
                }
5012
            }
5013
        }
5014
    }
5015
    // Check that all variants are covered.
5016
    if not state.catchAll {
5017
        for variant, i in info.variants {
5018
            if not covered[i] {
5019
                throw emitError(
5020
                    self, node, ErrorKind::UnionMatchNonExhaustive(variant.name)
5021
                );
5022
            }
5023
        }
5024
    } else if coveredCount == info.variants.len as u32 {
5025
        throw emitError(self, node, ErrorKind::UnreachableElse);
5026
    }
5027
    return setNodeType(self, node, matchType);
5028
}
5029
5030
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
5031
/// booleans must cover both `true` and `false`, other types require a catch-all.
5032
fn resolveMatchGeneric(self: *mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type
5033
    throws (ResolveError)
5034
{
5035
    let prongs = sw.prongs;
5036
    let mut state = MatchState { catchAll: false, isConst: true };
5037
    let mut matchType = Type::Never;
5038
    let mut hasTrue = false;
5039
    let mut hasFalse = false;
5040
    let mut hasConstCase = false;
5041
5042
    for prongNode in prongs {
5043
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
5044
            else panic "resolveMatchGeneric: expected match prong";
5045
5046
        set matchType = try resolveMatchProng(
5047
            self, prongNode, prong, subjectTy, &mut state, matchType, MatchBy::Value
5048
        );
5049
        // Track boolean coverage. Guarded prongs don't count as covering.
5050
        if let case ast::ProngArm::Case(patterns) = prong.arm {
5051
            for p in patterns {
5052
                if prong.guard == nil {
5053
                    if let case ast::NodeValue::Bool(val) = p.value {
5054
                        if (val and hasTrue) or (not val and hasFalse) {
5055
                            throw emitError(self, p, ErrorKind::DuplicateMatchPattern);
5056
                        }
5057
                        if val {
5058
                            set hasTrue = true;
5059
                        } else {
5060
                            set hasFalse = true;
5061
                        }
5062
                    }
5063
                }
5064
                // Scalar constant patterns allow the match to be lowered
5065
                // to a switch instruction.
5066
                if let c = constValueEntry(self, p) {
5067
                    match c {
5068
                        case ConstValue::Bool(_), ConstValue::Char(_), ConstValue::Int(_) =>
5069
                            set hasConstCase = true,
5070
                        else =>
5071
                            set state.isConst = false,
5072
                    }
5073
                }
5074
            }
5075
        }
5076
    }
5077
5078
    // Check exhaustiveness.
5079
    if not state.catchAll {
5080
        if let case Type::Bool = subjectTy {
5081
            if not hasTrue {
5082
                throw emitError(self, node, ErrorKind::BoolMatchMissing(true));
5083
            }
5084
            if not hasFalse {
5085
                throw emitError(self, node, ErrorKind::BoolMatchMissing(false));
5086
            }
5087
        } else {
5088
            throw emitError(self, node, ErrorKind::MatchNonExhaustive);
5089
        }
5090
    } else if let case Type::Bool = subjectTy {
5091
        if hasTrue and hasFalse {
5092
            throw emitError(self, node, ErrorKind::UnreachableElse);
5093
        }
5094
    }
5095
    setMatchConst(self, node, state.isConst and hasConstCase);
5096
5097
    return setNodeType(self, node, matchType);
5098
}
5099
5100
/// Analyze a single `match` prong branch. Returns the unified match type.
5101
fn visitMatchProng(
5102
    self: *mut Resolver,
5103
    node: *ast::Node,
5104
    prongNode: ast::MatchProng,
5105
    subjectTy: Type,
5106
    matchType: Type,
5107
    matchBy: MatchBy
5108
) -> Type throws (ResolveError) {
5109
    enterScope(self, node);
5110
    let prongTy = try resolveMatchProngBody(self, prongNode, subjectTy, matchBy) catch e {
5111
        exitScope(self);
5112
        throw e;
5113
    };
5114
    exitScope(self);
5115
    setNodeType(self, node, prongTy);
5116
5117
    return unifyBranches(matchType, prongTy);
5118
}
5119
5120
/// Analyze the contents of a `match` prong while inside the prong scope.
5121
fn resolveMatchProngBody(
5122
    self: *mut Resolver,
5123
    prong: ast::MatchProng,
5124
    subjectTy: Type,
5125
    matchBy: MatchBy
5126
) -> Type throws (ResolveError) {
5127
    match prong.arm {
5128
        case ast::ProngArm::Binding(pat) => {
5129
            // For optionals, bind the unwrapped inner type.
5130
            let mut bindTy = subjectTy;
5131
            if let case Type::Optional(inner) = subjectTy {
5132
                set bindTy = *inner;
5133
            }
5134
            try bindPatternVar(self, pat, bindTy, matchBy);
5135
        }
5136
        case ast::ProngArm::Case(patterns) => {
5137
            for pattern in patterns {
5138
                try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy);
5139
            }
5140
        }
5141
        case ast::ProngArm::Else => {}
5142
    }
5143
    if let g = prong.guard {
5144
        try checkBoolean(self, g);
5145
    }
5146
    return try visit(self, prong.body, Type::Void);
5147
}
5148
5149
/// Ensure a scope access pattern references a compatible union variant.
5150
fn resolveUnionScopePattern(
5151
    self: *mut Resolver,
5152
    pattern: *ast::Node,
5153
    access: ast::Access,
5154
    subjectTy: Type,
5155
    unionType: UnionType
5156
) throws (ResolveError) {
5157
    let patternTy = try visit(self, pattern, subjectTy);
5158
    if not isComparable(patternTy, subjectTy) {
5159
        throw emitTypeMismatch(self, pattern, TypeMismatch {
5160
            expected: subjectTy,
5161
            actual: patternTy,
5162
        });
5163
    }
5164
    let case NodeExtra::UnionVariant { ordinal: index, .. } = self.nodeData.entries[pattern.id].extra else {
5165
        throw emitError(self, pattern, ErrorKind::Internal);
5166
    };
5167
    let variant = &unionType.variants[index];
5168
    // If this variant has a payload, throw an error, since the user hasn't
5169
    // provided one.
5170
    if variant.valueType <> Type::Void {
5171
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
5172
    }
5173
}
5174
5175
/// Validate and bind a union constructor call used as a `match` pattern.
5176
fn resolveUnionCallPattern(
5177
    self: *mut Resolver,
5178
    pattern: *ast::Node,
5179
    call: ast::Call,
5180
    subjectTy: Type,
5181
    unionType: UnionType,
5182
    matchBy: MatchBy
5183
) throws (ResolveError) {
5184
    let calleeTy = try checkEqual(self, call.callee, subjectTy);
5185
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
5186
        throw emitError(self, call.callee, ErrorKind::Internal);
5187
    };
5188
    let variant = &unionType.variants[index];
5189
    // Copy variant index to the pattern node for the lowerer.
5190
    setVariantInfo(self, pattern, index, tag);
5191
5192
    if variant.valueType <> Type::Void {
5193
        try bindUnionPatternPayload(self, pattern, call, variant.name, variant.valueType, matchBy);
5194
    } else {
5195
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5196
    }
5197
}
5198
5199
/// Bind the payload introduced by a union constructor pattern.
5200
fn bindUnionPatternPayload(
5201
    self: *mut Resolver,
5202
    pattern: *ast::Node,
5203
    call: ast::Call,
5204
    variantName: *[u8],
5205
    payloadTy: Type,
5206
    matchBy: MatchBy
5207
) throws (ResolveError) {
5208
    if call.args.len == 0 {
5209
        throw emitError(
5210
            self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName)
5211
        );
5212
    }
5213
    // All variant payloads are records.
5214
    let recInfo = getRecord(payloadTy)
5215
        else panic "bindUnionPatternPayload: payload is not a record";
5216
5217
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5218
}
5219
5220
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
5221
fn bindPatternVar(self: *mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy)
5222
    throws (ResolveError)
5223
{
5224
    let mut bindTy = ty;
5225
    match matchBy {
5226
        case MatchBy::Value => {}
5227
        case MatchBy::Ref => set bindTy = Type::Pointer {
5228
            class: types::PointerClass::Ref,
5229
            target: allocType(self, ty),
5230
            mutable: false,
5231
        },
5232
        case MatchBy::MutRef => set bindTy = Type::Pointer {
5233
            class: types::PointerClass::Ref,
5234
            target: allocType(self, ty),
5235
            mutable: true,
5236
        },
5237
    }
5238
    match binding.value {
5239
        case ast::NodeValue::Placeholder => {
5240
            // Nothing to do.
5241
        }
5242
        case ast::NodeValue::Ident(_) => {
5243
            try bindValueIdent(self, binding, binding, bindTy, false, 0, 0);
5244
        }
5245
        else => {
5246
            // Nested pattern: recursively resolve (record destructuring,
5247
            // union variant, scope access, call, literals, etc).
5248
            try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy);
5249
        }
5250
    }
5251
}
5252
5253
/// Bind record pattern fields to variables in the current scope.
5254
fn bindRecordPatternFields(
5255
    self: *mut Resolver,
5256
    pattern: *ast::Node,
5257
    recInfo: RecordType,
5258
    matchBy: MatchBy
5259
) throws (ResolveError) {
5260
    match pattern.value {
5261
        case ast::NodeValue::Call(call) => {
5262
            // Unlabeled patterns: `S(x, y)`.
5263
            try checkRecordArity(self, call.args, recInfo, pattern);
5264
5265
            for binding, i in call.args {
5266
                let fieldType = recInfo.fields[i].fieldType;
5267
                try bindPatternVar(self, binding, fieldType, matchBy);
5268
            }
5269
        }
5270
        case ast::NodeValue::RecordLit(lit) => {
5271
            // Labeled patterns: `T { x, y }` or `T { x: binding }`.
5272
            if not lit.ignoreRest {
5273
                try checkRecordArity(self, lit.fields, recInfo, pattern);
5274
            }
5275
            for fieldNode in lit.fields {
5276
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5277
                    else panic "expected RecordLitField";
5278
5279
                // Brace patterns require labeled fields.
5280
                let label = field.label else panic "expected labeled field";
5281
                let fieldName = try nodeName(self, label);
5282
                let fieldIndex = findRecordField(&recInfo, fieldName)
5283
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
5284
                let fieldType = recInfo.fields[fieldIndex].fieldType;
5285
                // Store field index for the lowerer.
5286
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5287
                try bindPatternVar(self, field.value, fieldType, matchBy);
5288
            }
5289
        }
5290
        else => throw emitError(self, pattern, ErrorKind::Internal)
5291
    }
5292
}
5293
5294
/// Validate and bind a record literal pattern for matching labeled union variants.
5295
fn resolveUnionRecordPattern(
5296
    self: *mut Resolver,
5297
    pattern: *ast::Node,
5298
    lit: ast::RecordLit,
5299
    subjectTy: Type,
5300
    unionType: UnionType,
5301
    matchBy: MatchBy
5302
) throws (ResolveError) {
5303
    let typeName = lit.typeName else {
5304
        throw emitError(self, pattern, ErrorKind::Internal);
5305
    };
5306
    // Verify the type matches the subject.
5307
    let patternTy = try visit(self, typeName, subjectTy);
5308
    if not isComparable(patternTy, subjectTy) {
5309
        throw emitTypeMismatch(self, pattern, TypeMismatch {
5310
            expected: subjectTy,
5311
            actual: patternTy,
5312
        });
5313
    }
5314
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[typeName.id].extra else {
5315
        throw emitError(self, typeName, ErrorKind::Internal);
5316
    };
5317
    let variant = &unionType.variants[index];
5318
5319
    // Copy variant index to the pattern node for the lowerer.
5320
    setVariantInfo(self, pattern, index, tag);
5321
5322
    if variant.valueType == Type::Void {
5323
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5324
    }
5325
    let recInfo = getRecord(variant.valueType)
5326
        else panic "resolveUnionRecordPattern: payload is not a record";
5327
5328
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5329
}
5330
5331
/// Analyze a pattern appearing in a union case.
5332
fn resolveUnionPattern(
5333
    self: *mut Resolver,
5334
    pattern: *ast::Node,
5335
    subjectTy: Type,
5336
    unionType: UnionType,
5337
    matchBy: MatchBy
5338
) throws (ResolveError) {
5339
    match pattern.value {
5340
        case ast::NodeValue::ScopeAccess(access) =>
5341
            try resolveUnionScopePattern(self, pattern, access, subjectTy, unionType),
5342
        case ast::NodeValue::Call(call) =>
5343
            try resolveUnionCallPattern(self, pattern, call, subjectTy, unionType, matchBy),
5344
        case ast::NodeValue::RecordLit(lit) =>
5345
            try resolveUnionRecordPattern(self, pattern, lit, subjectTy, unionType, matchBy),
5346
        else => {
5347
            let patternTy = try visit(self, pattern, subjectTy);
5348
            throw emitTypeMismatch(self, pattern, TypeMismatch {
5349
                expected: subjectTy,
5350
                actual: patternTy,
5351
            });
5352
        }
5353
    }
5354
}
5355
5356
/// Return whether a case pattern introduces value bindings.
5357
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
5358
    match pattern.value {
5359
        case ast::NodeValue::Ident(_) => return nested,
5360
        case ast::NodeValue::Call(call) => {
5361
            for arg in call.args {
5362
                if casePatternIntroducesBindings(arg, true) {
5363
                    return true;
5364
                }
5365
            }
5366
        }
5367
        case ast::NodeValue::RecordLit(lit) => {
5368
            for fieldNode in lit.fields {
5369
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5370
                    else continue;
5371
                if casePatternIntroducesBindings(field.value, true) {
5372
                    return true;
5373
                }
5374
            }
5375
        }
5376
        case ast::NodeValue::ArrayLit(items) => {
5377
            for item in items {
5378
                if casePatternIntroducesBindings(item, true) {
5379
                    return true;
5380
                }
5381
            }
5382
        }
5383
        else => {}
5384
    }
5385
    return false;
5386
}
5387
5388
/// Analyze a `let-else` guard.
5389
fn resolveLetElse(self: *mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
5390
    throws (ResolveError)
5391
{
5392
    let pat = &letElse.pattern;
5393
    let exprTy = try infer(self, pat.scrutinee);
5394
5395
    match pat.kind {
5396
        case ast::PatternKind::Binding => {
5397
            // Simple binding requires an optional expression.
5398
            let case Type::Optional(inner) = exprTy else {
5399
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
5400
            };
5401
            let payloadTy = *inner;
5402
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
5403
            // The `else` branch supplies the binding when the optional is nil.
5404
            try checkAssignable(self, letElse.elseBranch, payloadTy);
5405
5406
            return setNodeType(self, node, Type::Void);
5407
        }
5408
        case ast::PatternKind::Case => {
5409
            // Resolve the failure path before introducing success-only bindings.
5410
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
5411
            try resolveCasePattern(
5412
                self,
5413
                pat.pattern,
5414
                exprTy,
5415
                IdentMode::Compare,
5416
                MatchBy::Value,
5417
            );
5418
            if let guardExpr = pat.guard {
5419
                try checkBoolean(self, guardExpr);
5420
            }
5421
            if elseTy <> Type::Never and
5422
               casePatternIntroducesBindings(pat.pattern, false)
5423
            {
5424
                throw emitError(
5425
                    self,
5426
                    letElse.elseBranch,
5427
                    ErrorKind::LinearLetElseMustTerminate,
5428
                );
5429
            }
5430
        }
5431
    }
5432
    return setNodeType(self, node, Type::Void);
5433
}
5434
5435
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
5436
fn resolveBuiltinCall(
5437
    self: *mut Resolver,
5438
    node: *ast::Node,
5439
    kind: ast::Builtin,
5440
    args: *mut [*ast::Node]
5441
) -> Type throws (ResolveError) {
5442
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
5443
    if kind == ast::Builtin::SliceOf {
5444
        if args.len <> 2 and args.len <> 3 {
5445
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5446
                expected: 2,
5447
                actual: args.len as u32,
5448
            }));
5449
        }
5450
        try requireUnsafe(self, node);
5451
        let ptrType = try visit(self, args[0], Type::Unknown);
5452
        let case Type::Pointer { class, target, mutable } = ptrType else {
5453
            throw emitError(self, node, ErrorKind::ExpectedPointer);
5454
        };
5455
        let _ = try checkAssignable(self, args[1], Type::U32);
5456
        if args.len == 3 {
5457
            let _ = try checkAssignable(self, args[2], Type::U32);
5458
        }
5459
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
5460
    }
5461
    if args.len <> 1 {
5462
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5463
            expected: 1,
5464
            actual: args.len as u32,
5465
        }));
5466
    }
5467
5468
    let ty = try resolveValueType(self, args[0]);
5469
    // Ensure the type body is resolved before computing layout.
5470
    // TODO: Somehow, ensuring the type is resolved should just happen all
5471
    // the time, lazily.
5472
    try ensureTypeResolved(self, ty, args[0]);
5473
    // TODO: This should be stored in `symbol` instead of having to recompute it.
5474
    // That way there's a canonical place to look for code gen.
5475
    let layout = getTypeLayout(ty);
5476
5477
    // Evaluate the built-in.
5478
    let mut value: u32 = undefined;
5479
    match kind {
5480
        case ast::Builtin::SizeOf => {
5481
            set value = layout.size;
5482
        },
5483
        case ast::Builtin::AlignOf => {
5484
            set value = layout.alignment;
5485
        },
5486
        case ast::Builtin::SliceOf => {
5487
            panic "unreachable: @sliceOf handled above";
5488
        }
5489
    }
5490
    // Record as constant value for constant folding.
5491
    setNodeConstValue(self, node, ConstValue::Int(ConstInt {
5492
        magnitude: value as u64,
5493
        bits: 32,
5494
        signed: false,
5495
        negative: false,
5496
    }));
5497
    return setNodeType(self, node, Type::U32);
5498
}
5499
5500
/// Validate call arguments against a function type: check argument count,
5501
/// type-check each argument, and verify that throwing functions use `try`.
5502
fn checkCallArgs(self: *mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
5503
    throws (ResolveError)
5504
{
5505
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
5506
        throw emitError(self, node, ErrorKind::MissingTry);
5507
    }
5508
    if call.args.len <> info.paramTypes.len as u32 {
5509
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5510
            expected: info.paramTypes.len as u32,
5511
            actual: call.args.len,
5512
        }));
5513
    }
5514
    for argNode, i in call.args {
5515
        let expectedTy = *info.paramTypes[i];
5516
5517
        try checkAssignable(self, argNode, expectedTy);
5518
    }
5519
}
5520
5521
/// Analyze a function call expression.
5522
fn resolveCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type
5523
    throws (ResolveError)
5524
{
5525
    // Intercept method calls on slices before inferring the callee.
5526
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5527
        let parentTy = try infer(self, access.parent);
5528
        if isUnsafePointerType(parentTy) {
5529
            try requireUnsafe(self, access.parent);
5530
        }
5531
        let subjectTy = autoDeref(parentTy);
5532
5533
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5534
            let methodName = try nodeName(self, access.child);
5535
            if methodName == "append" {
5536
                return try resolveSliceAppend(
5537
                    self, node, access.parent, parentTy, call.args, item, mutable
5538
                );
5539
            }
5540
            if methodName == "delete" {
5541
                return try resolveSliceDelete(
5542
                    self, node, access.parent, call.args, item, mutable
5543
                );
5544
            }
5545
        }
5546
    }
5547
    let calleeTy = try infer(self, call.callee);
5548
    if let case Type::Fn(info) = calleeTy {
5549
        try checkUnsafeCall(self, call.callee, info);
5550
    }
5551
5552
    // Check if callee is a union variant and dispatch to constructor handler.
5553
    // TODO: Move this out. We should decide on this earlier, based on the callee.
5554
    if let calleeSym = symbolFor(self, call.callee) {
5555
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
5556
            // TODO: Don't pass the callee type, pass the union type by getting it from
5557
            // the symbol.
5558
            let declSym = symbolFor(self, decl) else panic;
5559
            let case SymbolData::Type(ty) = declSym.data else panic;
5560
5561
            return try resolveUnionConstructorCall(self, node, call, ty);
5562
        }
5563
        // Check if callee is an unlabeled record type for constructor call syntax.
5564
        if let case SymbolData::Type(ty) = calleeSym.data {
5565
            // Ensure the record body is resolved before checking if labeled.
5566
            try ensureNominalResolved(self, ty, call.callee);
5567
            if let case NominalType::Record(recInfo) = *ty {
5568
                if not recInfo.labeled {
5569
                    return try resolveRecordConstructorCall(self, node, call, ty);
5570
                }
5571
            }
5572
        }
5573
    }
5574
5575
    // Check if we have a trait method call, ie. callee is a trait object.
5576
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5577
        let mut parentTy = Type::Unknown;
5578
        if let t = typeFor(self, access.parent) {
5579
            set parentTy = t;
5580
        }
5581
        let subjectTy = autoDeref(parentTy);
5582
5583
        if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy {
5584
            let methodName = try nodeName(self, access.child);
5585
            let method = findTraitMethod(traitInfo, methodName)
5586
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
5587
5588
            // Reject mutable-receiver methods called on immutable trait objects.
5589
            if method.mutable and not objMutable {
5590
                throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5591
            }
5592
            try checkCallArgs(self, node, call, method.fnType, ctx);
5593
            setTraitMethodCall(self, node, traitInfo, method.index);
5594
5595
            return setNodeType(self, node, *method.fnType.returnType);
5596
        }
5597
5598
        // Check for a standalone method call on a concrete type.
5599
        if let case Type::Nominal(_) = subjectTy {
5600
            let methodName = try nodeName(self, access.child);
5601
            if let method = findMethod(self, subjectTy, methodName) {
5602
                // Reject mutable-receiver methods on immutable bindings.
5603
                // If the parent is already a mutable pointer, the receiver is fine.
5604
                // Otherwise, check that the parent can yield a mutable borrow.
5605
                if method.mutable {
5606
                    let mut isMutPtr = false;
5607
                    if let case Type::Pointer { mutable, .. } = parentTy {
5608
                        set isMutPtr = mutable;
5609
                    }
5610
                    if not isMutPtr and not (try canBorrowMutFrom(self, access.parent)) {
5611
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5612
                    }
5613
                }
5614
                // Check arguments (excluding receiver).
5615
                try checkCallArgs(self, node, call, method.fnType, ctx);
5616
                set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
5617
5618
                return setNodeType(self, node, *method.fnType.returnType);
5619
            }
5620
        }
5621
    }
5622
    let case Type::Fn(info) = calleeTy else {
5623
        throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch {
5624
            expected: Type::Unknown,
5625
            actual: calleeTy,
5626
        }));
5627
    };
5628
    try checkCallArgs(self, node, call, info, ctx);
5629
    // Associate function type to callee.
5630
    setNodeType(self, call.callee, calleeTy);
5631
5632
    // Associate return type to call.
5633
    return setNodeType(self, node, *info.returnType);
5634
}
5635
5636
/// Return whether a type has the exact allocator representation used by slice append lowering.
5637
fn isSliceAllocatorType(ty: Type) -> bool {
5638
    let case Type::Nominal(NominalType::Record(recInfo)) = ty
5639
        else return false;
5640
    if recInfo.fields.len <> 2
5641
        or recInfo.layout.size <> PTR_SIZE * 2
5642
        or recInfo.layout.alignment <> PTR_SIZE
5643
        or recInfo.fields[0].offset <> 0
5644
        or recInfo.fields[1].offset <> PTR_SIZE as i32
5645
    {
5646
        return false;
5647
    }
5648
5649
    let case Type::Fn(callback) = recInfo.fields[0].fieldType
5650
        else return false;
5651
    if callback.isUnsafe
5652
        or callback.paramTypes.len <> 3
5653
        or callback.throwList.len <> 0
5654
        or *callback.paramTypes[1] <> Type::U32
5655
        or *callback.paramTypes[2] <> Type::U32
5656
    {
5657
        return false;
5658
    }
5659
    let case Type::Pointer {
5660
        class: types::PointerClass::Owned,
5661
        target: callbackCtx,
5662
        mutable: true,
5663
    } = *callback.paramTypes[0] else return false;
5664
    if *callbackCtx <> Type::Opaque {
5665
        return false;
5666
    }
5667
    let case Type::Pointer {
5668
        class: types::PointerClass::Owned,
5669
        target: result,
5670
        mutable: true,
5671
    } = *callback.returnType else return false;
5672
    if *result <> Type::Opaque {
5673
        return false;
5674
    }
5675
5676
    return typesEqual(recInfo.fields[1].fieldType, *callback.paramTypes[0]);
5677
}
5678
5679
/// Resolve `slice.append(val, allocator)`.
5680
fn resolveSliceAppend(
5681
    self: *mut Resolver,
5682
    node: *ast::Node,
5683
    parent: *ast::Node,
5684
    parentType: Type,
5685
    args: *mut [*ast::Node],
5686
    elemType: *Type,
5687
    mutable: bool
5688
) -> Type throws (ResolveError) {
5689
    if not mutable {
5690
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5691
    }
5692
    if args.len <> 2 {
5693
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5694
            expected: 2,
5695
            actual: args.len as u32,
5696
        }));
5697
    }
5698
    try requireUnsafe(self, node);
5699
    // First argument must be assignable to the element type.
5700
    try checkAssignable(self, args[0], *elemType);
5701
    // The lowerer loads the allocator callback and context from fixed offsets.
5702
    let allocatorType = try visit(self, args[1], Type::Unknown);
5703
    try ensureTypeResolved(self, allocatorType, args[1]);
5704
    if not isSliceAllocatorType(allocatorType) {
5705
        throw emitTypeMismatch(self, args[1], TypeMismatch {
5706
            expected: Type::Unknown,
5707
            actual: allocatorType,
5708
        });
5709
    }
5710
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
5711
5712
    // Return the parent's type so the caller can rebind:
5713
    return setNodeType(self, node, parentType);
5714
}
5715
5716
/// Resolve `slice.delete(index)`.
5717
fn resolveSliceDelete(
5718
    self: *mut Resolver,
5719
    node: *ast::Node,
5720
    parent: *ast::Node,
5721
    args: *mut [*ast::Node],
5722
    elemType: *Type,
5723
    mutable: bool
5724
) -> Type throws (ResolveError) {
5725
    if not mutable {
5726
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5727
    }
5728
    if args.len <> 1 {
5729
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5730
            expected: 1,
5731
            actual: args.len as u32,
5732
        }));
5733
    }
5734
    try checkAssignable(self, args[0], Type::U32);
5735
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType };
5736
5737
    return setNodeType(self, node, Type::Void);
5738
}
5739
5740
/// Analyze an assignment expression.
5741
fn resolveAssign(self: *mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
5742
    throws (ResolveError)
5743
{
5744
    // Slice assignment: `slice[range] = value`.
5745
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
5746
        if let case ast::NodeValue::Range(range) = index.value {
5747
            try infer(self, index);
5748
            let containerTy = try infer(self, container);
5749
            if not try canBorrowMutFrom(self, container) {
5750
                throw emitError(self, container, ErrorKind::ImmutableBinding);
5751
            }
5752
            let subjectTy = autoDeref(containerTy);
5753
            try checkSliceRangeIndices(self, range);
5754
5755
            let mut item: *Type = undefined;
5756
            let mut capacity: ?u32 = nil;
5757
5758
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
5759
                if not sliceMutable {
5760
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
5761
                }
5762
                set item = sliceItem;
5763
            } else {
5764
                match subjectTy {
5765
                    case Type::Array(a) => {
5766
                        try validateArraySliceBounds(self, range, a.length, node);
5767
                        set item = a.item;
5768
                        set capacity = a.length;
5769
                    }
5770
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5771
                }
5772
            }
5773
            // RHS is either a fill value or a source slice.
5774
            let rhsTy = try infer(self, assign.right);
5775
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
5776
                if *sourceItem <> *item {
5777
                    throw emitTypeMismatch(
5778
                        self,
5779
                        assign.right,
5780
                        TypeMismatch { expected: *item, actual: *sourceItem },
5781
                    );
5782
                }
5783
            } else {
5784
                try checkAssignable(self, assign.right, *item);
5785
            }
5786
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
5787
            setNodeType(self, assign.left, *item);
5788
5789
            return setNodeType(self, node, Type::Void);
5790
        }
5791
    }
5792
    let leftTy = try infer(self, assign.left);
5793
5794
    // Check if the left-hand side can be assigned to by checking if it's a mutable location.
5795
    if not try canBorrowMutFrom(self, assign.left) {
5796
        throw emitError(self, assign.left, ErrorKind::ImmutableBinding);
5797
    }
5798
    try checkAssignable(self, assign.right, leftTy);
5799
5800
    return setNodeType(self, node, leftTy);
5801
}
5802
5803
/// Ensure slice range bounds are valid `u32` values.
5804
fn checkSliceRangeIndices(self: *mut Resolver, range: ast::Range) throws (ResolveError) {
5805
    if let start = range.start {
5806
        try checkIndex(self, start);
5807
    }
5808
    if let end = range.end {
5809
        try checkIndex(self, end);
5810
    }
5811
}
5812
5813
/// Emit an error when a slice range with compile-tyime values exceeds the array length.
5814
fn validateArraySliceBounds(self: *mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5815
    let mut startVal: ?u32 = nil;
5816
    let mut endVal: ?u32 = length;
5817
5818
    if let startNode = range.start {
5819
        if let val = constSliceIndex(self, startNode) {
5820
            set startVal = val;
5821
        }
5822
    }
5823
    if let endNode = range.end {
5824
        if let val = constSliceIndex(self, endNode) {
5825
            set endVal = val;
5826
        }
5827
    }
5828
    if let val = startVal; val > length {
5829
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5830
    }
5831
    if let val = endVal; val > length {
5832
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5833
    }
5834
    if let start = startVal {
5835
        if let end = endVal; start > end {
5836
            throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5837
        }
5838
    }
5839
}
5840
5841
/// Check that an index expression has an unsigned integer type.
5842
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
5843
/// Smaller types are widened to `u32` via a numeric cast coercion.
5844
fn checkIndex(self: *mut Resolver, indexNode: *ast::Node) throws (ResolveError) {
5845
    let indexTy = try visit(self, indexNode, Type::U32);
5846
    if indexTy == Type::Int or indexTy == Type::U32 {
5847
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
5848
        return;
5849
    }
5850
    match indexTy {
5851
        case Type::U8, Type::U16 => {
5852
            setNodeCoercion(self, indexNode, Coercion::NumericCast {
5853
                from: indexTy, to: Type::U32,
5854
            });
5855
        }
5856
        else => {
5857
            throw emitTypeMismatch(self, indexNode, TypeMismatch {
5858
                expected: Type::U32,
5859
                actual: indexTy,
5860
            });
5861
        }
5862
    }
5863
}
5864
5865
/// Analyze an array or slice subscript expression.
5866
fn resolveSubscript(self: *mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
5867
    throws (ResolveError)
5868
{
5869
    // Range subscripts always require `&` to form a slice.
5870
    if let case ast::NodeValue::Range(range) = indexNode.value {
5871
        let _ = try infer(self, indexNode);
5872
        let _ = try infer(self, container);
5873
        try checkSliceRangeIndices(self, range);
5874
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
5875
    }
5876
    let containerTy = try infer(self, container);
5877
    if isUnsafePointerType(containerTy) {
5878
        try requireUnsafe(self, container);
5879
    }
5880
    try checkIndex(self, indexNode);
5881
    let subjectTy = autoDeref(containerTy);
5882
    if let case Type::Slice { item, .. } = subjectTy {
5883
        return setNodeType(self, node, *item);
5884
    }
5885
5886
    match subjectTy {
5887
        case Type::Array(arrayInfo) => {
5888
            return setNodeType(self, node, *arrayInfo.item);
5889
        }
5890
        else => {
5891
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
5892
        }
5893
    }
5894
}
5895
5896
/// Find a record field by name.
5897
fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 {
5898
    for field, i in s.fields {
5899
        if let name = field.name {
5900
            if name == fieldName {
5901
                return i;
5902
            }
5903
        }
5904
    }
5905
    return nil;
5906
}
5907
5908
/// Analyze a union constructor call with payload.
5909
fn resolveUnionConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *NominalType) -> Type
5910
    throws (ResolveError)
5911
{
5912
    // Get the union nominal type.
5913
    let case NominalType::Union(unionType) = *unionNominal
5914
        else panic "resolveUnionConstructorCall: not a union type";
5915
5916
    // Callee was already visited; get the variant index it set.
5917
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
5918
        throw emitError(self, call.callee, ErrorKind::Internal);
5919
    };
5920
    let variant = &unionType.variants[index];
5921
5922
    // Associate variant index with `call` node for the lowerer.
5923
    setVariantInfo(self, node, index, tag);
5924
5925
    // Check if this variant expects a payload.
5926
    let payloadType = variant.valueType;
5927
    if payloadType <> Type::Void {
5928
        let recInfo = getRecord(payloadType)
5929
            else panic "resolveUnionVariantConstructor: payload is not a record";
5930
        try checkRecordConstructorArgs(self, node, call.args, recInfo);
5931
    } else {
5932
        if call.args.len > 0 {
5933
            throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5934
        }
5935
    }
5936
    return setNodeType(self, node, Type::Nominal(unionNominal));
5937
}
5938
5939
/// Analyze an unlabeled record constructor call.
5940
///
5941
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
5942
/// number of arguments matches the record's field count and that each argument
5943
/// is assignable to its corresponding field type.
5944
fn resolveRecordConstructorCall(self: *mut Resolver, node: *ast::Node, call: ast::Call, recordType: *NominalType) -> Type
5945
    throws (ResolveError)
5946
{
5947
    let case NominalType::Record(recInfo) = *recordType
5948
        else panic "resolveRecordConstructorCall: not a record type";
5949
5950
    try checkRecordConstructorArgs(self, node, call.args, recInfo);
5951
    return setNodeType(self, node, Type::Nominal(recordType));
5952
}
5953
5954
/// Resolve the type name of a record literal, handling both record types and
5955
/// union variant payloads like `Union::Variant { ... }`.
5956
fn resolveRecordLitType(
5957
    self: *mut Resolver, node: *ast::Node, typeIdent: *ast::Node
5958
) -> ResolvedRecordLitType
5959
    throws (ResolveError)
5960
{
5961
    // Check if this is a scope access that might be a union variant.
5962
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
5963
        let sym = try resolveAccess(self, typeIdent, access, self.scope);
5964
5965
        // Check if resolved symbol is a union variant.
5966
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
5967
            // Get the union type from the variant's declaration.
5968
            let declSym = symbolFor(self, decl)
5969
                else throw emitError(self, node, ErrorKind::Internal);
5970
            let case SymbolData::Type(unionNominalType) = declSym.data
5971
                else throw emitError(self, node, ErrorKind::Internal);
5972
5973
            // Get the variant's payload type.
5974
            let case Type::Nominal(payloadInfo) = type
5975
                else throw emitError(self, node, ErrorKind::ExpectedRecord);
5976
5977
            // Store the variant index for the lowerer.
5978
            setVariantInfo(self, node, ordinal, index);
5979
5980
            return ResolvedRecordLitType {
5981
                recordType: payloadInfo,
5982
                resultType: Type::Nominal(unionNominalType),
5983
            };
5984
        }
5985
        // Not a variant, must be a type.
5986
        let case SymbolData::Type(ty) = sym.data
5987
            else throw emitError(self, node, ErrorKind::ExpectedRecord);
5988
        return ResolvedRecordLitType {
5989
            recordType: ty,
5990
            resultType: Type::Nominal(ty),
5991
        };
5992
    }
5993
    // Simple identifier, resolve as type name.
5994
    let tyInfo = try resolveTypeName(self, typeIdent);
5995
    return ResolvedRecordLitType {
5996
        recordType: tyInfo,
5997
        resultType: Type::Nominal(tyInfo),
5998
    };
5999
}
6000
6001
/// Analyze a record literal expression.
6002
fn resolveRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6003
    throws (ResolveError)
6004
{
6005
    // If no type name, infer an anonymous tuple type.
6006
    let typeIdent = lit.typeName else {
6007
        return try resolveAnonRecordLit(self, node, lit, hint);
6008
    };
6009
    // Resolve the type name, handling both record types and union variants.
6010
    let resolved = try resolveRecordLitType(self, node, typeIdent);
6011
    let tyInfo = resolved.recordType;
6012
    let resultType = resolved.resultType;
6013
6014
    // Lazily resolve record body if not yet done.
6015
    try ensureNominalResolved(self, tyInfo, typeIdent);
6016
    let case NominalType::Record(recordType) = *tyInfo
6017
        else throw emitError(self, node, ErrorKind::ExpectedRecord);
6018
6019
    // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax.
6020
    if not recordType.labeled {
6021
        throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch);
6022
    }
6023
    // Check field count. With `{ .. }` syntax, fewer fields are allowed.
6024
    if lit.fields.len > recordType.fields.len {
6025
        throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
6026
            expected: recordType.fields.len as u32,
6027
            actual: lit.fields.len,
6028
        }));
6029
    }
6030
    if not lit.ignoreRest and lit.fields.len < recordType.fields.len {
6031
        let missingName = recordType.fields[lit.fields.len].name else panic;
6032
        throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
6033
    }
6034
6035
    // Fields must be in declaration order.
6036
    for fieldNode, idx in lit.fields {
6037
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
6038
            else panic "resolveRecordLit: expected field node value";
6039
        let label = fieldArg.label
6040
            else panic "resolveRecordLit: expected labeled field";
6041
        let fieldName = try nodeName(self, label);
6042
        let expected = recordType.fields[idx];
6043
        let expectedName = expected.name else panic;
6044
6045
        if fieldName <> expectedName {
6046
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
6047
                field: fieldName,
6048
                prev: expectedName,
6049
            });
6050
        }
6051
        setRecordFieldIndex(self, fieldNode, idx);
6052
        try checkAssignable(self, fieldArg.value, expected.fieldType);
6053
        setNodeType(self, fieldNode, expected.fieldType);
6054
    }
6055
    return setNodeType(self, node, resultType);
6056
}
6057
6058
/// Analyze an anonymous record literal, checking fields against the hint type.
6059
fn resolveAnonRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6060
    throws (ResolveError)
6061
{
6062
    // Unwrap optional hint to get the inner record type.
6063
    let mut innerHint = hint;
6064
    if let case Type::Optional(inner) = hint {
6065
        set innerHint = *inner;
6066
    }
6067
    let mut hintInfo: ?RecordType = nil;
6068
    if let case Type::Nominal(info) = innerHint {
6069
        try ensureNominalResolved(self, info, node);
6070
        if let case NominalType::Record(s) = *info {
6071
            set hintInfo = s;
6072
        }
6073
    }
6074
    let targetInfo = hintInfo else {
6075
        throw emitError(self, node, ErrorKind::CannotInferType);
6076
    };
6077
6078
    // Check field count.
6079
    if lit.fields.len <> targetInfo.fields.len {
6080
        if lit.fields.len < targetInfo.fields.len {
6081
            let missingName = targetInfo.fields[lit.fields.len].name else panic;
6082
            throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
6083
        } else {
6084
            throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
6085
                expected: targetInfo.fields.len as u32,
6086
                actual: lit.fields.len,
6087
            }));
6088
        }
6089
    }
6090
6091
    // Fields must be in declaration order.
6092
    for fieldNode, idx in lit.fields {
6093
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
6094
            else panic "resolveAnonRecordLit: expected field node value";
6095
        let label = fieldArg.label
6096
            else panic "resolveAnonRecordLit: expected labeled field";
6097
        let fieldName = try nodeName(self, label);
6098
        let expected = targetInfo.fields[idx];
6099
        let expectedName = expected.name else panic;
6100
6101
        if fieldName <> expectedName {
6102
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
6103
                field: fieldName,
6104
                prev: expectedName,
6105
            });
6106
        }
6107
        setRecordFieldIndex(self, fieldNode, idx);
6108
        let fieldType = try visit(self, fieldArg.value, expected.fieldType);
6109
6110
        try expectAssignable(self, expected.fieldType, fieldType, fieldArg.value);
6111
        setNodeType(self, fieldNode, fieldType);
6112
    }
6113
    return setNodeType(self, node, innerHint);
6114
}
6115
6116
/// Analyze an array literal expression.
6117
fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type
6118
    throws (ResolveError)
6119
{
6120
    let length = items.len;
6121
    let mut expectedTy: Type = Type::Unknown;
6122
6123
    if let case Type::Array(ary) = hint {
6124
        set expectedTy = *ary.item;
6125
    } else if let case Type::Optional(inner) = hint {
6126
        if let case Type::Array(ary) = *inner {
6127
            set expectedTy = *ary.item;
6128
        }
6129
    };
6130
    for itemNode in items {
6131
        let itemTy = try visit(self, itemNode, expectedTy);
6132
        assert itemTy <> Type::Unknown;
6133
6134
        // Set the expected type to the first type we encounter.
6135
        if expectedTy == Type::Unknown {
6136
            set expectedTy = itemTy;
6137
        } else {
6138
            try expectAssignable(self, expectedTy, itemTy, itemNode);
6139
        }
6140
    }
6141
    if expectedTy == Type::Unknown {
6142
        throw emitError(self, node, ErrorKind::CannotInferType);
6143
    };
6144
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
6145
    return setNodeType(self, node, arrayTy);
6146
}
6147
6148
/// Analyze an array repeat literal expression.
6149
fn resolveArrayRepeat(self: *mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
6150
    throws (ResolveError)
6151
{
6152
    let mut itemHint = hint;
6153
    if let case Type::Array(ary) = hint {
6154
        set itemHint = *ary.item;
6155
    } else if let case Type::Optional(inner) = hint {
6156
        if let case Type::Array(ary) = *inner {
6157
            set itemHint = *ary.item;
6158
        }
6159
    }
6160
    let valueTy = try visit(self, lit.item, itemHint);
6161
    let count = try checkSizeInt(self, lit.count);
6162
    let arrayTy = Type::Array(ArrayType {
6163
        item: allocType(self, valueTy),
6164
        length: count,
6165
    });
6166
    return setNodeType(self, node, arrayTy);
6167
}
6168
6169
/// Resolve union variant access.
6170
fn resolveUnionVariantAccess(
6171
    self: *mut Resolver,
6172
    node: *ast::Node,
6173
    access: ast::Access,
6174
    unionType: UnionType,
6175
    variantName: *[u8]
6176
) -> *mut Symbol throws (ResolveError) {
6177
    // Look up the variant in the union's nominal type.
6178
    for i in 0..unionType.variants.len {
6179
        let variant = &unionType.variants[i];
6180
        if variant.name == variantName {
6181
            let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data
6182
                else panic "resolveUnionVariantAccess: expected variant symbol";
6183
6184
            // Associate the variant symbol with the child node.
6185
            setNodeSymbol(self, access.child, variant.symbol);
6186
            setNodeSymbol(self, node, variant.symbol);
6187
6188
            // Store the variant index for the lowerer.
6189
            setVariantInfo(self, node, ordinal, index);
6190
6191
            return variant.symbol;
6192
        }
6193
    }
6194
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
6195
}
6196
6197
/// Analyze a scope access expression.
6198
fn resolveScopeAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6199
    throws (ResolveError)
6200
{
6201
    let sym = try resolveAccess(self, node, access, self.scope);
6202
    try checkUnsafeBindingAccess(self, node, sym);
6203
    let mut ty: Type = undefined;
6204
6205
    match sym.data {
6206
        case SymbolData::Value { type, .. } => {
6207
            setNodeSymbol(self, node, sym);
6208
            set ty = type;
6209
        }
6210
        case SymbolData::Constant { type, value } => {
6211
            // Propagate the constant value.
6212
            if let val = value {
6213
                setNodeConstValue(self, node, val);
6214
            }
6215
            setNodeSymbol(self, node, sym);
6216
            set ty = type;
6217
        }
6218
        case SymbolData::Type(t) => {
6219
            setNodeSymbol(self, node, sym);
6220
            set ty = Type::Nominal(t);
6221
        }
6222
        case SymbolData::Variant { index, .. } => {
6223
            let ty = typeFor(self, node)
6224
                else throw emitError(self, node, ErrorKind::Internal);
6225
            // For unions without payload, store the variant index as a constant.
6226
            if isVoidUnion(ty) {
6227
                setNodeConstValue(self, node, ConstValue::Int(ConstInt {
6228
                    magnitude: index as u64,
6229
                    bits: 32,
6230
                    signed: false,
6231
                    negative: false,
6232
                }));
6233
            }
6234
            return setNodeType(self, node, ty);
6235
        }
6236
        case SymbolData::Module { .. } => {
6237
            throw emitError(self, node, ErrorKind::UnexpectedModuleName);
6238
        }
6239
        case SymbolData::Trait(_) => { // Trait names are not values.
6240
            throw emitError(self, node, ErrorKind::UnexpectedTraitName);
6241
        }
6242
    }
6243
    return setNodeType(self, node, ty);
6244
}
6245
6246
/// Analyze a field access expression.
6247
fn resolveFieldAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6248
    throws (ResolveError)
6249
{
6250
    let parentTy = try infer(self, access.parent);
6251
    if isUnsafePointerType(parentTy) {
6252
        try requireUnsafe(self, access.parent);
6253
    }
6254
    let subjectTy = autoDeref(parentTy);
6255
6256
    if let case Type::Slice { class, item, mutable } = subjectTy {
6257
        let fieldNode = access.child;
6258
        let fieldName = try nodeName(self, fieldNode);
6259
        if mem::eq(fieldName, PTR_FIELD) {
6260
            setRecordFieldIndex(self, fieldNode, 0);
6261
            return setNodeType(
6262
                self,
6263
                node,
6264
                Type::Pointer { class, target: item, mutable },
6265
            );
6266
        }
6267
        if mem::eq(fieldName, LEN_FIELD) {
6268
            setRecordFieldIndex(self, fieldNode, 1);
6269
            return setNodeType(self, node, Type::U32);
6270
        }
6271
        if mem::eq(fieldName, CAP_FIELD) {
6272
            setRecordFieldIndex(self, fieldNode, 2);
6273
            return setNodeType(self, node, Type::U32);
6274
        }
6275
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
6276
    }
6277
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
6278
        let fieldName = try nodeName(self, access.child);
6279
        let method = findTraitMethod(traitInfo, fieldName)
6280
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6281
        return setNodeType(self, node, Type::Fn(method.fnType));
6282
    }
6283
6284
    match subjectTy {
6285
        case Type::Nominal(NominalType::Record(recordType)) => {
6286
            let fieldNode = access.child;
6287
            let fieldName = try nodeName(self, fieldNode);
6288
            if let fieldIndex = findRecordField(&recordType, fieldName) {
6289
                let fieldTy = recordType.fields[fieldIndex].fieldType;
6290
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6291
                return setNodeType(self, node, fieldTy);
6292
            }
6293
            // Not a field: check for a standalone method.
6294
            if let method = findMethod(self, subjectTy, fieldName) {
6295
                return setNodeType(self, node, Type::Fn(method.fnType));
6296
            }
6297
            throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6298
        }
6299
        case Type::Array(arrayInfo) => {
6300
            let fieldNode = access.child;
6301
            let fieldName = try nodeName(self, fieldNode);
6302
6303
            if mem::eq(fieldName, LEN_FIELD) {
6304
                let lengthConst = constInt(arrayInfo.length as u64, 32, false, false);
6305
                setNodeConstValue(self, node, lengthConst);
6306
6307
                return setNodeType(self, node, Type::U32);
6308
            }
6309
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
6310
        }
6311
6312
        else => {
6313
            // Check for standalone methods on any nominal type (e.g. unions).
6314
            if let case Type::Nominal(_) = subjectTy {
6315
                let fieldName = try nodeName(self, access.child);
6316
                if let method = findMethod(self, subjectTy, fieldName) {
6317
                    return setNodeType(self, node, Type::Fn(method.fnType));
6318
                }
6319
            }
6320
            throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
6321
        }
6322
    }
6323
}
6324
6325
/// Determine whether an expression can yield a mutable location for borrowing.
6326
fn canBorrowMutFrom(self: *mut Resolver, node: *ast::Node) -> bool
6327
    throws (ResolveError)
6328
{
6329
    match node.value {
6330
        case ast::NodeValue::Ident(name) => {
6331
            let sym = findValueSymbol(self.scope, name)
6332
                else return false;
6333
            let case SymbolData::Value { mutable, .. } = sym.data
6334
                else return false;
6335
            // Check if the binding itself is mutable, or if it's a mutable pointer.
6336
            if mutable {
6337
                return true;
6338
            }
6339
            // Check if the type is a mutable pointer or slice.
6340
            let ty = typeFor(self, node) else return false;
6341
            if let case Type::Pointer { mutable, .. } = ty {
6342
                return mutable;
6343
            }
6344
            if let case Type::Slice { mutable, .. } = ty {
6345
                return mutable;
6346
            }
6347
            return false;
6348
        }
6349
        case ast::NodeValue::FieldAccess(access) => {
6350
            let _ = try infer(self, access.parent);
6351
            return try canBorrowMutFrom(self, access.parent);
6352
        }
6353
        case ast::NodeValue::ScopeAccess(_) => {
6354
            // Module-qualified access to a top-level symbol. A `static`
6355
            // binds as a mutable value; a `constant` does not.
6356
            let _ = try infer(self, node);
6357
            let sym = nodeData(self, node).sym
6358
                else return false;
6359
6360
            if let case SymbolData::Value { mutable, .. } = sym.data {
6361
                return mutable;
6362
            }
6363
            return false;
6364
        }
6365
        case ast::NodeValue::Subscript { container, .. } => {
6366
            let containerTy = try infer(self, container);
6367
            // Subscript auto-derefs pointers, so check the actual indexed type.
6368
            let subjectTy = autoDeref(containerTy);
6369
6370
            if let case Type::Slice { mutable, .. } = subjectTy {
6371
                return mutable;
6372
            }
6373
            if let case Type::Array(_) = subjectTy {
6374
                return try canBorrowMutFrom(self, container);
6375
            }
6376
            return false;
6377
        }
6378
        case ast::NodeValue::ArrayLit(_),
6379
             ast::NodeValue::ArrayRepeatLit(_) =>
6380
        {
6381
            return true;
6382
        }
6383
        case ast::NodeValue::Call(_) => {
6384
            // A call returning `*mut T` (or `&mut [T]`) yields a
6385
            // mutable place. Non-pointer returns cannot be mutably borrowed.
6386
            let ty = try infer(self, node);
6387
            if let case Type::Pointer { mutable, .. } = ty {
6388
                return mutable;
6389
            }
6390
            if let case Type::Slice { mutable, .. } = ty {
6391
                return mutable;
6392
            }
6393
            return false;
6394
        }
6395
        case ast::NodeValue::Deref(inner) => {
6396
            let innerTy = try infer(self, inner);
6397
6398
            if let case Type::Pointer { mutable, .. } = innerTy {
6399
                return mutable;
6400
            }
6401
            if let case Type::Slice { mutable, .. } = innerTy {
6402
                return mutable;
6403
            }
6404
            // Record deref: mutability depends on the inner binding.
6405
            if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy {
6406
                if not recInfo.labeled and recInfo.fields.len == 1 {
6407
                    return try canBorrowMutFrom(self, inner);
6408
                }
6409
            }
6410
            return false;
6411
        }
6412
        else => {
6413
            return false;
6414
        }
6415
    }
6416
}
6417
6418
/// Analyze an address-of expression.
6419
fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
6420
    throws (ResolveError)
6421
{
6422
    // Safe address-of expressions always create call-scoped loans. Trusted
6423
    // implementations may create owners when no loan or raw type is required.
6424
    let class = types::PointerClass::Owned
6425
        if self.unsafeDepth > 0 and not isRefType(hint) and not isUnsafePointerType(hint)
6426
        else types::PointerClass::Ref;
6427
    if addr.mutable {
6428
        if not try canBorrowMutFrom(self, addr.target) {
6429
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6430
        }
6431
    }
6432
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
6433
        if let case ast::NodeValue::Range(range) = index.value {
6434
            let containerTy = try infer(self, container);
6435
            let subjectTy = autoDeref(containerTy);
6436
6437
            try checkSliceRangeIndices(self, range);
6438
6439
            let mut item: *Type = undefined;
6440
            let mut capacity: ?u32 = nil;
6441
6442
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
6443
                if addr.mutable and not sliceMutable {
6444
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6445
                }
6446
                set item = sliceItem;
6447
            } else {
6448
                match subjectTy {
6449
                    case Type::Array(arrayInfo) => {
6450
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
6451
                        set item = arrayInfo.item;
6452
                        set capacity = arrayInfo.length;
6453
                    }
6454
                    else => {
6455
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
6456
                    }
6457
                }
6458
            }
6459
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
6460
            let alloc = allocType(self, sliceTy);
6461
            setSliceRangeInfo(self, node, SliceRangeInfo {
6462
                itemType: item,
6463
                mutable: addr.mutable,
6464
                capacity,
6465
            });
6466
            setNodeType(self, addr.target, *alloc);
6467
            return setNodeType(self, node, *alloc);
6468
        }
6469
    }
6470
    // Derive a hint for the target type from the slice hint.
6471
    let mut targetHint: Type = Type::Unknown;
6472
    if let case Type::Slice { item, .. } = hint {
6473
        set targetHint = Type::Array(ArrayType { item, length: 0 });
6474
    }
6475
    let targetTy = try visit(self, addr.target, targetHint);
6476
6477
    // Mark local variable symbols as address-taken so the lowerer
6478
    // allocates a stack slot eagerly.
6479
    if let case ast::NodeValue::Ident(name) = addr.target.value {
6480
        if let sym = findValueSymbol(self.scope, name) {
6481
            match &mut sym.data {
6482
                case SymbolData::Value { addressTaken, .. } => {
6483
                    set *addressTaken = true;
6484
                }
6485
                else => {}
6486
            }
6487
        }
6488
    }
6489
6490
    if let case Type::Array(arrayInfo) = targetTy {
6491
        match addr.target.value {
6492
            case ast::NodeValue::ArrayLit(_),
6493
                 ast::NodeValue::ArrayRepeatLit(_) =>
6494
            {
6495
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.mutable };
6496
                return setNodeType(self, node, *allocType(self, sliceTy));
6497
            }
6498
            else => {}
6499
        }
6500
    }
6501
    let pointerTy = Type::Pointer {
6502
        class, target: allocType(self, targetTy), mutable: addr.mutable,
6503
    };
6504
    return setNodeType(self, node, pointerTy);
6505
}
6506
6507
/// Analyze a dereference expression.
6508
fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
6509
    throws (ResolveError)
6510
{
6511
    let operandTy = try visit(self, targetNode, hint);
6512
    if let case Type::Pointer { class, target, .. } = operandTy {
6513
        if class == types::PointerClass::Unsafe {
6514
            try requireUnsafe(self, targetNode);
6515
        }
6516
        // Disallow dereferencing opaque pointers.
6517
        if *target == Type::Opaque {
6518
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
6519
        }
6520
        return setNodeType(self, node, *target);
6521
    }
6522
    // Auto-deref for single-field unlabeled records.
6523
    if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy {
6524
        if not recInfo.labeled and recInfo.fields.len == 1 {
6525
            let fieldTy = recInfo.fields[0].fieldType;
6526
            setRecordFieldIndex(self, node, 0);
6527
            return setNodeType(self, node, fieldTy);
6528
        }
6529
    }
6530
    throw emitError(self, targetNode, ErrorKind::ExpectedPointer);
6531
}
6532
6533
/// Check if a type is a pointer to opaque.
6534
fn isOpaquePointer(ty: Type) -> bool {
6535
    if let case Type::Pointer { target, .. } = ty {
6536
        return *target == Type::Opaque;
6537
    }
6538
    return false;
6539
}
6540
6541
/// Check if a type is an opaque slice.
6542
fn isOpaqueSlice(ty: Type) -> bool {
6543
    if let case Type::Slice { item, .. } = ty {
6544
        return *item == Type::Opaque;
6545
    }
6546
    return false;
6547
}
6548
6549
/// Check if an `as` cast between two types is valid.
6550
fn isValidCast(source: Type, target: Type) -> bool {
6551
    // Allow identity casts.
6552
    if source == target {
6553
        return true;
6554
    }
6555
    // Allow numeric to numeric.
6556
    if isNumericType(source) and isNumericType(target) {
6557
        return true;
6558
    }
6559
    // Allow `void` union to numeric.
6560
    // TODO: Check that variant index fits in target type.
6561
    if isVoidUnion(source) and isNumericType(target) {
6562
        return true;
6563
    }
6564
    // Allow address to numeric.
6565
    if let case Type::Slice { .. } = source {
6566
        // Disallow slice to numeric; slices are fat pointers.
6567
    } else if isAddressType(source) and isNumericType(target) {
6568
        return true;
6569
    }
6570
    // Allow pointer casts if one side is `*opaque` or target types are castable.
6571
    if let case Type::Pointer {
6572
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6573
    } = source {
6574
        if let case Type::Pointer {
6575
            class: targetClass, target: targetTarget, mutable: targetMutable,
6576
        } = target {
6577
            if sourceClass <> targetClass {
6578
                return false;
6579
            }
6580
            if targetMutable and not sourceMutable {
6581
                return false;
6582
            }
6583
            if isOpaquePointer(source) or isOpaquePointer(target) {
6584
                return true;
6585
            }
6586
            return isValidCast(*sourceTarget, *targetTarget);
6587
        }
6588
    }
6589
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
6590
    // or element types are castable.
6591
    if let case Type::Slice {
6592
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6593
    } = source {
6594
        if let case Type::Slice {
6595
            class: targetClass, item: targetItem, mutable: targetMutable,
6596
        } = target {
6597
            if sourceClass <> targetClass {
6598
                return false;
6599
            }
6600
            if targetMutable and not sourceMutable {
6601
                return false;
6602
            }
6603
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
6604
                return true;
6605
            }
6606
            if *targetItem == Type::U8 {
6607
                return true;
6608
            }
6609
            return isValidCast(*sourceItem, *targetItem);
6610
        }
6611
    }
6612
    return false;
6613
}
6614
6615
/// Analyze an `as` cast expression.
6616
fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6617
    throws (ResolveError)
6618
{
6619
    let targetTy = try infer(self, expr.type);
6620
    let sourceTy = try visit(self, expr.value, targetTy);
6621
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6622
        try requireUnsafe(self, node);
6623
    }
6624
6625
    assert sourceTy <> Type::Unknown;
6626
    assert targetTy <> Type::Unknown;
6627
6628
    let mut valid = isValidCast(sourceTy, targetTy);
6629
    if let case Type::Pointer {
6630
        class: sourceClass, mutable: sourceMutable, ..
6631
    } = sourceTy {
6632
        if let case Type::Pointer {
6633
            class: targetClass, mutable: targetMutable, ..
6634
        } = targetTy {
6635
            let compatibleClass = sourceClass == targetClass or (
6636
                sourceClass == types::PointerClass::Ref and
6637
                targetClass == types::PointerClass::Unsafe
6638
            );
6639
            if compatibleClass and (not targetMutable or sourceMutable) {
6640
                set valid = true;
6641
            }
6642
        }
6643
    }
6644
    if let case Type::Slice {
6645
        class: sourceClass, mutable: sourceMutable, ..
6646
    } = sourceTy {
6647
        if let case Type::Slice {
6648
            class: targetClass, mutable: targetMutable, ..
6649
        } = targetTy {
6650
            let compatibleClass = sourceClass == targetClass or (
6651
                sourceClass == types::PointerClass::Ref and
6652
                targetClass == types::PointerClass::Unsafe
6653
            );
6654
            if compatibleClass and (not targetMutable or sourceMutable) {
6655
                set valid = true;
6656
            }
6657
        }
6658
    }
6659
    if valid {
6660
        let mut changesRepresentation = false;
6661
        match sourceTy {
6662
            case Type::Pointer { target: sourceTarget, .. } => {
6663
                if let case Type::Pointer { target: targetTarget, .. } = targetTy {
6664
                    set changesRepresentation = not typesEqual(*sourceTarget, *targetTarget);
6665
                }
6666
            }
6667
            case Type::Slice { item: sourceItem, .. } => {
6668
                if let case Type::Slice { item: targetItem, .. } = targetTy {
6669
                    set changesRepresentation = not typesEqual(*sourceItem, *targetItem);
6670
                }
6671
            }
6672
            else => {}
6673
        }
6674
        if changesRepresentation {
6675
            try requireUnsafe(self, node);
6676
        }
6677
        // Propagate the constant value after applying the cast's target-width
6678
        // truncation and signed interpretation.
6679
        if let value = constValueEntry(self, expr.value) {
6680
            if let case ConstValue::Int(i) = value {
6681
                setNodeConstValue(self, node, castConstInt(i, targetTy));
6682
            }
6683
        }
6684
        return setNodeType(self, node, targetTy);
6685
    }
6686
    throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
6687
        from: sourceTy,
6688
        to: targetTy,
6689
    }));
6690
}
6691
6692
/// Analyze a range expression.
6693
fn resolveRange(self: *mut Resolver, node: *ast::Node, range: ast::Range) -> Type
6694
    throws (ResolveError)
6695
{
6696
    let mut start: ?*Type = nil;
6697
    let mut end: ?*Type = nil;
6698
6699
    if let s = range.start {
6700
        let startTy = try checkNumeric(self, s);
6701
6702
        if let e = range.end {
6703
            let endTy = try checkNumeric(self, e);
6704
            let mut resolvedTy = startTy;
6705
6706
            // Infer unsuffixed integer literals from the opposite bound.
6707
            if startTy == Type::Int and endTy <> Type::Int {
6708
                let _ = try checkAssignable(self, s, endTy);
6709
                set resolvedTy = endTy;
6710
            } else if endTy == Type::Int and startTy <> Type::Int {
6711
                let _ = try checkAssignable(self, e, startTy);
6712
                set resolvedTy = startTy;
6713
            } else {
6714
                let _ = try checkAssignable(self, e, startTy);
6715
            }
6716
            set start = allocType(self, resolvedTy);
6717
            set end = allocType(self, resolvedTy);
6718
        } else {
6719
            set start = allocType(self, startTy);
6720
        }
6721
    } else if let e = range.end {
6722
        set end = allocType(self, try checkNumeric(self, e));
6723
    }
6724
    return setNodeType(self, node, Type::Range { start, end });
6725
}
6726
6727
/// Analyze a `try` expression and its handlers.
6728
/// The `expected` type is used to determine if the value is discarded (`Void`)
6729
/// or if the catch expression needs type checking.
6730
fn resolveTry(self: *mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
6731
    throws (ResolveError)
6732
{
6733
    let call = tryExpr.expr;
6734
    let case ast::NodeValue::Call(callExpr) = call.value
6735
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
6736
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try);
6737
6738
    // TODO: It's annoying that we need to re-fetch the function type after
6739
    // analyzing the call.
6740
    let calleeTy = typeFor(self, callExpr.callee)
6741
        else return setNodeType(self, node, resultTy);
6742
    let case Type::Fn(calleeInfo) = calleeTy
6743
        else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
6744
6745
    if calleeInfo.throwList.len == 0 {
6746
        throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
6747
    }
6748
    // If we're not catching the error, nor panicking on error, nor returning
6749
    // optional, then the current function must be able to propagate it.
6750
    let mut tryResultTy = resultTy;
6751
    if tryExpr.returnsOptional {
6752
        // `try?` converts errors to `nil` and wraps the result in an optional.
6753
        if let case Type::Optional(_) = resultTy {
6754
            // Already optional, no wrapping needed.
6755
        } else {
6756
            set tryResultTy = Type::Optional(allocType(self, resultTy));
6757
        }
6758
    } else if tryExpr.catches.len > 0 {
6759
        // `try ... catch` -- one or more catch clauses.
6760
        set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint);
6761
    } else if not tryExpr.shouldPanic {
6762
        let fnInfo = self.currentFn
6763
            else throw emitError(self, node, ErrorKind::TryRequiresThrows);
6764
        if fnInfo.throwList.len == 0 {
6765
            throw emitError(self, node, ErrorKind::TryRequiresThrows);
6766
        }
6767
        // Check that *all* thrown errors of the callee can be propagated by
6768
        // the caller.
6769
        for throwTy in calleeInfo.throwList {
6770
            let mut found = false;
6771
6772
            for callerThrowTy in fnInfo.throwList {
6773
                if callerThrowTy == throwTy {
6774
                    set found = true;
6775
                    break;
6776
                }
6777
            }
6778
            if not found {
6779
                throw emitError(self, node, ErrorKind::TryIncompatibleError);
6780
            }
6781
        }
6782
    }
6783
    return setNodeType(self, node, tryResultTy);
6784
}
6785
6786
/// Check that a `catch` body is assignable to the expected result type, but only
6787
/// in expression context (`hint` is neither `Unknown` nor `Void`).
6788
fn checkCatchBody(self: *mut Resolver, body: *ast::Node, resultTy: Type, hint: Type)
6789
    throws (ResolveError)
6790
{
6791
    if hint <> Type::Unknown and hint <> Type::Void {
6792
        try checkAssignable(self, body, resultTy);
6793
    }
6794
}
6795
6796
/// Resolve catch clauses for a `try ... catch` expression.
6797
///
6798
/// For a single untyped catch (with or without binding), resolves the catch
6799
/// body and returns the result type. Multi-error callees with inferred bindings
6800
/// are rejected; you must use typed catches.
6801
fn resolveTryCatches(
6802
    self: *mut Resolver,
6803
    node: *ast::Node,
6804
    catches: *mut [*ast::Node],
6805
    calleeInfo: *FnType,
6806
    resultTy: Type,
6807
    hint: Type
6808
) -> Type throws (ResolveError) {
6809
    let firstNode = catches[0];
6810
    let case ast::NodeValue::CatchClause(first) = firstNode.value else
6811
        throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode));
6812
6813
    // Typed catches: dispatch to dedicated handler.
6814
    if first.typeNode <> nil {
6815
        return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint);
6816
    }
6817
    // Single untyped catch clause.
6818
    if let binding = first.binding {
6819
        if calleeInfo.throwList.len > 1 {
6820
            throw emitError(self, binding, ErrorKind::TryCatchMultiError);
6821
        }
6822
        enterScope(self, node);
6823
6824
        let errTy = *calleeInfo.throwList[0];
6825
        try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
6826
    }
6827
    try visit(self, first.body, resultTy);
6828
6829
    if let _ = first.binding {
6830
        exitScope(self);
6831
    }
6832
    try checkCatchBody(self, first.body, resultTy, hint);
6833
6834
    return resultTy;
6835
}
6836
6837
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
6838
///
6839
/// Validates that each type annotation is in the callee's throw list, that
6840
/// there are no duplicate catch types, and that the clauses are exhaustive.
6841
fn resolveTypedCatches(
6842
    self: *mut Resolver,
6843
    node: *ast::Node,
6844
    catches: *mut [*ast::Node],
6845
    calleeInfo: *FnType,
6846
    resultTy: Type,
6847
    hint: Type
6848
) -> Type throws (ResolveError) {
6849
    // Track which of the callee's throw types have been covered.
6850
    let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS];
6851
    let mut hasCatchAll = false;
6852
6853
    for clauseNode in catches {
6854
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value else
6855
            throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode));
6856
6857
        if let typeNode = clause.typeNode {
6858
            // Typed catch clause: validate against callee's throw list.
6859
            let errTy = try infer(self, typeNode);
6860
            let mut foundIdx: ?u32 = nil;
6861
6862
            for throwType, j in calleeInfo.throwList {
6863
                if errTy == *throwType {
6864
                    set foundIdx = j;
6865
                    break;
6866
                }
6867
            }
6868
            let idx = foundIdx else {
6869
                throw emitError(self, typeNode, ErrorKind::TryIncompatibleError);
6870
            };
6871
            if covered[idx] {
6872
                throw emitError(self, typeNode, ErrorKind::TryCatchDuplicateType);
6873
            }
6874
            set covered[idx] = true;
6875
6876
            // Bind the error variable if present.
6877
            if let binding = clause.binding {
6878
                enterScope(self, clauseNode);
6879
                try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
6880
            }
6881
        } else {
6882
            // Catch-all clause with no type annotation or binding.
6883
            set hasCatchAll = true;
6884
        }
6885
        // Resolve the catch body and check assignability.
6886
        try visit(self, clause.body, resultTy);
6887
        // Only typed clauses can have bindings.
6888
        if let _ = clause.binding {
6889
            exitScope(self);
6890
        }
6891
        try checkCatchBody(self, clause.body, resultTy, hint);
6892
    }
6893
6894
    // Check exhaustiveness: all callee error types must be covered.
6895
    if not hasCatchAll {
6896
        for i in 0..calleeInfo.throwList.len {
6897
            if not covered[i] {
6898
                throw emitError(self, node, ErrorKind::TryCatchNonExhaustive);
6899
            }
6900
        }
6901
    }
6902
    return resultTy;
6903
}
6904
6905
/// Analyze a `throw` statement.
6906
fn resolveThrow(self: *mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type
6907
    throws (ResolveError)
6908
{
6909
    let fnInfo = self.currentFn
6910
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
6911
    if fnInfo.throwList.len == 0 {
6912
        throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
6913
    }
6914
    let throwTy = try infer(self, expr);
6915
    for errTy in fnInfo.throwList {
6916
        if let coerce = isAssignable(self, *errTy, throwTy, expr) {
6917
            setNodeCoercion(self, expr, coerce);
6918
            return setNodeType(self, node, Type::Never);
6919
        }
6920
    }
6921
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
6922
}
6923
6924
/// Analyze a `return` statement.
6925
fn resolveReturn(self: *mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type
6926
    throws (ResolveError)
6927
{
6928
    let f = self.currentFn
6929
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
6930
    let expected = *f.returnType;
6931
6932
    if let val = retVal {
6933
        let _actualTy = try checkAssignable(self, val, expected);
6934
    } else if expected <> Type::Void {
6935
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void });
6936
    }
6937
    // In throwing functions, return values are wrapped in the success variant.
6938
    if f.throwList.len > 0 {
6939
        setNodeCoercion(self, node, Coercion::ResultWrap);
6940
    }
6941
    return setNodeType(self, node, Type::Never);
6942
}
6943
6944
/// Convert a [`ConstInt`] to its two's-complement bit pattern.
6945
fn constIntToBits(c: ConstInt) -> u64 {
6946
    return (0 - c.magnitude) if c.negative else c.magnitude;
6947
}
6948
6949
/// Convert a [`ConstInt`] to its signed two's-complement representation.
6950
fn constIntToSigned(c: ConstInt) -> i64 {
6951
    return constIntToBits(c) as i64;
6952
}
6953
6954
/// Build a [`ConstInt`] from a signed result, preserving bit width and signedness.
6955
fn constIntFromSigned(value: i64, bits: u8, signed: bool) -> ConstInt {
6956
    if value < 0 {
6957
        // Compute magnitude without signed overflow.
6958
        let uval = value as u64;
6959
        return ConstInt {
6960
            magnitude: 0 - uval,
6961
            bits,
6962
            signed,
6963
            negative: true,
6964
        };
6965
    }
6966
    return ConstInt {
6967
        magnitude: value as u64,
6968
        bits,
6969
        signed,
6970
        negative: false,
6971
    };
6972
}
6973
6974
/// Build a [`ConstInt`] from a two's-complement bit pattern.
6975
fn constIntFromBits(raw: u64, bits: u8, signed: bool) -> ConstInt {
6976
    let mask = parser::U64_MAX if bits == 64 else parser::U64_MAX >> (64 - bits) as u64;
6977
    let truncated = raw & mask;
6978
6979
    if signed {
6980
        let signBit = (mask >> 1) + 1;
6981
        if (truncated & signBit) <> 0 {
6982
            return ConstInt {
6983
                magnitude: (0 - truncated) & mask,
6984
                bits,
6985
                signed,
6986
                negative: true,
6987
            };
6988
        }
6989
    }
6990
    return ConstInt { magnitude: truncated, bits, signed, negative: false };
6991
}
6992
6993
/// Try to fold a binary operation on two integer constants.
6994
/// Returns the resulting constant value if successful.
6995
fn foldIntBinOp(op: ast::BinaryOp, left: ConstInt, right: ConstInt) -> ?ConstValue {
6996
    // Use the wider bit width and propagate signedness.
6997
    let mut bits = left.bits;
6998
    if right.bits > bits {
6999
        set bits = right.bits;
7000
    }
7001
    let signed = left.signed or right.signed;
7002
    let l = constIntToSigned(left);
7003
    let r = constIntToSigned(right);
7004
7005
    match op {
7006
        // Shift counts are masked to the left operand's width, matching
7007
        // the runtime word instructions.
7008
        case ast::BinaryOp::Shl => {
7009
            let raw = constIntToBits(left);
7010
            let shamt = constIntToBits(right) % left.bits as u64;
7011
            return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed));
7012
        },
7013
        case ast::BinaryOp::Shr => {
7014
            let shamt = constIntToBits(right) % left.bits as u64;
7015
            if left.signed {
7016
                let shifted = constIntToSigned(left) >> shamt as i64;
7017
                return ConstValue::Int(
7018
                    constIntFromBits(shifted as u64, left.bits, true)
7019
                );
7020
            }
7021
            return ConstValue::Int(
7022
                constIntFromBits(left.magnitude >> shamt, left.bits, false)
7023
            );
7024
        },
7025
        case ast::BinaryOp::Eq  => return ConstValue::Bool(l == r),
7026
        case ast::BinaryOp::Ne  => return ConstValue::Bool(l <> r),
7027
        case ast::BinaryOp::Lt =>
7028
            return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude),
7029
        case ast::BinaryOp::Gt =>
7030
            return ConstValue::Bool(l > r if signed else left.magnitude > right.magnitude),
7031
        case ast::BinaryOp::Lte =>
7032
            return ConstValue::Bool(l <= r if signed else left.magnitude <= right.magnitude),
7033
        case ast::BinaryOp::Gte =>
7034
            return ConstValue::Bool(l >= r if signed else left.magnitude >= right.magnitude),
7035
        case ast::BinaryOp::Add => return ConstValue::Int(constIntFromSigned(l + r, bits, signed)),
7036
        case ast::BinaryOp::Sub => return ConstValue::Int(constIntFromSigned(l - r, bits, signed)),
7037
        case ast::BinaryOp::Mul => return ConstValue::Int(constIntFromSigned(l * r, bits, signed)),
7038
        case ast::BinaryOp::Div => {
7039
            if signed {
7040
                if r == 0 {
7041
                    return nil;
7042
                }
7043
                return ConstValue::Int(constIntFromSigned(l / r, bits, true));
7044
            }
7045
            if right.magnitude == 0 {
7046
                return nil;
7047
            }
7048
            return constInt(left.magnitude / right.magnitude, bits, false, false);
7049
        },
7050
        case ast::BinaryOp::Mod => {
7051
            if signed {
7052
                if r == 0 {
7053
                    return nil;
7054
                }
7055
                return ConstValue::Int(constIntFromSigned(l % r, bits, true));
7056
            }
7057
            if right.magnitude == 0 {
7058
                return nil;
7059
            }
7060
            return constInt(left.magnitude % right.magnitude, bits, false, false);
7061
        },
7062
        case ast::BinaryOp::BitAnd => return ConstValue::Int(constIntFromSigned(l & r, bits, signed)),
7063
        case ast::BinaryOp::BitOr  => return ConstValue::Int(constIntFromSigned(l | r, bits, signed)),
7064
        case ast::BinaryOp::BitXor => return ConstValue::Int(constIntFromSigned(l ^ r, bits, signed)),
7065
        else => return nil,
7066
    }
7067
}
7068
7069
/// Try to constant-fold a binary operation on two resolved operands.
7070
/// Only folds when the result type is concrete.
7071
fn tryFoldBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
7072
    let leftVal = constValueEntry(self, binop.left)
7073
        else return;
7074
    let rightVal = constValueEntry(self, binop.right)
7075
        else return;
7076
7077
    // Fold integer binary ops.
7078
    if let case ConstValue::Int(leftInt) = leftVal {
7079
        if let case ConstValue::Int(rightInt) = rightVal {
7080
            if let result = foldIntBinOp(binop.op, leftInt, rightInt) {
7081
                setNodeConstValue(self, node, result);
7082
            }
7083
            return;
7084
        }
7085
    }
7086
7087
    // Fold boolean binary ops.
7088
    if let case ConstValue::Bool(l) = leftVal {
7089
        if let case ConstValue::Bool(r) = rightVal {
7090
            match binop.op {
7091
                case ast::BinaryOp::And => setNodeConstValue(self, node, ConstValue::Bool(l and r)),
7092
                case ast::BinaryOp::Or => setNodeConstValue(self, node, ConstValue::Bool(l or r)),
7093
                case ast::BinaryOp::Eq => setNodeConstValue(self, node, ConstValue::Bool(l == r)),
7094
                case ast::BinaryOp::Ne,
7095
                     ast::BinaryOp::Xor => setNodeConstValue(self, node, ConstValue::Bool(l <> r)),
7096
                else => {}
7097
            }
7098
        }
7099
    }
7100
}
7101
7102
/// Analyze a binary expression.
7103
fn resolveBinOp(self: *mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type
7104
    throws (ResolveError)
7105
{
7106
    let mut resultTy = Type::Unknown;
7107
7108
    match binop.op {
7109
        case ast::BinaryOp::And,
7110
             ast::BinaryOp::Or,
7111
             ast::BinaryOp::Xor =>
7112
        {
7113
            try checkBoolean(self, binop.left);
7114
            try checkBoolean(self, binop.right);
7115
7116
            set resultTy = Type::Bool;
7117
        },
7118
        case ast::BinaryOp::Eq,
7119
             ast::BinaryOp::Ne =>
7120
        {
7121
            let leftTy = try infer(self, binop.left);
7122
            let rightTy = try visit(self, binop.right, leftTy);
7123
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
7124
                try requireUnsafe(self, node);
7125
            }
7126
7127
            if not isComparable(leftTy, rightTy) {
7128
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
7129
                    expected: leftTy,
7130
                    actual: rightTy,
7131
                });
7132
            }
7133
            // When comparing `T == ?T`, record a coercion on the
7134
            // non-optional side so the lowerer lifts it before comparing.
7135
            // We use the already-optional type from the other side rather than
7136
            // constructing a new optional, so that e.g. `?u8 == 42` coerces
7137
            // `42` to `?u8` (not `?i32`). We also record OptionalLift directly
7138
            // rather than using expectAssignable, because comparisons should
7139
            // allow e.g. `?*mut T == *T` where mutability differs.
7140
            if let case Type::Optional(_) = leftTy {
7141
                if not isOptionalType(rightTy) {
7142
                    setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy));
7143
                }
7144
            } else if let case Type::Optional(_) = rightTy {
7145
                setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy));
7146
            }
7147
            set resultTy = Type::Bool;
7148
        },
7149
        else => {
7150
            // Check for pointer arithmetic before numeric check.
7151
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
7152
                let leftTy = try infer(self, binop.left);
7153
                let rightTy = try visit(self, binop.right, leftTy);
7154
7155
                // Allow arithmetic on owning pointers and unsafe pointers, but
7156
                // never on references.
7157
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
7158
                    if *leftTarget == Type::Opaque {
7159
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7160
                    }
7161
                    if leftClass <> types::PointerClass::Ref
7162
                        and isNumericType(rightTy)
7163
                    {
7164
                        try requireUnsafe(self, node);
7165
                        return setNodeType(self, node, leftTy);
7166
                    }
7167
                }
7168
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
7169
                    if *rightTarget == Type::Opaque {
7170
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7171
                    }
7172
                    if binop.op == ast::BinaryOp::Add
7173
                        and rightClass <> types::PointerClass::Ref
7174
                        and isNumericType(leftTy)
7175
                    {
7176
                        try requireUnsafe(self, node);
7177
                        return setNodeType(self, node, rightTy);
7178
                    }
7179
                }
7180
            }
7181
            let leftTy = try checkNumeric(self, binop.left);
7182
            let rightTy = try checkNumeric(self, binop.right);
7183
7184
            let mut operandTy = leftTy;
7185
            if leftTy <> rightTy {
7186
                if leftTy == Type::Int {
7187
                    set operandTy = rightTy;
7188
                } else if rightTy <> Type::Int {
7189
                    throw emitTypeMismatch(self, binop.right, TypeMismatch {
7190
                        expected: leftTy,
7191
                        actual: rightTy,
7192
                    });
7193
                }
7194
            }
7195
7196
            // Ordering comparisons return `bool`, not the operand type.
7197
            match binop.op {
7198
                case ast::BinaryOp::Lt, ast::BinaryOp::Gt,
7199
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
7200
                    set resultTy = Type::Bool,
7201
                else =>
7202
                    set resultTy = operandTy,
7203
            }
7204
7205
        }
7206
    };
7207
    // Try constant folding after both operands are resolved.
7208
    tryFoldBinOp(self, node, binop, resultTy);
7209
7210
    return setNodeType(self, node, resultTy);
7211
}
7212
7213
/// Analyze a unary expression.
7214
fn resolveUnOp(self: *mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type
7215
    throws (ResolveError)
7216
{
7217
    let mut resultTy = Type::Unknown;
7218
7219
    match unop.op {
7220
        case ast::UnaryOp::Not => {
7221
            set resultTy = try checkBoolean(self, unop.value);
7222
            if let value = constValueEntry(self, unop.value) {
7223
                if let case ConstValue::Bool(val) = value {
7224
                    setNodeConstValue(self, node, ConstValue::Bool(not val));
7225
                }
7226
            }
7227
        },
7228
        case ast::UnaryOp::Neg => {
7229
            // TODO: Check that we're allowed to use `-` here? Should negation
7230
            // only be valid for signed integers?
7231
            set resultTy = try checkNumeric(self, unop.value);
7232
            if let value = constValueEntry(self, unop.value) {
7233
                // Get the constant expression for the value, flip the sign,
7234
                // and store that new expression on the unary op node.
7235
                if let case ConstValue::Int(intVal) = value {
7236
                    setNodeConstValue(
7237
                        self,
7238
                        node,
7239
                        constInt(intVal.magnitude, intVal.bits, true, not intVal.negative)
7240
                    );
7241
                }
7242
            }
7243
        },
7244
        case ast::UnaryOp::BitNot => {
7245
            set resultTy = try checkNumeric(self, unop.value);
7246
            if let value = constValueEntry(self, unop.value) {
7247
                if let case ConstValue::Int(intVal) = value {
7248
                    let signed = constIntToSigned(intVal);
7249
                    let inverted = constIntFromSigned(-(signed + 1), intVal.bits, intVal.signed);
7250
                    setNodeConstValue(self, node, ConstValue::Int(inverted));
7251
                }
7252
            }
7253
        },
7254
    };
7255
    return setNodeType(self, node, resultTy);
7256
}
7257
7258
/// Resolve a type signature node and set its type.
7259
fn inferTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7260
    throws (ResolveError)
7261
{
7262
    let resolved = try resolveTypeSig(self, node, sig);
7263
7264
    return setNodeType(self, node, resolved);
7265
}
7266
7267
/// Convert a type signature node into a type value.
7268
fn resolveTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7269
    throws (ResolveError)
7270
{
7271
    match sig {
7272
        case ast::TypeSig::Void => {
7273
            return Type::Void;
7274
        }
7275
        case ast::TypeSig::Opaque => {
7276
            return Type::Opaque;
7277
        }
7278
        case ast::TypeSig::Bool => {
7279
            return Type::Bool;
7280
        }
7281
        case ast::TypeSig::Integer { width, sign } => {
7282
            let u = sign == ast::Signedness::Unsigned;
7283
            match width {
7284
                case 1 => return Type::U8 if u else Type::I8,
7285
                case 2 => return Type::U16 if u else Type::I16,
7286
                case 4 => return Type::U32 if u else Type::I32,
7287
                case 8 => return Type::U64 if u else Type::I64,
7288
                else => {
7289
                    panic "resolveTypeSig: invalid integer width";
7290
                }
7291
            }
7292
        }
7293
        case ast::TypeSig::Array { itemType, length } => {
7294
            let item = try infer(self, itemType);
7295
            let length = try checkSizeInt(self, length);
7296
7297
            return Type::Array(ArrayType { item: allocType(self, item), length });
7298
        }
7299
        case ast::TypeSig::Slice { class, itemType, mutable } => {
7300
            let item = try infer(self, itemType);
7301
            return Type::Slice {
7302
                class,
7303
                item: allocType(self, item),
7304
                mutable,
7305
            };
7306
        }
7307
        case ast::TypeSig::Pointer { class, valueType, mutable } => {
7308
            let target = try infer(self, valueType);
7309
            return Type::Pointer {
7310
                class,
7311
                target: allocType(self, target),
7312
                mutable,
7313
            };
7314
        }
7315
        case ast::TypeSig::Optional { valueType } => {
7316
            let payload = try infer(self, valueType);
7317
            return Type::Optional(allocType(self, payload));
7318
        }
7319
        case ast::TypeSig::Nominal(name) => {
7320
            let ty = try resolveTypeName(self, name);
7321
            return Type::Nominal(ty);
7322
        }
7323
        case ast::TypeSig::Record { fields, labeled } => {
7324
            let recordType = try resolveRecordFields(self, node, fields, labeled);
7325
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
7326
            return Type::Nominal(nominalTy);
7327
        }
7328
        case ast::TypeSig::Fn(t) => {
7329
            let a = alloc::arenaAllocator(&mut self.arena);
7330
            let mut paramTypes: *mut [*Type] = &mut [];
7331
            let mut throwList: *mut [*Type] = &mut [];
7332
7333
            if t.params.len > MAX_FN_PARAMS {
7334
                throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
7335
                    expected: MAX_FN_PARAMS,
7336
                    actual: t.params.len,
7337
                }));
7338
            }
7339
            if t.throwList.len > MAX_FN_THROWS {
7340
                throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
7341
                    expected: MAX_FN_THROWS,
7342
                    actual: t.throwList.len,
7343
                }));
7344
            }
7345
7346
            for paramNode in t.params {
7347
                let paramTy = try resolveValueType(self, paramNode);
7348
                paramTypes.append(allocType(self, paramTy), a);
7349
            }
7350
            for tyNode in t.throwList {
7351
                let throwTy = try resolveValueType(self, tyNode);
7352
                try ensureStorableType(self, tyNode, throwTy);
7353
                throwList.append(allocType(self, throwTy), a);
7354
            }
7355
            let mut retType = allocType(self, Type::Void);
7356
            if let ret = t.returnType {
7357
                let resolvedRet = try resolveValueType(self, ret);
7358
                try ensureStorableType(self, ret, resolvedRet);
7359
                set retType = allocType(self, resolvedRet);
7360
            }
7361
            let fnType = FnType {
7362
                paramTypes: &paramTypes[..],
7363
                returnType: retType,
7364
                throwList: &throwList[..],
7365
                isUnsafe: false,
7366
                localCount: 0,
7367
            };
7368
            return Type::Fn(allocFnType(self, fnType));
7369
        }
7370
        // Resolve an opaque trait object signature.
7371
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
7372
            let sym = try resolveNamePath(self, traitName);
7373
            let case SymbolData::Trait(traitInfo) = sym.data
7374
                else throw emitError(self, traitName, ErrorKind::Internal);
7375
            setNodeSymbol(self, traitName, sym);
7376
7377
            return Type::TraitObject { class, traitInfo, mutable };
7378
        }
7379
    }
7380
}
7381
7382
/// Check if a type can be used for inferrence.
7383
fn isTypeInferrable(type: Type) -> bool {
7384
    if let case Type::Pointer { target, .. } = type {
7385
        return isTypeInferrable(*target);
7386
    }
7387
    match type {
7388
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
7389
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
7390
        case Type::Optional(opt) => return isTypeInferrable(*opt),
7391
        else => return true,
7392
    }
7393
}
7394
7395
/// Analyze a standalone expression by wrapping it in a synthetic function.
7396
export fn resolveExpr(
7397
    self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena
7398
) -> Diagnostics throws (ResolveError) {
7399
    let a = alloc::arenaAllocator(&mut arena.arena);
7400
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
7401
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
7402
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
7403
7404
    let case ast::NodeValue::Block(block) = module.modBody.value
7405
        else panic "resolveExpr: expected block for module body";
7406
    enterScope(self, module.modBody);
7407
    try resolveModuleDecls(self, &block) catch {
7408
        return Diagnostics { errors: self.errors };
7409
    };
7410
    try resolveModuleDefs(self, &block) catch {
7411
        return Diagnostics { errors: self.errors };
7412
    };
7413
    exitScope(self);
7414
7415
    return Diagnostics { errors: self.errors };
7416
}
7417
7418
/// Analyze a parsed module root, ie. a block of top-level statements.
7419
export fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7420
    let case ast::NodeValue::Block(block) = root.value
7421
        else panic "resolveModuleRoot: expected block for module root";
7422
7423
    enterScope(self, root);
7424
    try resolveModuleDecls(self, &block) catch {
7425
        return Diagnostics { errors: self.errors };
7426
    };
7427
    try resolveModuleDefs(self, &block) catch {
7428
        return Diagnostics { errors: self.errors };
7429
    };
7430
    exitScope(self);
7431
    setNodeType(self, root, Type::Void);
7432
7433
    return Diagnostics { errors: self.errors };
7434
}
7435
7436
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
7437
/// and scopes for them, and also binds type names in each module so that cross-module
7438
/// type references work regardless of declaration order.
7439
fn resolveModuleGraph(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7440
    try bindTypeNames(self, block);
7441
7442
    for node in block.statements {
7443
        if let case ast::NodeValue::Mod(decl) = node.value {
7444
            try resolveModGraph(self, node, decl);
7445
        }
7446
    }
7447
}
7448
7449
/// Bind all type names in a module.
7450
/// Skips declarations that have already been bound.
7451
fn bindTypeNames(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7452
    for node in block.statements {
7453
        match node.value {
7454
            case ast::NodeValue::RecordDecl(decl) => {
7455
                if symbolFor(self, node) == nil {
7456
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7457
                }
7458
            }
7459
            case ast::NodeValue::UnionDecl(decl) => {
7460
                if symbolFor(self, node) == nil {
7461
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7462
                }
7463
            }
7464
            case ast::NodeValue::TraitDecl { name, attrs, .. } => {
7465
                if symbolFor(self, node) == nil {
7466
                    try bindTraitName(self, node, name, attrs) catch {};
7467
                }
7468
            }
7469
            else => {}
7470
        }
7471
    }
7472
}
7473
7474
/// Resolve all type bodies in a module.
7475
fn resolveTypeBodies(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7476
    for node in block.statements {
7477
        match node.value {
7478
            case ast::NodeValue::RecordDecl(decl) => {
7479
                try resolveRecordBody(self, node, decl) catch {
7480
                    // Continue resolving other types even if one fails.
7481
                };
7482
            }
7483
            case ast::NodeValue::UnionDecl(decl) => {
7484
                try resolveUnionBody(self, node, decl) catch {
7485
                    // Continue resolving other types even if one fails.
7486
                };
7487
            }
7488
            case ast::NodeValue::TraitDecl { supertraits, methods, .. } => {
7489
                try resolveTraitBody(self, node, supertraits, methods) catch {
7490
                    // Continue resolving other types even if one fails.
7491
                };
7492
            }
7493
            else => {
7494
                // Ignore other declarations.
7495
            }
7496
        }
7497
    }
7498
}
7499
7500
/// Analyze module declarations. This pass processes all top-level statements. When it hits
7501
/// a `mod` statement, it recurses inside the module, analyzing its statements. Module import
7502
/// statements (`use`) are processed here, and make use of the module graph established in the
7503
/// previous pass.
7504
///
7505
/// This function uses a two-phase approach:
7506
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
7507
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
7508
fn resolveModuleDecls(res: *mut Resolver, block: *ast::Block) throws (ResolveError) {
7509
    // Phase 1: Bind all type names as placeholders.
7510
    try bindTypeNames(res, block);
7511
    // Phase 2: Process imports so names available from the module graph can
7512
    // be used in function signatures.
7513
    for node in block.statements {
7514
        if let case ast::NodeValue::Use(decl) = node.value {
7515
            try resolveUse(res, node, decl);
7516
        }
7517
    }
7518
    // Phase 3: Bind function signatures so that function references are
7519
    // available in constant and static initializers.
7520
    for node in block.statements {
7521
        if let case ast::NodeValue::FnDecl(decl) = node.value {
7522
            try resolveFnDecl(res, node, decl);
7523
        }
7524
    }
7525
    // Phase 4: Process constants before submodules, so that child modules
7526
    // can reference parent constants via `super::`.
7527
    for node in block.statements {
7528
        if let case ast::NodeValue::ConstDecl(_) = node.value {
7529
            try infer(res, node);
7530
        }
7531
    }
7532
    // Phase 5: Process submodule declarations -- recurses into child modules.
7533
    // Child modules may trigger on-demand type resolution via
7534
    // [`ensureNominalResolved`] which switches to the declaring module's
7535
    // scope.
7536
    for node in block.statements {
7537
        if let case ast::NodeValue::Mod(decl) = node.value {
7538
            try resolveModDecl(res, node, decl);
7539
        }
7540
    }
7541
    // Phase 5b: Process wildcard imports after submodules are resolved,
7542
    // so that transitive re-exports (export use foo::*) are visible.
7543
    for node in block.statements {
7544
        if let case ast::NodeValue::Use(decl) = node.value {
7545
            if decl.wildcard {
7546
                try resolveUse(res, node, decl);
7547
            }
7548
        }
7549
    }
7550
    // Phase 6: Resolve type bodies (record fields, union variants).
7551
    try resolveTypeBodies(res, block);
7552
    // Phase 7: Process all other declarations (statics, etc.).
7553
    for stmt in block.statements {
7554
        try visitDecl(res, stmt);
7555
    }
7556
}
7557
7558
/// Find a tracked binding by symbol identity.
7559
fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 {
7560
    for i in 0..env.len {
7561
        if let bound = env.symbols[i]; bound == sym {
7562
            return i;
7563
        }
7564
    }
7565
    return nil;
7566
}
7567
7568
/// Return whether a tracked binding is still available.
7569
fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool {
7570
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7571
}
7572
7573
/// Add a local binding when its resolved type is linear.
7574
fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node)
7575
    throws (ResolveError)
7576
{
7577
    let sym = symbolFor(checker.resolver, node) else return;
7578
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7579
    if not isLinear(ty) {
7580
        return;
7581
    }
7582
    if env.len >= MAX_LINEAR_BINDINGS {
7583
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7584
    }
7585
    set env.symbols[env.len] = sym;
7586
    set env.available |= (1 as u64) << (env.len as u64);
7587
    set env.len += 1;
7588
}
7589
7590
/// Require all bindings introduced after `start` to have been consumed.
7591
fn finishLinearScope(
7592
    checker: *mut LinearChecker,
7593
    env: *mut LinearEnv,
7594
    start: u32,
7595
) throws (ResolveError) {
7596
    if not env.terminated {
7597
        for i in start..env.len {
7598
            if linearBindingAvailable(env, i) {
7599
                let sym = env.symbols[i] else panic "finishLinearScope: missing symbol";
7600
                throw emitError(
7601
                    checker.resolver,
7602
                    sym.node,
7603
                    ErrorKind::LinearNotConsumed(sym.name),
7604
                );
7605
            }
7606
        }
7607
    }
7608
    set env.len = start;
7609
}
7610
7611
/// Mark a tracked identifier as consumed.
7612
fn consumeLinearIdent(env: *mut LinearEnv, index: u32) {
7613
    set env.available &= ~((1 as u64) << (index as u64));
7614
}
7615
7616
/// Check that a tracked identifier is available for its requested use.
7617
fn checkLinearIdent(
7618
    checker: *mut LinearChecker,
7619
    env: *mut LinearEnv,
7620
    node: *ast::Node,
7621
    usage: LinearUse,
7622
) throws (ResolveError) {
7623
    if usage == LinearUse::Place {
7624
        return;
7625
    }
7626
    let sym = symbolFor(checker.resolver, node) else return;
7627
    let index = findLinearBinding(env, sym) else return;
7628
    if not linearBindingAvailable(env, index) {
7629
        throw emitError(
7630
            checker.resolver,
7631
            node,
7632
            ErrorKind::LinearUseAfterConsume(sym.name),
7633
        );
7634
    }
7635
    if usage == LinearUse::Consume {
7636
        consumeLinearIdent(env, index);
7637
    }
7638
}
7639
7640
/// Verify that two live branches agree on every outer binding.
7641
fn joinLinearBranches(
7642
    checker: *mut LinearChecker,
7643
    env: *mut LinearEnv,
7644
    left: LinearEnv,
7645
    right: LinearEnv,
7646
    node: *ast::Node,
7647
) throws (ResolveError) {
7648
    if left.terminated and right.terminated {
7649
        set *env = left;
7650
        set env.terminated = true;
7651
        return;
7652
    }
7653
    if left.terminated {
7654
        set *env = right;
7655
        return;
7656
    }
7657
    if right.terminated {
7658
        set *env = left;
7659
        return;
7660
    }
7661
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7662
    for i in 0..left.len {
7663
        if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) {
7664
            let sym = left.symbols[i] else panic "joinLinearBranches: missing symbol";
7665
            throw emitError(
7666
                checker.resolver,
7667
                node,
7668
                ErrorKind::LinearBranchMismatch(sym.name),
7669
            );
7670
        }
7671
    }
7672
    set *env = left;
7673
}
7674
7675
/// Require all current bindings to be consumed at a function exit.
7676
fn finishLinearExit(
7677
    checker: *mut LinearChecker,
7678
    env: *mut LinearEnv,
7679
) throws (ResolveError) {
7680
    for i in 0..env.len {
7681
        if linearBindingAvailable(env, i) {
7682
            let sym = env.symbols[i] else panic "finishLinearExit: missing symbol";
7683
            throw emitError(
7684
                checker.resolver,
7685
                sym.node,
7686
                ErrorKind::LinearNotConsumed(sym.name),
7687
            );
7688
        }
7689
    }
7690
    set env.terminated = true;
7691
}
7692
7693
/// Find the root borrowed or consumed by an argument expression.
7694
fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7695
    match node.value {
7696
        case ast::NodeValue::Ident(_),
7697
             ast::NodeValue::ScopeAccess(_) => return symbolFor(self, node),
7698
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7699
        case ast::NodeValue::FieldAccess(access) =>
7700
            return linearRootSymbol(self, access.parent),
7701
        case ast::NodeValue::Subscript { container, .. } =>
7702
            return linearRootSymbol(self, container),
7703
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
7704
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
7705
        case ast::NodeValue::Unsafe(body) => return linearRootSymbol(self, body),
7706
        else => return nil,
7707
    }
7708
}
7709
7710
/// Reject an access that overlaps a loan active in an enclosing call.
7711
fn checkCallLoanConflicts(
7712
    checker: *mut LinearChecker,
7713
    node: *ast::Node,
7714
    exclusive: bool,
7715
) throws (ResolveError) {
7716
    let root = linearRootSymbol(checker.resolver, node) else return;
7717
    let mut cursor = checker.loans;
7718
    while let loans = cursor {
7719
        for i in 0..loans.len {
7720
            if let previous = loans.roots[i];
7721
                previous == root and (loans.exclusive[i] or exclusive)
7722
            {
7723
                throw emitError(
7724
                    checker.resolver,
7725
                    node,
7726
                    ErrorKind::BorrowConflict(root.name),
7727
                );
7728
            }
7729
        }
7730
        set cursor = loans.parent;
7731
    }
7732
}
7733
7734
/// Add the value identifiers introduced by a pattern.
7735
fn addLinearPatternBindings(
7736
    checker: *mut LinearChecker,
7737
    env: *mut LinearEnv,
7738
    pattern: *ast::Node,
7739
) throws (ResolveError) {
7740
    match pattern.value {
7741
        case ast::NodeValue::Ident(_) => try addLinearBinding(checker, env, pattern),
7742
        case ast::NodeValue::Call(call) => {
7743
            for arg in call.args {
7744
                try addLinearPatternBindings(checker, env, arg);
7745
            }
7746
        }
7747
        case ast::NodeValue::RecordLit(lit) => {
7748
            for fieldNode in lit.fields {
7749
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7750
                    else panic "addLinearPatternBindings: expected field";
7751
                try addLinearPatternBindings(checker, env, field.value);
7752
            }
7753
        }
7754
        case ast::NodeValue::ArrayLit(items) => {
7755
            for item in items {
7756
                try addLinearPatternBindings(checker, env, item);
7757
            }
7758
        }
7759
        else => {}
7760
    }
7761
}
7762
7763
/// Check a lexical block and exact-use of locals introduced in it.
7764
fn checkLinearBlock(
7765
    checker: *mut LinearChecker,
7766
    env: *mut LinearEnv,
7767
    node: *ast::Node,
7768
) throws (ResolveError) {
7769
    let start = env.len;
7770
    let case ast::NodeValue::Block(block) = node.value
7771
        else panic "checkLinearBlock: expected block";
7772
    for stmt in block.statements {
7773
        if env.terminated {
7774
            break;
7775
        }
7776
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
7777
    }
7778
    try finishLinearScope(checker, env, start);
7779
}
7780
7781
/// Push a repeated-control-flow boundary.
7782
fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) {
7783
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
7784
    let depth = checker.loopDepth;
7785
    set checker.loopMarks[depth] = env.len;
7786
    set checker.loopAvailable[depth] = env.available;
7787
    set checker.loopExitAvailable[depth] = env.available;
7788
    set checker.loopHasNaturalExit[depth] = false;
7789
    set checker.loopBreakSeen[depth] = false;
7790
    set checker.loopDepth += 1;
7791
}
7792
7793
/// Require a repeated body's outer bindings to match its entry state.
7794
fn checkLinearLoopBackEdge(
7795
    checker: *mut LinearChecker,
7796
    env: *LinearEnv,
7797
    node: *ast::Node,
7798
) throws (ResolveError) {
7799
    if env.terminated {
7800
        return;
7801
    }
7802
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
7803
    let depth = checker.loopDepth - 1;
7804
    let mark = checker.loopMarks[depth];
7805
    let entryAvailable = checker.loopAvailable[depth];
7806
    for i in 0..mark {
7807
        let bit = (1 as u64) << (i as u64);
7808
        if (env.available & bit) <> (entryAvailable & bit) {
7809
            let sym = env.symbols[i] else panic "checkLinearLoopBackEdge: missing symbol";
7810
            throw emitError(
7811
                checker.resolver,
7812
                node,
7813
                ErrorKind::LinearBranchMismatch(sym.name),
7814
            );
7815
        }
7816
    }
7817
}
7818
7819
/// Record the ownership state of a loop's condition-false exit.
7820
fn setLinearLoopNaturalExit(checker: *mut LinearChecker, env: *LinearEnv) {
7821
    assert checker.loopDepth > 0, "linear loop exit outside loop";
7822
    let depth = checker.loopDepth - 1;
7823
    set checker.loopExitAvailable[depth] = env.available;
7824
    set checker.loopHasNaturalExit[depth] = true;
7825
}
7826
7827
/// Require a break exit to agree with every other exit from this loop.
7828
fn checkLinearLoopBreak(
7829
    checker: *mut LinearChecker,
7830
    env: *LinearEnv,
7831
    node: *ast::Node,
7832
) throws (ResolveError) {
7833
    assert checker.loopDepth > 0, "linear loop break outside loop";
7834
    let depth = checker.loopDepth - 1;
7835
    let mark = checker.loopMarks[depth];
7836
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
7837
        let expected = checker.loopExitAvailable[depth];
7838
        for i in 0..mark {
7839
            let bit = (1 as u64) << (i as u64);
7840
            if (env.available & bit) <> (expected & bit) {
7841
                let sym = env.symbols[i] else panic "checkLinearLoopBreak: missing symbol";
7842
                throw emitError(
7843
                    checker.resolver,
7844
                    node,
7845
                    ErrorKind::LinearBranchMismatch(sym.name),
7846
                );
7847
            }
7848
        }
7849
    } else {
7850
        set checker.loopExitAvailable[depth] = env.available;
7851
    }
7852
    set checker.loopBreakSeen[depth] = true;
7853
}
7854
7855
/// Pop a repeated-control-flow boundary.
7856
fn exitLinearLoop(checker: *mut LinearChecker) {
7857
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
7858
    set checker.loopDepth -= 1;
7859
}
7860
7861
/// Check a conditional and merge its ownership states.
7862
fn checkLinearIf(
7863
    checker: *mut LinearChecker,
7864
    env: *mut LinearEnv,
7865
    node: *ast::Node,
7866
    conditional: ast::If,
7867
) throws (ResolveError) {
7868
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7869
    let base = *env;
7870
    let mut thenEnv = base;
7871
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7872
    let mut elseEnv = base;
7873
    if let branch = conditional.elseBranch {
7874
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7875
    }
7876
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7877
}
7878
7879
/// Check an expression conditional and merge its ownership states.
7880
fn checkLinearCondExpr(
7881
    checker: *mut LinearChecker,
7882
    env: *mut LinearEnv,
7883
    node: *ast::Node,
7884
    conditional: ast::CondExpr,
7885
    usage: LinearUse,
7886
) throws (ResolveError) {
7887
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7888
    let base = *env;
7889
    let mut thenEnv = base;
7890
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
7891
    let mut elseEnv = base;
7892
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7893
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7894
}
7895
7896
/// Check a match expression, including ownership transferred into patterns.
7897
fn checkLinearMatch(
7898
    checker: *mut LinearChecker,
7899
    env: *mut LinearEnv,
7900
    node: *ast::Node,
7901
    matchExpr: ast::Match,
7902
) throws (ResolveError) {
7903
    try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume);
7904
    let base = *env;
7905
    let mut haveResult = false;
7906
    let mut result = base;
7907
    for prongNode in matchExpr.prongs {
7908
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
7909
            else panic "checkLinearMatch: expected prong";
7910
        let mut branch = base;
7911
        let bindingsStart = branch.len;
7912
        match prong.arm {
7913
            case ast::ProngArm::Case(patterns) => {
7914
                for pattern in patterns {
7915
                    try addLinearPatternBindings(checker, &mut branch, pattern);
7916
                }
7917
            }
7918
            case ast::ProngArm::Binding(binding) => {
7919
                try addLinearPatternBindings(checker, &mut branch, binding);
7920
            }
7921
            case ast::ProngArm::Else => {}
7922
        }
7923
        if prong.guard <> nil and branch.len > bindingsStart {
7924
            throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7925
        }
7926
        if let guard = prong.guard {
7927
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
7928
        }
7929
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
7930
        try finishLinearScope(checker, &mut branch, bindingsStart);
7931
        if haveResult {
7932
            try joinLinearBranches(checker, &mut result, result, branch, node);
7933
        } else {
7934
            set result = branch;
7935
            set haveResult = true;
7936
        }
7937
    }
7938
    if haveResult {
7939
        set *env = result;
7940
    }
7941
}
7942
7943
/// Check one call argument and retain its loan through all later arguments.
7944
fn checkLinearCallArg(
7945
    checker: *mut LinearChecker,
7946
    env: *mut LinearEnv,
7947
    loans: *mut LinearLoans,
7948
    arg: *ast::Node,
7949
    expected: Type,
7950
) throws (ResolveError) {
7951
    let mut exclusive = isLinear(expected);
7952
    if let case Type::Pointer {
7953
        class: types::PointerClass::Ref,
7954
        mutable,
7955
        ..
7956
    } = expected {
7957
        set exclusive = mutable;
7958
    } else if let case Type::Slice {
7959
        class: types::PointerClass::Ref,
7960
        mutable,
7961
        ..
7962
    } = expected {
7963
        set exclusive = mutable;
7964
    } else if let case Type::TraitObject {
7965
        class: types::PointerClass::Ref,
7966
        mutable,
7967
        ..
7968
    } = expected {
7969
        set exclusive = mutable;
7970
    }
7971
    if not isUnsafePointerType(expected) {
7972
        try checkCallLoanConflicts(checker, arg, exclusive);
7973
    }
7974
    if isRefType(expected) {
7975
        try checkLinearNode(checker, env, arg, LinearUse::Borrow);
7976
    } else {
7977
        try checkLinearNode(checker, env, arg, LinearUse::Consume);
7978
    }
7979
    if (isRefType(expected) or isLinear(expected)) and not isUnsafePointerType(expected) {
7980
        if let root = linearRootSymbol(checker.resolver, arg) {
7981
            set loans.roots[loans.len] = root;
7982
            set loans.exclusive[loans.len] = exclusive;
7983
            set loans.len += 1;
7984
        }
7985
    }
7986
}
7987
7988
/// Check a syntactic slice append or delete call without a callee function type.
7989
fn checkLinearSliceCall(
7990
    checker: *mut LinearChecker,
7991
    env: *mut LinearEnv,
7992
    node: *ast::Node,
7993
    call: ast::Call,
7994
    elemType: ?*Type,
7995
) throws (ResolveError) {
7996
    let case ast::NodeValue::FieldAccess(access) = call.callee.value else {
7997
        throw emitError(checker.resolver, call.callee, ErrorKind::Internal);
7998
    };
7999
    let receiverTy = typeFor(checker.resolver, access.parent) else {
8000
        throw emitError(checker.resolver, access.parent, ErrorKind::Internal);
8001
    };
8002
    let case Type::Slice { class: receiverClass, .. } = autoDeref(receiverTy) else {
8003
        throw emitError(checker.resolver, access.parent, ErrorKind::Internal);
8004
    };
8005
    let mut loans = LinearLoans {
8006
        parent: checker.loans,
8007
        roots: [nil; MAX_FN_PARAMS + 1],
8008
        exclusive: [false; MAX_FN_PARAMS + 1],
8009
        len: 0,
8010
    };
8011
    set checker.loans = &loans;
8012
8013
    if receiverClass <> types::PointerClass::Unsafe {
8014
        try checkCallLoanConflicts(checker, access.parent, true);
8015
    }
8016
    if receiverClass == types::PointerClass::Owned and elemType <> nil {
8017
        try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
8018
    } else if receiverClass <> types::PointerClass::Unsafe {
8019
        try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
8020
    } else {
8021
        try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
8022
    }
8023
    if receiverClass <> types::PointerClass::Unsafe {
8024
        if let root = linearRootSymbol(checker.resolver, access.parent) {
8025
            set loans.roots[loans.len] = root;
8026
            set loans.exclusive[loans.len] = true;
8027
            set loans.len += 1;
8028
        }
8029
    }
8030
8031
    if let item = elemType {
8032
        if call.args.len <> 2 {
8033
            throw emitError(checker.resolver, node, ErrorKind::Internal);
8034
        }
8035
        try checkLinearCallArg(checker, env, &mut loans, call.args[0], *item);
8036
        let allocatorTy = typeFor(checker.resolver, call.args[1]) else {
8037
            throw emitError(checker.resolver, call.args[1], ErrorKind::Internal);
8038
        };
8039
        try checkLinearCallArg(
8040
            checker,
8041
            env,
8042
            &mut loans,
8043
            call.args[1],
8044
            allocatorTy,
8045
        );
8046
    } else {
8047
        if call.args.len <> 1 {
8048
            throw emitError(checker.resolver, node, ErrorKind::Internal);
8049
        }
8050
        try checkLinearCallArg(
8051
            checker,
8052
            env,
8053
            &mut loans,
8054
            call.args[0],
8055
            Type::U32,
8056
        );
8057
    }
8058
    set checker.loans = loans.parent;
8059
}
8060
8061
/// Check call-scoped loans and argument ownership transfers.
8062
fn checkLinearCall(
8063
    checker: *mut LinearChecker,
8064
    env: *mut LinearEnv,
8065
    node: *ast::Node,
8066
    call: ast::Call,
8067
) throws (ResolveError) {
8068
    match checker.resolver.nodeData.entries[node.id].extra {
8069
        case NodeExtra::SliceAppend { elemType } => {
8070
            try checkLinearSliceCall(checker, env, node, call, elemType);
8071
            return;
8072
        }
8073
        case NodeExtra::SliceDelete { .. } => {
8074
            try checkLinearSliceCall(checker, env, node, call, nil);
8075
            return;
8076
        }
8077
        else => {}
8078
    }
8079
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
8080
    let calleeTy = typeFor(checker.resolver, call.callee) else {
8081
        throw emitError(checker.resolver, call.callee, ErrorKind::Internal);
8082
    };
8083
    let case Type::Fn(info) = calleeTy else {
8084
        for arg in call.args {
8085
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
8086
        }
8087
        return;
8088
    };
8089
    let mut loans = LinearLoans {
8090
        parent: checker.loans,
8091
        roots: [nil; MAX_FN_PARAMS + 1],
8092
        exclusive: [false; MAX_FN_PARAMS + 1],
8093
        len: 0,
8094
    };
8095
    set checker.loans = &loans;
8096
8097
    // Method function types exclude their implicit receiver. Account for it
8098
    // explicitly so owning receivers are consumed and reference receivers
8099
    // participate in call-scoped loan conflict checks.
8100
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
8101
        let mut receiverClass = types::PointerClass::Unsafe;
8102
        let mut receiverMutable = false;
8103
        let mut haveReceiver = false;
8104
        match checker.resolver.nodeData.entries[node.id].extra {
8105
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
8106
                let method = &traitInfo.methods[methodIndex];
8107
                set receiverClass = method.receiverClass;
8108
                set receiverMutable = method.mutable;
8109
                set haveReceiver = true;
8110
            }
8111
            case NodeExtra::MethodCall { method } => {
8112
                set receiverClass = method.receiverClass;
8113
                set receiverMutable = method.mutable;
8114
                set haveReceiver = true;
8115
            }
8116
            else => {}
8117
        }
8118
        if haveReceiver {
8119
            let receiverExclusive =
8120
                receiverClass == types::PointerClass::Owned or receiverMutable;
8121
            if receiverClass <> types::PointerClass::Unsafe {
8122
                try checkCallLoanConflicts(checker, access.parent, receiverExclusive);
8123
            }
8124
            if receiverClass == types::PointerClass::Ref {
8125
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
8126
            } else if receiverClass == types::PointerClass::Owned {
8127
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
8128
            }
8129
            if receiverClass <> types::PointerClass::Unsafe {
8130
                if let root = linearRootSymbol(checker.resolver, access.parent) {
8131
                    set loans.roots[loans.len] = root;
8132
                    set loans.exclusive[loans.len] = receiverExclusive;
8133
                    set loans.len += 1;
8134
                }
8135
            }
8136
        }
8137
    }
8138
8139
    for arg, i in call.args {
8140
        try checkLinearCallArg(
8141
            checker,
8142
            env,
8143
            &mut loans,
8144
            arg,
8145
            *info.paramTypes[i],
8146
        );
8147
    }
8148
    set checker.loans = loans.parent;
8149
}
8150
8151
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
8152
fn checkLinearIfLet(
8153
    checker: *mut LinearChecker,
8154
    env: *mut LinearEnv,
8155
    node: *ast::Node,
8156
    conditional: ast::IfLet,
8157
) throws (ResolveError) {
8158
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
8159
        isLinear(subjectTy)
8160
    {
8161
        throw emitError(
8162
            checker.resolver,
8163
            conditional.pattern.scrutinee,
8164
            ErrorKind::LinearPartialMove,
8165
        );
8166
    }
8167
    try checkLinearNode(
8168
        checker,
8169
        env,
8170
        conditional.pattern.scrutinee,
8171
        LinearUse::Consume,
8172
    );
8173
    let base = *env;
8174
    let mut thenEnv = base;
8175
    let bindingsStart = thenEnv.len;
8176
    try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern);
8177
    if let guard = conditional.pattern.guard {
8178
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
8179
    }
8180
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
8181
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
8182
    let mut elseEnv = base;
8183
    if let branch = conditional.elseBranch {
8184
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
8185
    }
8186
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
8187
}
8188
8189
/// Check one expression or statement under an ownership-use context.
8190
fn checkLinearNode(
8191
    checker: *mut LinearChecker,
8192
    env: *mut LinearEnv,
8193
    node: *ast::Node,
8194
    usage: LinearUse,
8195
) throws (ResolveError) {
8196
    if env.terminated {
8197
        return;
8198
    }
8199
    let mut exclusive = usage == LinearUse::Place;
8200
    if usage == LinearUse::Consume {
8201
        if let ty = typeFor(checker.resolver, node) {
8202
            set exclusive = isLinear(ty);
8203
        }
8204
    }
8205
    if let case ast::NodeValue::AddressOf(addr) = node.value; addr.mutable {
8206
        set exclusive = true;
8207
    }
8208
    try checkCallLoanConflicts(checker, node, exclusive);
8209
    match node.value {
8210
        case ast::NodeValue::Ident(_) =>
8211
            try checkLinearIdent(checker, env, node, usage),
8212
        case ast::NodeValue::ExprStmt(expr) => {
8213
            if let exprTy = typeFor(checker.resolver, expr) {
8214
                if isLinear(exprTy) {
8215
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
8216
                }
8217
            }
8218
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8219
        }
8220
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
8221
        case ast::NodeValue::Unsafe(body) =>
8222
            try checkLinearNode(checker, env, body, usage),
8223
        case ast::NodeValue::Let(binding) => {
8224
            if let case ast::NodeValue::Undef = binding.value.value {
8225
                if let bindingTy = typeFor(checker.resolver, binding.ident);
8226
                    isLinear(bindingTy)
8227
                {
8228
                    throw emitError(
8229
                        checker.resolver,
8230
                        binding.value,
8231
                        ErrorKind::LinearUndefined,
8232
                    );
8233
                }
8234
            }
8235
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
8236
            try addLinearBinding(checker, env, node);
8237
        }
8238
        case ast::NodeValue::Assign(assign) => {
8239
            let mut target: ?u32 = nil;
8240
            if let leftTy = typeFor(checker.resolver, assign.left) {
8241
                if isLinear(leftTy) {
8242
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
8243
                        if let sym = symbolFor(checker.resolver, assign.left) {
8244
                            set target = findLinearBinding(env, sym);
8245
                        }
8246
                    }
8247
                    if target == nil {
8248
                        throw emitError(
8249
                            checker.resolver,
8250
                            assign.left,
8251
                            ErrorKind::LinearOverwrite,
8252
                        );
8253
                    }
8254
                }
8255
            }
8256
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
8257
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
8258
            if let index = target {
8259
                if linearBindingAvailable(env, index) {
8260
                    throw emitError(
8261
                        checker.resolver,
8262
                        assign.left,
8263
                        ErrorKind::LinearOverwrite,
8264
                    );
8265
                }
8266
                set env.available |= (1 as u64) << (index as u64);
8267
            }
8268
        }
8269
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
8270
        case ast::NodeValue::AddressOf(addr) => {
8271
            try checkLinearNode(checker, env, addr.target, LinearUse::Borrow);
8272
        }
8273
        case ast::NodeValue::Deref(target) => {
8274
            if let resultTy = typeFor(checker.resolver, node) {
8275
                if isLinear(resultTy) and usage == LinearUse::Consume {
8276
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8277
                }
8278
            }
8279
            try checkLinearNode(checker, env, target, LinearUse::Observe);
8280
        }
8281
        case ast::NodeValue::FieldAccess(access) => {
8282
            if let resultTy = typeFor(checker.resolver, node) {
8283
                if isLinear(resultTy) and usage == LinearUse::Consume {
8284
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8285
                }
8286
            }
8287
            try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
8288
        }
8289
        case ast::NodeValue::ScopeAccess(_) => {}
8290
        case ast::NodeValue::Subscript { container, index } => {
8291
            if let resultTy = typeFor(checker.resolver, node) {
8292
                if isLinear(resultTy) and usage == LinearUse::Consume {
8293
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8294
                }
8295
            }
8296
            try checkLinearNode(checker, env, container, LinearUse::Observe);
8297
            try checkLinearNode(checker, env, index, LinearUse::Consume);
8298
        }
8299
        case ast::NodeValue::RecordLit(lit) => {
8300
            for fieldNode in lit.fields {
8301
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
8302
                    else panic "checkLinearNode: expected field";
8303
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
8304
            }
8305
        }
8306
        case ast::NodeValue::ArrayLit(items) => {
8307
            for item in items {
8308
                try checkLinearNode(checker, env, item, LinearUse::Consume);
8309
            }
8310
        }
8311
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
8312
            if let itemTy = typeFor(checker.resolver, repeat.item) {
8313
                if isLinear(itemTy) {
8314
                    throw emitError(
8315
                        checker.resolver,
8316
                        repeat.item,
8317
                        ErrorKind::LinearDiscard,
8318
                    );
8319
                }
8320
            }
8321
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
8322
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
8323
        }
8324
        case ast::NodeValue::BinOp(op) => {
8325
            try checkLinearNode(checker, env, op.left, LinearUse::Consume);
8326
            if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or {
8327
                let skipped = *env;
8328
                let mut evaluated = skipped;
8329
                try checkLinearNode(
8330
                    checker,
8331
                    &mut evaluated,
8332
                    op.right,
8333
                    LinearUse::Consume,
8334
                );
8335
                try joinLinearBranches(checker, env, skipped, evaluated, node);
8336
            } else {
8337
                try checkLinearNode(checker, env, op.right, LinearUse::Consume);
8338
            }
8339
        }
8340
        case ast::NodeValue::UnOp(op) => {
8341
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
8342
        }
8343
        case ast::NodeValue::As(expr) => {
8344
            try checkLinearNode(checker, env, expr.value, LinearUse::Consume);
8345
        }
8346
        case ast::NodeValue::Range(range) => {
8347
            if let start = range.start {
8348
                try checkLinearNode(checker, env, start, LinearUse::Consume);
8349
            }
8350
            if let end = range.end {
8351
                try checkLinearNode(checker, env, end, LinearUse::Consume);
8352
            }
8353
        }
8354
        case ast::NodeValue::BuiltinCall { args, .. } => {
8355
            for arg in args {
8356
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
8357
            }
8358
        }
8359
        case ast::NodeValue::If(conditional) => {
8360
            try checkLinearIf(checker, env, node, conditional);
8361
        }
8362
        case ast::NodeValue::CondExpr(conditional) => {
8363
            try checkLinearCondExpr(checker, env, node, conditional, usage);
8364
        }
8365
        case ast::NodeValue::IfLet(conditional) => {
8366
            try checkLinearIfLet(checker, env, node, conditional);
8367
        }
8368
        case ast::NodeValue::LetElse(binding) => {
8369
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
8370
                isLinear(subjectTy)
8371
            {
8372
                throw emitError(
8373
                    checker.resolver,
8374
                    binding.pattern.scrutinee,
8375
                    ErrorKind::LinearPartialMove,
8376
                );
8377
            }
8378
            try checkLinearNode(
8379
                checker,
8380
                env,
8381
                binding.pattern.scrutinee,
8382
                LinearUse::Consume,
8383
            );
8384
            let base = *env;
8385
            let mut guardedEnv = base;
8386
            if let guard = binding.pattern.guard {
8387
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
8388
            }
8389
            let mut successEnv = guardedEnv;
8390
            try addLinearPatternBindings(
8391
                checker,
8392
                &mut successEnv,
8393
                binding.pattern.pattern,
8394
            );
8395
            let mut fallbackEnv = base;
8396
            try checkLinearNode(
8397
                checker,
8398
                &mut fallbackEnv,
8399
                binding.elseBranch,
8400
                LinearUse::Consume,
8401
            );
8402
            if binding.pattern.guard <> nil {
8403
                let mut guardFallbackEnv = guardedEnv;
8404
                try checkLinearNode(
8405
                    checker,
8406
                    &mut guardFallbackEnv,
8407
                    binding.elseBranch,
8408
                    LinearUse::Consume,
8409
                );
8410
                try joinLinearBranches(
8411
                    checker,
8412
                    &mut fallbackEnv,
8413
                    fallbackEnv,
8414
                    guardFallbackEnv,
8415
                    binding.elseBranch,
8416
                );
8417
            }
8418
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8419
                try addLinearPatternBindings(
8420
                    checker,
8421
                    &mut fallbackEnv,
8422
                    binding.pattern.pattern,
8423
                );
8424
            }
8425
            try joinLinearBranches(checker, env, successEnv, fallbackEnv, node);
8426
        }
8427
        case ast::NodeValue::Match(matchExpr) => {
8428
            try checkLinearMatch(checker, env, node, matchExpr);
8429
        }
8430
        case ast::NodeValue::Try(tryExpr) => {
8431
            try checkLinearNode(checker, env, tryExpr.expr, usage);
8432
            let success = *env;
8433
            if tryExpr.catches.len == 0
8434
                and not tryExpr.shouldPanic
8435
                and not tryExpr.returnsOptional
8436
            {
8437
                let mut errorExit = success;
8438
                try finishLinearExit(checker, &mut errorExit);
8439
            }
8440
            for catchNode in tryExpr.catches {
8441
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
8442
                    else panic "checkLinearNode: expected catch";
8443
                let mut branch = success;
8444
                let start = branch.len;
8445
                if let binding = catchClause.binding {
8446
                    try addLinearBinding(checker, &mut branch, binding);
8447
                }
8448
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8449
                try finishLinearScope(checker, &mut branch, start);
8450
                try joinLinearBranches(checker, env, *env, branch, node);
8451
            }
8452
        }
8453
        case ast::NodeValue::While(whileStmt) => {
8454
            enterLinearLoop(checker, env);
8455
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8456
            let conditionExit = *env;
8457
            setLinearLoopNaturalExit(checker, &conditionExit);
8458
            let mut bodyEnv = conditionExit;
8459
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8460
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8461
            exitLinearLoop(checker);
8462
            set *env = conditionExit;
8463
            if let elseBranch = whileStmt.elseBranch {
8464
                let mut elseEnv = conditionExit;
8465
                try checkLinearNode(
8466
                    checker,
8467
                    &mut elseEnv,
8468
                    elseBranch,
8469
                    LinearUse::Discard,
8470
                );
8471
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8472
            }
8473
        }
8474
        case ast::NodeValue::WhileLet(whileStmt) => {
8475
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8476
                isLinear(subjectTy)
8477
            {
8478
                throw emitError(
8479
                    checker.resolver,
8480
                    whileStmt.pattern.scrutinee,
8481
                    ErrorKind::LinearPartialMove,
8482
                );
8483
            }
8484
            let base = *env;
8485
            enterLinearLoop(checker, env);
8486
            let mut bodyEnv = base;
8487
            try checkLinearNode(
8488
                checker,
8489
                &mut bodyEnv,
8490
                whileStmt.pattern.scrutinee,
8491
                LinearUse::Consume,
8492
            );
8493
            let mut conditionExit = bodyEnv;
8494
            let start = bodyEnv.len;
8495
            try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern);
8496
            if let guard = whileStmt.pattern.guard {
8497
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8498
                let mut guardExit = bodyEnv;
8499
                try finishLinearScope(checker, &mut guardExit, start);
8500
                try joinLinearBranches(
8501
                    checker,
8502
                    &mut conditionExit,
8503
                    conditionExit,
8504
                    guardExit,
8505
                    guard,
8506
                );
8507
            }
8508
            setLinearLoopNaturalExit(checker, &conditionExit);
8509
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8510
            try finishLinearScope(checker, &mut bodyEnv, start);
8511
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8512
            exitLinearLoop(checker);
8513
            set *env = conditionExit;
8514
            if let elseBranch = whileStmt.elseBranch {
8515
                let mut elseEnv = conditionExit;
8516
                try checkLinearNode(
8517
                    checker,
8518
                    &mut elseEnv,
8519
                    elseBranch,
8520
                    LinearUse::Discard,
8521
                );
8522
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8523
            }
8524
        }
8525
        case ast::NodeValue::For(forStmt) => {
8526
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8527
                if isLinear(iterableTy) {
8528
                    throw emitError(
8529
                        checker.resolver,
8530
                        forStmt.iterable,
8531
                        ErrorKind::LinearPartialMove,
8532
                    );
8533
                }
8534
            }
8535
            try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
8536
            let base = *env;
8537
            enterLinearLoop(checker, env);
8538
            setLinearLoopNaturalExit(checker, &base);
8539
            let mut bodyEnv = base;
8540
            let start = bodyEnv.len;
8541
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
8542
            if let index = forStmt.index {
8543
                try addLinearBinding(checker, &mut bodyEnv, index);
8544
            }
8545
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
8546
            try finishLinearScope(checker, &mut bodyEnv, start);
8547
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
8548
            exitLinearLoop(checker);
8549
            set *env = base;
8550
            if let elseBranch = forStmt.elseBranch {
8551
                let mut elseEnv = base;
8552
                try checkLinearNode(
8553
                    checker,
8554
                    &mut elseEnv,
8555
                    elseBranch,
8556
                    LinearUse::Discard,
8557
                );
8558
                try joinLinearBranches(checker, env, base, elseEnv, node);
8559
            }
8560
        }
8561
        case ast::NodeValue::Loop { body } => {
8562
            let base = *env;
8563
            enterLinearLoop(checker, env);
8564
            let mut bodyEnv = base;
8565
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
8566
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
8567
            let depth = checker.loopDepth - 1;
8568
            let breakSeen = checker.loopBreakSeen[depth];
8569
            let exitAvailable = checker.loopExitAvailable[depth];
8570
            exitLinearLoop(checker);
8571
            set *env = base;
8572
            if breakSeen {
8573
                set env.available = exitAvailable;
8574
            } else {
8575
                set env.terminated = true;
8576
            }
8577
        }
8578
        case ast::NodeValue::Break => {
8579
            assert checker.loopDepth > 0, "linear loop control outside loop";
8580
            let start = checker.loopMarks[checker.loopDepth - 1];
8581
            try finishLinearScope(checker, env, start);
8582
            try checkLinearLoopBreak(checker, env, node);
8583
            set env.terminated = true;
8584
        }
8585
        case ast::NodeValue::Continue => {
8586
            assert checker.loopDepth > 0, "linear loop control outside loop";
8587
            let start = checker.loopMarks[checker.loopDepth - 1];
8588
            try finishLinearScope(checker, env, start);
8589
            try checkLinearLoopBackEdge(checker, env, node);
8590
            set env.terminated = true;
8591
        }
8592
        case ast::NodeValue::Return { value } => {
8593
            if let expr = value {
8594
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8595
            }
8596
            try finishLinearExit(checker, env);
8597
        }
8598
        case ast::NodeValue::Throw { expr } => {
8599
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8600
            try finishLinearExit(checker, env);
8601
        }
8602
        case ast::NodeValue::Panic { message } => {
8603
            if let expr = message {
8604
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8605
            }
8606
            set env.terminated = true;
8607
        }
8608
        case ast::NodeValue::Assert { condition, message } => {
8609
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
8610
            if let expr = message {
8611
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8612
            }
8613
        }
8614
        else => {}
8615
    }
8616
}
8617
8618
/// Check exact-use ownership for one resolved function.
8619
fn checkLinearFn(
8620
    self: *mut Resolver,
8621
    receiver: ?*ast::Node,
8622
    params: *mut [*ast::Node],
8623
    body: *ast::Node,
8624
) throws (ResolveError) {
8625
    let mut checker = LinearChecker {
8626
        resolver: self,
8627
        loans: nil,
8628
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
8629
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8630
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8631
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
8632
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
8633
        loopDepth: 0,
8634
    };
8635
    let mut env = LinearEnv {
8636
        symbols: [nil; MAX_LINEAR_BINDINGS],
8637
        available: 0,
8638
        len: 0,
8639
        terminated: false,
8640
    };
8641
    if let receiverNode = receiver {
8642
        try addLinearBinding(&mut checker, &mut env, receiverNode);
8643
    }
8644
    for paramNode in params {
8645
        let case ast::NodeValue::FnParam(_) = paramNode.value
8646
            else panic "checkLinearFn: expected parameter";
8647
        try addLinearBinding(&mut checker, &mut env, paramNode);
8648
    }
8649
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
8650
    try finishLinearScope(&mut checker, &mut env, 0);
8651
}
8652
8653
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
8654
fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
8655
    for stmt in block.statements {
8656
        try visitDef(self, stmt);
8657
    }
8658
}
8659
8660
/// Resolve all packages.
8661
export fn resolve(self: *mut Resolver, graph: *module::ModuleGraph, packages: *[Pkg]) -> Diagnostics throws (ResolveError) {
8662
    set self.moduleGraph = graph;
8663
8664
    // 1. Bind all package roots to enable cross-package references.
8665
    for i in 0..packages.len {
8666
        let pkg = &packages[i];
8667
        // Enter a new scope for the module.
8668
        let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry);
8669
        // Bind the package root module name in the global package scope.
8670
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, self.pkgScope);
8671
8672
        exitModuleScope(self, enter);
8673
    }
8674
    // 2. Resolve each package's contents.
8675
    for i in 0..packages.len {
8676
        let pkg = &packages[i];
8677
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
8678
        if not success(&diags) {
8679
            return diags;
8680
        }
8681
    }
8682
    return Diagnostics { errors: self.errors };
8683
}
8684
8685
/// Resolve a package.
8686
fn resolvePackage(self: *mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
8687
    let rootId = rootEntry.id;
8688
    let scope = self.moduleScopes[rootId as u32]
8689
        else panic "resolvePackage: module scope not found";
8690
8691
    // Set up the module scope for this package.
8692
    set self.scope = scope;
8693
    set self.currentMod = rootId;
8694
8695
    let case ast::NodeValue::Block(block) = node.value
8696
        else panic "resolvePackage: expected block for module root";
8697
8698
    // Module graph analysis phase: bind all module name symbols and scopes.
8699
    try resolveModuleGraph(self, &block) catch {
8700
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8701
        return Diagnostics { errors: self.errors };
8702
    };
8703
8704
    // Declaration phase: bind all names and analyze top-level declarations.
8705
    try resolveModuleDecls(self, &block) catch {
8706
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8707
    };
8708
    if self.errors.len > 0 {
8709
        return Diagnostics { errors: self.errors };
8710
    }
8711
8712
    // Definition phase: analyze function bodies and sub-module definitions.
8713
    try resolveModuleDefs(self, &block) catch {
8714
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
8715
    };
8716
    setNodeType(self, node, Type::Void);
8717
8718
    return Diagnostics { errors: self.errors };
8719
}