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