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