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