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