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