lib/std/lang/resolver.rad 332.7 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 that provides access, or nil for a pending call argument.
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
    // Ownership checks require complete type and call metadata.
3591
    if self.errors.len == 0 {
3592
        try checkLinearFn(self, receiverName, params, body);
3593
    }
3594
    return false;
3595
}
3596
3597
/// Analyze a function parameter and bind its identifier.
3598
unsafe fn resolveFnParam(self: &mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
3599
    throws (ResolveError)
3600
{
3601
    let ty = try resolveValueType(self, param.type);
3602
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3603
3604
    return ty;
3605
}
3606
3607
/// Compiler-known ownership markers carried by a composite declaration.
3608
record OwnershipMarkers: Copy {
3609
    /// The declaration requires exact consumption.
3610
    linear: bool,
3611
    /// The declaration permits implicit copies.
3612
    copy: bool,
3613
}
3614
3615
/// Resolve compiler-known ownership markers from a derive list.
3616
unsafe fn resolveOwnershipMarkers(self: &mut Resolver, derives: *[*ast::Node]) -> OwnershipMarkers
3617
    throws (ResolveError)
3618
{
3619
    let mut result = OwnershipMarkers { linear: false, copy: false };
3620
    for derive in derives {
3621
        let name = try nodeName(self, derive);
3622
        if mem::eq(name, "Once") {
3623
            if result.linear {
3624
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3625
            }
3626
            if result.copy {
3627
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
3628
            }
3629
            set result.linear = true;
3630
        } else if mem::eq(name, "Copy") {
3631
            if result.copy {
3632
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3633
            }
3634
            if result.linear {
3635
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
3636
            }
3637
            set result.copy = true;
3638
        } else {
3639
            // Resolve an ordinary trait derive.
3640
            try infer(self, derive);
3641
        }
3642
    }
3643
    return result;
3644
}
3645
3646
/// Resolve record fields from a node list.
3647
unsafe fn resolveRecordFields(self: &mut Resolver, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
3648
    throws (ResolveError)
3649
{
3650
    let a = alloc::arenaAllocator(&mut self.arena);
3651
    let mut result: *unsafe mut [RecordField] = &mut [];
3652
    let mut currentOffset: u32 = 0;
3653
    let mut maxAlignment: u32 = 1;
3654
3655
    if fields.len > parser::MAX_RECORD_FIELDS {
3656
        throw emitError(self, node, ErrorKind::Internal);
3657
    }
3658
    // TODO: Add cycle detection to catch invalid recursive types like `record A { a: A }`.
3659
    for field in fields {
3660
        let case ast::NodeValue::RecordField {
3661
            field: fieldNode,
3662
            type: typeNode,
3663
            value: valueNode
3664
        } = field.value else panic "resolveRecordFields: invalid record field";
3665
        let fieldTy = try resolveValueType(self, typeNode);
3666
        try ensureStorableType(self, typeNode, fieldTy);
3667
3668
        if let v = valueNode {
3669
            let _valTy = try checkAssignable(self, v, fieldTy);
3670
        }
3671
        // Get field name for labeled records.
3672
        let mut fieldName: ?*[u8] = nil;
3673
        if labeled {
3674
            let n = fieldNode
3675
                else panic "resolveRecordFields: labeled record field missing name";
3676
            set fieldName = try nodeName(self, n);
3677
        }
3678
        let fieldType = typeFor(self, typeNode)
3679
            else throw emitError(self, typeNode, ErrorKind::CannotInferType);
3680
3681
        // Ensure field type is fully resolved before computing layout.
3682
        try ensureTypeResolved(self, fieldType, typeNode);
3683
3684
        // Compute field offset by aligning to field's alignment.
3685
        let fieldLayout = getTypeLayout(fieldType);
3686
        set currentOffset = mem::alignUp(currentOffset, fieldLayout.alignment);
3687
3688
        result.append(RecordField { name: fieldName, fieldType, offset: currentOffset as i32 }, a);
3689
3690
        // Advance offset past this field.
3691
        set currentOffset += fieldLayout.size;
3692
3693
        // Track max alignment for record layout.
3694
        set maxAlignment = max(maxAlignment, fieldLayout.alignment);
3695
    }
3696
    // Compute cached layout.
3697
    let recordLayout = Layout {
3698
        size: mem::alignUp(currentOffset, maxAlignment),
3699
        alignment: maxAlignment
3700
    };
3701
    return RecordType {
3702
        fields: &result[..],
3703
        labeled,
3704
        layout: recordLayout,
3705
        declaredLinear: false,
3706
        declaredCopy: false,
3707
    };
3708
}
3709
3710
/// Resolve record field types for a named record declaration.
3711
unsafe fn resolveRecordBody(self: &mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3712
    throws (ResolveError)
3713
{
3714
    // Get the type symbol that was bound to this declaration node.
3715
    // If there's no symbol, it's because an earlier phase failed.
3716
    let sym = symbolFor(self, node)
3717
        else return;
3718
    let case SymbolData::Type(nominalTy) = sym.data
3719
        else panic "resolveRecordBody: unexpected type symbol data";
3720
3721
    // Skip if already resolved.
3722
    if let case NominalType::Record(_) = *nominalTy {
3723
        return;
3724
    }
3725
    let markers = try resolveOwnershipMarkers(self, decl.derives);
3726
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3727
    if markers.copy {
3728
        for field in recordType.fields {
3729
            if not isCopy(field.fieldType) {
3730
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
3731
            }
3732
        }
3733
    }
3734
    set recordType.declaredLinear = markers.linear;
3735
    set recordType.declaredCopy = markers.copy;
3736
3737
    set *nominalTy = NominalType::Record(recordType);
3738
}
3739
3740
/// Bind a type name.
3741
unsafe fn bindTypeName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3742
    throws (ResolveError)
3743
{
3744
    let attrMask = resolveAttributes(self, attrs);
3745
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3746
3747
    // Create a placeholder nominal type that will be replaced in
3748
    // the next phase.
3749
    let nominalTy = allocNominalType(self, NominalType::Placeholder(node));
3750
3751
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
3752
}
3753
3754
/// Allocate a trait type descriptor and return a pointer to it.
3755
unsafe fn allocTraitType(self: &mut Resolver, name: *[u8]) -> *unsafe mut TraitType {
3756
    let p = try! alloc::allocRaw(&mut self.arena, @sizeOf(TraitType), @alignOf(TraitType));
3757
    let entry = p as *unsafe mut TraitType;
3758
    set *entry = TraitType { name, methods: &mut [], supertraits: &mut [] };
3759
3760
    return entry;
3761
}
3762
3763
/// Bind a trait name in the current scope.
3764
unsafe fn bindTraitName(self: &mut Resolver, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
3765
    throws (ResolveError)
3766
{
3767
    let attrMask = resolveAttributes(self, attrs);
3768
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
3769
3770
    let traitName = try nodeName(self, name);
3771
    let traitType = allocTraitType(self, traitName);
3772
    let data = SymbolData::Trait(traitType);
3773
    let scope = self.scope;
3774
    let sym = try bindIdent(self, traitName, node, data, attrMask, scope);
3775
3776
    setNodeType(self, node, Type::Void);
3777
    setNodeType(self, name, Type::Void);
3778
3779
    return sym;
3780
}
3781
3782
/// Find a trait method by name.
3783
export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod {
3784
    for i in 0..traitType.methods.len {
3785
        if traitType.methods[i].name == name {
3786
            return &traitType.methods[i];
3787
        }
3788
    }
3789
    return nil;
3790
}
3791
3792
/// Resolve a trait declaration body: supertrait methods, then own methods.
3793
unsafe fn resolveTraitBody(self: &mut Resolver, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
3794
    throws (ResolveError)
3795
{
3796
    let sym = symbolFor(self, node)
3797
        else return;
3798
    let case SymbolData::Trait(traitType) = sym.data
3799
        else return;
3800
    if traitType.methods.len > 0 {
3801
        return;
3802
    }
3803
3804
    // Resolve supertrait bounds and copy their methods into this trait.
3805
    for superNode in supertraits {
3806
        let superSym = try resolveNamePath(self, superNode);
3807
        let case SymbolData::Trait(superTrait) = superSym.data
3808
            else throw emitError(self, superNode, ErrorKind::Internal);
3809
        // Trait bodies are otherwise resolved in source order. Recursively
3810
        // resolve a supertrait only when it is declared later.
3811
        if superSym.node.id > node.id {
3812
            let case ast::NodeValue::TraitDecl {
3813
                supertraits: inheritedTraits, methods: inheritedMethods, ..
3814
            } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal);
3815
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
3816
        }
3817
3818
        setNodeSymbol(self, superNode, superSym);
3819
3820
        let a = alloc::arenaAllocator(&mut self.arena);
3821
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
3822
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3823
                expected: ast::MAX_TRAIT_METHODS,
3824
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
3825
            }));
3826
        }
3827
        // Copy inherited methods into this trait's method table.
3828
        for inherited in superTrait.methods {
3829
            if let _ = findTraitMethod(traitType, inherited.name) {
3830
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
3831
            }
3832
            traitType.methods.append(TraitMethod {
3833
                name: inherited.name,
3834
                fnType: inherited.fnType,
3835
                mutable: inherited.mutable,
3836
                receiverClass: inherited.receiverClass,
3837
                index: traitType.methods.len as u32,
3838
            }, a);
3839
        }
3840
        traitType.supertraits.append(superTrait, a);
3841
    }
3842
3843
    if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS {
3844
        throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3845
            expected: ast::MAX_TRAIT_METHODS,
3846
            actual: traitType.methods.len as u32 + methods.len as u32,
3847
        }));
3848
    }
3849
3850
    for methodNode in methods {
3851
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value
3852
            else continue;
3853
        let methodName = try nodeName(self, name);
3854
        let attrMask = resolveAttributes(self, attrs);
3855
3856
        // Reject duplicate method names.
3857
        if let _ = findTraitMethod(traitType, methodName) {
3858
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3859
        }
3860
        // Determine the receiver class and mutability, and validate that it
3861
        // points to the declaring trait.
3862
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3863
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3864
        let case ast::TypeSig::Pointer {
3865
            class: receiverClass, valueType: receiverValueType, mutable,
3866
        } = typeSig
3867
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3868
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
3869
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3870
        let case ast::TypeSig::Nominal(nameNode) = innerSig
3871
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3872
        let receiverTargetName = try nodeName(self, nameNode);
3873
3874
        if receiverTargetName <> traitType.name {
3875
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3876
        }
3877
        // Resolve parameter types and return type.
3878
        let a = alloc::arenaAllocator(&mut self.arena);
3879
        let mut paramTypes: *mut [*Type] = &mut [];
3880
        let mut throwList: *mut [*Type] = &mut [];
3881
        let mut retType = allocType(self, Type::Void);
3882
3883
        if sig.params.len > MAX_FN_PARAMS {
3884
            throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch {
3885
                expected: MAX_FN_PARAMS,
3886
                actual: sig.params.len,
3887
            }));
3888
        }
3889
        for paramNode in sig.params {
3890
            let paramTy = try infer(self, paramNode);
3891
            paramTypes.append(allocType(self, paramTy), a);
3892
        }
3893
        if let ret = sig.returnType {
3894
            set retType = allocType(self, try infer(self, ret));
3895
        }
3896
        // Resolve throws list.
3897
        if sig.throwList.len > MAX_FN_THROWS {
3898
            throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch {
3899
                expected: MAX_FN_THROWS,
3900
                actual: sig.throwList.len,
3901
            }));
3902
        }
3903
        for throwNode in sig.throwList {
3904
            let throwTy = try infer(self, throwNode);
3905
            throwList.append(allocType(self, throwTy), a);
3906
        }
3907
        let fnType = FnType {
3908
            paramTypes: &paramTypes[..],
3909
            returnType: retType,
3910
            throwList: &throwList[..],
3911
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3912
        };
3913
        traitType.methods.append(TraitMethod {
3914
            name: methodName,
3915
            fnType: allocFnType(self, fnType),
3916
            mutable,
3917
            receiverClass,
3918
            index: traitType.methods.len as u32,
3919
        }, a);
3920
3921
        setNodeType(self, methodNode, Type::Void);
3922
    }
3923
}
3924
3925
/// Resolve a name path node to a symbol.
3926
/// Used for trait and type references in instance declarations and trait objects.
3927
unsafe fn resolveNamePath(self: &mut Resolver, node: *ast::Node) -> *unsafe mut Symbol
3928
    throws (ResolveError)
3929
{
3930
    match node.value {
3931
        case ast::NodeValue::Ident(name) => {
3932
            let sym = findAnySymbol(self.scope, name)
3933
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
3934
            return sym;
3935
        }
3936
        case ast::NodeValue::ScopeAccess(access) => {
3937
            let scope = self.scope;
3938
            return try resolveAccess(self, node, access, scope);
3939
        }
3940
        else => {
3941
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
3942
        }
3943
    }
3944
}
3945
3946
/// Resolve an instance declaration.
3947
/// Validates that the trait exists, the target type exists, and all methods
3948
/// match the trait's signatures.
3949
unsafe fn resolveInstanceDecl(
3950
    self: &mut Resolver,
3951
    node: *ast::Node,
3952
    traitName: *ast::Node,
3953
    targetType: *ast::Node,
3954
    methods: *[*ast::Node]
3955
) throws (ResolveError) {
3956
    // Look up the trait.
3957
    let traitSym = try resolveNamePath(self, traitName);
3958
    let case SymbolData::Trait(traitInfo) = traitSym.data
3959
        else throw emitError(self, traitName, ErrorKind::Internal);
3960
3961
    setNodeSymbol(self, traitName, traitSym);
3962
3963
    // Look up the target type.
3964
    let typeSym = try resolveNamePath(self, targetType);
3965
    let case SymbolData::Type(nominalTy) = typeSym.data
3966
        else throw emitError(self, targetType, ErrorKind::Internal);
3967
    setNodeSymbol(self, targetType, typeSym);
3968
    // Ensure the concrete type body is resolved.
3969
    try ensureNominalResolved(self, nominalTy, targetType);
3970
3971
    // Reject duplicate instance for the same (trait, type) pair.
3972
    let concreteType = Type::Nominal(nominalTy);
3973
    if let _ = findInstance(self, traitInfo, concreteType) {
3974
        throw emitError(self, node, ErrorKind::DuplicateInstance);
3975
    }
3976
3977
    // Build the instance entry.
3978
    if self.instancesLen >= MAX_INSTANCES {
3979
        throw emitError(self, node, ErrorKind::Internal);
3980
    }
3981
    let methodSlice = try! alloc::allocRawSlice(
3982
        &mut self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
3983
    ) as *unsafe mut [*unsafe mut Symbol];
3984
    let mut entry = InstanceEntry {
3985
        traitType: traitInfo,
3986
        concreteType,
3987
        concreteTypeName: typeSym.name,
3988
        moduleId: self.currentMod,
3989
        methods: methodSlice,
3990
    };
3991
    // Track which trait methods are covered by the instance.
3992
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
3993
3994
    // Match each instance method to a trait method.
3995
    for methodNode in methods {
3996
        let case ast::NodeValue::MethodDecl {
3997
            name, receiverName, receiverType, sig, body, attrs,
3998
        } = methodNode.value else continue;
3999
4000
        let methodName = try nodeName(self, name);
4001
        let attrMask = resolveAttributes(self, attrs);
4002
4003
        // Find the matching trait method.
4004
        let tm = findTraitMethod(traitInfo, methodName)
4005
            else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
4006
        let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
4007
        if instanceUnsafe <> tm.fnType.isUnsafe {
4008
            throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
4009
        }
4010
4011
        // Determine receiver mutability and validate receiver type.
4012
        // The receiver must be `*Type` or `*mut Type`.
4013
        let case ast::NodeValue::TypeSig(typeSig) = receiverType.value
4014
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4015
        let case ast::TypeSig::Pointer {
4016
            class: receiverClass, valueType, mutable: receiverMut,
4017
        } = typeSig
4018
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4019
        if receiverClass <> tm.receiverClass {
4020
            throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4021
        }
4022
4023
        // Validate that the receiver type annotation matches the
4024
        // concrete type from the instance declaration.
4025
        let annotatedTy = try infer(self, valueType);
4026
        if not typesEqual(annotatedTy, concreteType) {
4027
            throw emitTypeMismatch(self, receiverType, TypeMismatch {
4028
                expected: concreteType,
4029
                actual: annotatedTy,
4030
            });
4031
        }
4032
4033
        // Check receiver mutability matches in both directions.
4034
        if tm.mutable and not receiverMut {
4035
            throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
4036
        }
4037
        if receiverMut and not tm.mutable {
4038
            throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
4039
        }
4040
4041
        // Build the function type for the instance method.
4042
        // The receiver becomes the first parameter.
4043
        let receiverPtrType = Type::Pointer {
4044
            class: receiverClass,
4045
            target: allocType(self, concreteType),
4046
            mutable: receiverMut,
4047
        };
4048
4049
        // Validate that the instance method's signature matches the
4050
        // trait method's signature exactly (params, return type, throws).
4051
        if sig.params.len <> tm.fnType.paramTypes.len {
4052
            throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
4053
                expected: tm.fnType.paramTypes.len as u32,
4054
                actual: sig.params.len,
4055
            }));
4056
        }
4057
        for paramNode, j in sig.params {
4058
            let case ast::NodeValue::FnParam(param) = paramNode.value
4059
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4060
            let instanceParamTy = try resolveValueType(self, param.type);
4061
            if not typesEqual(instanceParamTy, *tm.fnType.paramTypes[j]) {
4062
                throw emitTypeMismatch(self, paramNode, TypeMismatch {
4063
                    expected: *tm.fnType.paramTypes[j],
4064
                    actual: instanceParamTy,
4065
                });
4066
            }
4067
        }
4068
        let mut instanceRetTy = Type::Void;
4069
        if let retNode = sig.returnType {
4070
            set instanceRetTy = try resolveValueType(self, retNode);
4071
        }
4072
        if not typesEqual(instanceRetTy, *tm.fnType.returnType) {
4073
            throw emitTypeMismatch(self, methodNode, TypeMismatch {
4074
                expected: *tm.fnType.returnType,
4075
                actual: instanceRetTy,
4076
            });
4077
        }
4078
        if sig.throwList.len <> tm.fnType.throwList.len {
4079
            throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
4080
                expected: tm.fnType.throwList.len as u32,
4081
                actual: sig.throwList.len,
4082
            }));
4083
        }
4084
        for throwNode, j in sig.throwList {
4085
            let instanceThrowTy = try resolveValueType(self, throwNode);
4086
            if not typesEqual(instanceThrowTy, *tm.fnType.throwList[j]) {
4087
                throw emitTypeMismatch(self, throwNode, TypeMismatch {
4088
                    expected: *tm.fnType.throwList[j],
4089
                    actual: instanceThrowTy,
4090
                });
4091
            }
4092
        }
4093
4094
        // Build final function type: receiver plus trait's canonical types.
4095
        let a = alloc::arenaAllocator(&mut self.arena);
4096
        // TODO: Improve this pattern, maybe via something like `(&[]).append(..)`?
4097
        let mut paramTypes: *mut [*Type] = &mut [];
4098
        paramTypes.append(allocType(self, receiverPtrType), a);
4099
4100
        for ty in tm.fnType.paramTypes {
4101
            paramTypes.append(ty, a);
4102
        }
4103
        let fnType = FnType {
4104
            paramTypes: &paramTypes[..],
4105
            returnType: tm.fnType.returnType,
4106
            throwList: tm.fnType.throwList,
4107
            isUnsafe: tm.fnType.isUnsafe,
4108
        };
4109
4110
        // Create a symbol for the instance method without binding it into the
4111
        // module scope. Instance methods are dispatched via v-table, so they
4112
        // must not pollute the enclosing scope.
4113
        let fnTy = Type::Fn(allocFnType(self, fnType));
4114
        let mName = try nodeName(self, name);
4115
        let sym = allocSymbol(self, SymbolData::Value {
4116
            mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4117
        }, mName, methodNode, attrMask);
4118
4119
        setNodeSymbol(self, methodNode, sym);
4120
        setNodeType(self, methodNode, fnTy);
4121
        setNodeType(self, name, fnTy);
4122
4123
        // Store in instance entry at the matching v-table slot.
4124
        set entry.methods[tm.index] = sym;
4125
        set covered[tm.index] = true;
4126
    }
4127
4128
    // Fill inherited method slots from supertrait instances.
4129
    for superTrait in traitInfo.supertraits {
4130
        let superInst = findInstance(self, superTrait, concreteType)
4131
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
4132
        for superMethod, mi in superTrait.methods {
4133
            let merged = findTraitMethod(traitInfo, superMethod.name)
4134
                else panic "resolveInstanceDecl: inherited method not found";
4135
            if not covered[merged.index] {
4136
                set entry.methods[merged.index] = superInst.methods[mi];
4137
                set covered[merged.index] = true;
4138
            }
4139
        }
4140
    }
4141
4142
    // Check that all trait methods are implemented.
4143
    for method, i in traitInfo.methods {
4144
        if not covered[i] {
4145
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
4146
        }
4147
    }
4148
    set self.instances[self.instancesLen] = entry;
4149
    set self.instancesLen += 1;
4150
4151
    setNodeType(self, node, Type::Void);
4152
}
4153
4154
/// Resolve instance method bodies.
4155
unsafe fn resolveInstanceMethodBodies(self: &mut Resolver, methods: *[*ast::Node])
4156
    throws (ResolveError)
4157
{
4158
    for methodNode in methods {
4159
        let case ast::NodeValue::MethodDecl {
4160
            name, receiverName, receiverType, sig, body, ..
4161
        } = methodNode.value else continue;
4162
4163
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
4164
        // for this method (eg. unknown method name). Skip gracefully.
4165
        let sym = symbolFor(self, methodNode)
4166
            else continue;
4167
4168
        try resolveMethodBody(self, methodNode, receiverName, sig, body);
4169
    }
4170
}
4171
4172
/// Resolve a method body shared by instance methods and standalone methods.
4173
/// Binds the receiver and parameters, then type-checks the body.
4174
unsafe fn resolveMethodBody(
4175
    self: &mut Resolver,
4176
    node: *ast::Node,
4177
    receiverName: *ast::Node,
4178
    sig: ast::FnSig,
4179
    body: *ast::Node,
4180
) throws (ResolveError) {
4181
    let sym = symbolFor(self, node)
4182
        else throw emitError(self, node, ErrorKind::Internal);
4183
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
4184
        else panic "resolveMethodBody: expected value symbol";
4185
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body);
4186
}
4187
4188
/// Resolve a standalone method declaration (signature only).
4189
/// Validates the receiver type and registers the method in the method table.
4190
4191
/// Extract the type name from a resolved receiver type node.
4192
unsafe fn receiverTypeName(
4193
    self: &mut Resolver,
4194
    receiverType: *ast::Node,
4195
) -> *[u8] throws (ResolveError) {
4196
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4197
        receiverType.value
4198
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4199
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
4200
        else throw emitError(self, receiverType, ErrorKind::Internal);
4201
    let sym = symbolFor(self, nameNode)
4202
        else throw emitError(self, receiverType, ErrorKind::Internal);
4203
4204
    return sym.name;
4205
}
4206
4207
/// Resolve and register a standalone method declaration.
4208
unsafe fn resolveMethodDecl(
4209
    self: &mut Resolver,
4210
    node: *ast::Node,
4211
    name: *ast::Node,
4212
    receiverName: *ast::Node,
4213
    receiverType: *ast::Node,
4214
    sig: ast::FnSig,
4215
    attrs: ?ast::Attributes,
4216
) throws (ResolveError) {
4217
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
4218
    // nominal type.
4219
    let fullReceiverTy = try infer(self, receiverType);
4220
    let case Type::Pointer {
4221
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
4222
    } = fullReceiverTy
4223
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
4224
    let concreteType = *receiverTarget;
4225
    let case Type::Nominal(nominalTy) = concreteType
4226
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
4227
    try ensureNominalResolved(self, nominalTy, receiverType);
4228
4229
    // Get the type name from the inner type node's symbol.
4230
    let typeName = try receiverTypeName(self, receiverType);
4231
    let methodName = try nodeName(self, name);
4232
    let attrMask = resolveAttributes(self, attrs);
4233
4234
    // Reject duplicate method for the same (type, name).
4235
    if let _ = findMethod(self, concreteType, methodName) {
4236
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
4237
    }
4238
4239
    // Resolve parameter types.
4240
    let a = alloc::arenaAllocator(&mut self.arena);
4241
    let mut paramTypes: *mut [*Type] = &mut [];
4242
4243
    // Receiver is the first parameter.
4244
    let receiverPtrType = Type::Pointer {
4245
        class: receiverClass,
4246
        target: allocType(self, concreteType),
4247
        mutable: receiverMut,
4248
    };
4249
    paramTypes.append(allocType(self, receiverPtrType), a);
4250
4251
    for paramNode in sig.params {
4252
        let case ast::NodeValue::FnParam(param) = paramNode.value
4253
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
4254
        let paramTy = try resolveValueType(self, param.type);
4255
        paramTypes.append(allocType(self, paramTy), a);
4256
    }
4257
4258
    // Resolve return type.
4259
    let mut returnType = Type::Void;
4260
    if let retNode = sig.returnType {
4261
        set returnType = try resolveValueType(self, retNode);
4262
    }
4263
4264
    // Resolve throw list.
4265
    let mut throwTypes: *mut [*Type] = &mut [];
4266
    for throwNode in sig.throwList {
4267
        let throwTy = try resolveValueType(self, throwNode);
4268
        throwTypes.append(allocType(self, throwTy), a);
4269
    }
4270
4271
    let retTypePtr = allocType(self, returnType);
4272
    let throwList = &throwTypes[..];
4273
4274
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
4275
    // Full function type (receiver + params) for lowering.
4276
    let fullFnType = FnType {
4277
        paramTypes: &paramTypes[..],
4278
        returnType: retTypePtr,
4279
        throwList,
4280
        isUnsafe,
4281
    };
4282
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
4283
4284
    // Function type excluding receiver, for call arg checking.
4285
    let checkFnType = FnType {
4286
        paramTypes: &paramTypes[1..],
4287
        returnType: retTypePtr,
4288
        throwList,
4289
        isUnsafe,
4290
    };
4291
4292
    // Create a symbol for the method without binding it into the module scope.
4293
    let sym = allocSymbol(self, SymbolData::Value {
4294
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
4295
    }, methodName, node, attrMask);
4296
4297
    setNodeSymbol(self, node, sym);
4298
    setNodeType(self, node, fnTy);
4299
    setNodeType(self, name, fnTy);
4300
4301
    // Register in the method table.
4302
    if self.methodsLen >= MAX_METHODS {
4303
        throw emitError(self, node, ErrorKind::Internal);
4304
    }
4305
    set self.methods[self.methodsLen] = MethodEntry {
4306
        concreteType,
4307
        concreteTypeName: typeName,
4308
        name: methodName,
4309
        fnType: allocFnType(self, checkFnType),
4310
        mutable: receiverMut,
4311
        receiverClass,
4312
        symbol: sym,
4313
    };
4314
    set self.methodsLen += 1;
4315
}
4316
4317
/// Look up an instance entry by trait and concrete type.
4318
unsafe fn findInstance(self: &Resolver, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
4319
    for i in 0..self.instancesLen {
4320
        let entry: *unsafe InstanceEntry = &self.instances[i];
4321
        if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) {
4322
            return entry;
4323
        }
4324
    }
4325
    return nil;
4326
}
4327
4328
/// Look up a standalone method by concrete type and name.
4329
export unsafe fn findMethod(self: &Resolver, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
4330
    for i in 0..self.methodsLen {
4331
        let entry: *unsafe MethodEntry = &self.methods[i];
4332
        if typesEqual(entry.concreteType, concreteType) and entry.name == name {
4333
            return entry;
4334
        }
4335
    }
4336
    return nil;
4337
}
4338
4339
/// Look up a standalone method entry by its symbol.
4340
export unsafe fn findMethodBySymbol(self: &Resolver, sym: *unsafe mut Symbol) -> ?*unsafe MethodEntry {
4341
    for i in 0..self.methodsLen {
4342
        let entry: *unsafe MethodEntry = &self.methods[i];
4343
        if entry.symbol == sym {
4344
            return entry;
4345
        }
4346
    }
4347
    return nil;
4348
}
4349
4350
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
4351
unsafe fn resolveUnionBody(self: &mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
4352
    throws (ResolveError)
4353
{
4354
    // Get the type symbol that was bound to this declaration node.
4355
    // If there's no symbol, it's because an earlier phase failed.
4356
    let sym = symbolFor(self, node)
4357
        else return;
4358
    let case SymbolData::Type(nominalTy) = sym.data
4359
        else panic "resolveUnionBody: unexpected symbol data";
4360
4361
    // Check if already resolved, in which case there's no need to
4362
    // do it again.
4363
    if let case NominalType::Union(_) = *nominalTy {
4364
        return;
4365
    }
4366
    let a = alloc::arenaAllocator(&mut self.arena);
4367
    let mut variants: *unsafe mut [UnionVariant] = &mut [];
4368
4369
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
4370
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
4371
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4372
    let markers = try resolveOwnershipMarkers(self, decl.derives);
4373
    set *nominalTy = NominalType::Union(UnionType {
4374
        variants: &[],
4375
        layout: Layout { size: 0, alignment: 0 },
4376
        valOffset: 0,
4377
        isAllVoid: true,
4378
        declaredLinear: markers.linear,
4379
        declaredCopy: markers.copy,
4380
    });
4381
4382
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
4383
    let mut iota: u32 = 0;
4384
    for variantNode, i in decl.variants {
4385
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
4386
            else panic "resolveUnionBody: invalid union variant";
4387
        let variantName = try nodeName(self, variantDecl.name);
4388
        // Resolve the variant's payload type if present.
4389
        let mut variantType = Type::Void;
4390
        if let typeNode = variantDecl.type {
4391
            set variantType = try infer(self, typeNode);
4392
            try ensureStorableType(self, typeNode, variantType);
4393
        }
4394
        // Process the variant's explicit discriminant value if present.
4395
        try visitOptional(self, variantDecl.value, variantType);
4396
        let tag = variantTag(variantDecl, &mut iota);
4397
        // Create a symbol for this variant.
4398
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
4399
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
4400
4401
        variants.append(UnionVariant {
4402
            name: variantName,
4403
            valueType: variantType,
4404
            symbol: variantSym,
4405
        }, a);
4406
    }
4407
    if markers.copy {
4408
        for variant in variants {
4409
            if not isCopy(variant.valueType) {
4410
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
4411
            }
4412
        }
4413
    }
4414
    let info = computeUnionLayout(&variants[..]);
4415
4416
    // Update the nominal type with the resolved variants.
4417
    set *nominalTy = NominalType::Union(UnionType {
4418
        variants: &variants[..],
4419
        layout: info.layout,
4420
        valOffset: info.valOffset,
4421
        isAllVoid: info.isAllVoid,
4422
        declaredLinear: markers.linear,
4423
        declaredCopy: markers.copy,
4424
    });
4425
}
4426
4427
/// Check if a module should be analyzed based on its attributes and build configuration.
4428
fn shouldAnalyzeModule(self: &Resolver, attrs: ?ast::Attributes) -> bool {
4429
    if let attributes = attrs {
4430
        // Skip test modules unless we're building in test mode.
4431
        if ast::attributesContains(&attributes, ast::Attribute::Test) and not self.config.buildTest {
4432
            return false;
4433
        }
4434
    }
4435
    return true;
4436
}
4437
4438
/// Analyze a module during the graph analysis phase.
4439
unsafe fn resolveModGraph(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
4440
    throws (ResolveError)
4441
{
4442
    if not shouldAnalyzeModule(self, decl.attrs) {
4443
        return;
4444
    }
4445
    let modName = try nodeName(self, decl.name);
4446
    let attrMask = resolveAttributes(self, decl.attrs);
4447
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
4448
    let submod = try enterSubModule(self, modName, node);
4449
4450
    // Bind the module symbol in the outer scope, ie. where the `mod` statement is.
4451
    try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope);
4452
    let case ast::NodeValue::Block(block) = submod.root.value
4453
        else panic "resolveModGraph: expected block for module root";
4454
    try resolveModuleGraph(self, &block);
4455
4456
    exitModuleScope(self, submod);
4457
}
4458
4459
/// Analyze a module in the declaration phase.
4460
unsafe fn resolveModDecl(self: &mut Resolver, node: *ast::Node, decl: ast::Mod)
4461
    throws (ResolveError)
4462
{
4463
    if not shouldAnalyzeModule(self, decl.attrs) {
4464
        return;
4465
    }
4466
    // Find module under the current module.
4467
    let modName = try nodeName(self, decl.name);
4468
    let submod = try enterSubModule(self, modName, node);
4469
    let case ast::NodeValue::Block(block) = submod.root.value
4470
        else panic "resolveModDecl: expected block for module root";
4471
    try resolveModuleDecls(self, &block);
4472
4473
    exitModuleScope(self, submod);
4474
}
4475
4476
/// Analyze a `use` statement and create a symbol for the imported module.
4477
unsafe fn resolveUse(self: &mut Resolver, node: *ast::Node, decl: ast::Use) -> Type
4478
    throws (ResolveError)
4479
{
4480
    let resolved = try resolveModulePath(self, decl.path);
4481
    let attrMask = resolveAttributes(self, decl.attrs);
4482
4483
    if decl.wildcard {
4484
        // Import all public symbols from the target module.
4485
        for i in 0..resolved.scope.symbolsLen {
4486
            let sym = resolved.scope.symbols[i];
4487
            if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
4488
                if let existing = findSymbolInScope(self.scope, sym.name) {
4489
                    if existing == sym {
4490
                        continue;
4491
                    }
4492
                }
4493
                let scope = self.scope;
4494
                try addSymbolToScope(self, sym, scope, node);
4495
            }
4496
        }
4497
    } else {
4498
        // Regular module import.
4499
        let scope = self.scope;
4500
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope);
4501
    }
4502
    return Type::Void;
4503
}
4504
4505
/// Analyze a standard `if` statement.
4506
unsafe fn resolveIf(self: &mut Resolver, node: *ast::Node, cond: ast::If) -> Type
4507
    throws (ResolveError)
4508
{
4509
    try checkBoolean(self, cond.condition);
4510
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4511
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4512
4513
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4514
}
4515
4516
/// Analyze a conditional expression.
4517
unsafe fn resolveCondExpr(self: &mut Resolver, node: *ast::Node, cond: ast::CondExpr) -> Type
4518
    throws (ResolveError)
4519
{
4520
    try checkBoolean(self, cond.condition);
4521
    let thenTy = try infer(self, cond.thenExpr);
4522
    let elseTy = try infer(self, cond.elseExpr);
4523
4524
    // Either branch may supply the concrete type for an otherwise context-
4525
    // dependent expression, such as an unsuffixed integer or `nil`.
4526
    if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) {
4527
        setNodeCoercion(self, cond.elseExpr, coercion);
4528
        return setNodeType(self, node, thenTy);
4529
    }
4530
    if let coercion = isAssignable(self, elseTy, thenTy, cond.thenExpr) {
4531
        setNodeCoercion(self, cond.thenExpr, coercion);
4532
        return setNodeType(self, node, elseTy);
4533
    }
4534
    try expectAssignable(self, thenTy, elseTy, cond.elseExpr);
4535
4536
    return setNodeType(self, node, thenTy);
4537
}
4538
4539
/// Analyze a pattern match structure (used by if-let, while-let).
4540
unsafe fn resolvePatternMatch(self: &mut Resolver, node: *ast::Node, pat: &ast::PatternMatch)
4541
    throws (ResolveError)
4542
{
4543
    match pat.kind {
4544
        case ast::PatternKind::Case => {
4545
            // Analyze pattern against scrutinee type.
4546
            let scrutineeTy = try infer(self, pat.scrutinee);
4547
            if isUnsafePointerType(scrutineeTy) {
4548
                try requireUnsafe(self, pat.scrutinee);
4549
            }
4550
            let subject = unwrapMatchSubject(scrutineeTy);
4551
            try resolveCasePattern(self, pat.pattern, subject.effectiveTy, IdentMode::Compare, subject.by);
4552
        }
4553
        case ast::PatternKind::Binding => {
4554
            // Scrutinee must be optional, bind the payload.
4555
            let scrutineeTy = try checkOptional(self, pat.scrutinee);
4556
            let payloadTy = *scrutineeTy;
4557
4558
            try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
4559
            setNodeType(self, pat.pattern, payloadTy);
4560
        }
4561
    }
4562
    if let guard = pat.guard {
4563
        try checkBoolean(self, guard);
4564
    }
4565
}
4566
4567
/// Analyze an `if let` or `if let case` pattern binding.
4568
unsafe fn resolveIfLet(self: &mut Resolver, node: *ast::Node, cond: ast::IfLet) -> Type
4569
    throws (ResolveError)
4570
{
4571
    enterScope(self, node);
4572
    try resolvePatternMatch(self, node, &cond.pattern);
4573
4574
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
4575
    exitScope(self);
4576
4577
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
4578
4579
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
4580
}
4581
4582
/// Controls how bare identifiers are handled in case patterns.
4583
union IdentMode: Copy {
4584
    /// Identifier is a value to compare against.
4585
    Compare,
4586
    /// Identifier introduces a new binding.
4587
    Bind,
4588
}
4589
4590
/// Check whether a pattern node is a destructuring pattern that looks
4591
/// through structure (union variant, record literal, scope access).
4592
/// Identifiers, placeholders, and plain literals are not destructuring.
4593
export fn isDestructuringPattern(pattern: *ast::Node) -> bool {
4594
    match pattern.value {
4595
        case ast::NodeValue::Call(_),
4596
             ast::NodeValue::RecordLit(_),
4597
             ast::NodeValue::ScopeAccess(_) => return true,
4598
        else => return false,
4599
    }
4600
}
4601
4602
/// Analyze a case pattern for match, if-case, let-case, or while-case.
4603
///
4604
/// At the top level, bare identifiers are compared against existing values.
4605
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
4606
unsafe fn resolveCasePattern(
4607
    self: &mut Resolver,
4608
    pattern: *ast::Node,
4609
    scrutineeTy: Type,
4610
    mode: IdentMode,
4611
    matchBy: MatchBy
4612
) throws (ResolveError) {
4613
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
4614
        if isUnsafePointerType(scrutineeTy) {
4615
            try requireUnsafe(self, pattern);
4616
        }
4617
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
4618
        return;
4619
    }
4620
    // TODO: Collapse these nested matches.
4621
    match scrutineeTy {
4622
        case Type::Nominal(info) => {
4623
            try ensureNominalResolved(self, info, pattern);
4624
4625
            match *info {
4626
                case NominalType::Union(unionType) => {
4627
                    try resolveUnionPattern(self, pattern, scrutineeTy, unionType, matchBy);
4628
                    return;
4629
                }
4630
                case NominalType::Record(recInfo) => {
4631
                    match pattern.value {
4632
                        case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => {
4633
                            try bindRecordPatternFields(self, pattern, recInfo, matchBy);
4634
                            return;
4635
                        } else => {}
4636
                    }
4637
                } else => {}
4638
            }
4639
        }
4640
        case Type::Array(arrayInfo) => {
4641
            if let case ast::NodeValue::ArrayLit(items) = pattern.value {
4642
                if items.len as u32 <> arrayInfo.length {
4643
                    throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(
4644
                        CountMismatch { expected: arrayInfo.length, actual: items.len as u32 }
4645
                    ));
4646
                }
4647
                let elemTy = *arrayInfo.item;
4648
                for item in items {
4649
                    try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy);
4650
                }
4651
                setNodeType(self, pattern, scrutineeTy);
4652
                return;
4653
            }
4654
        } else => {}
4655
    }
4656
    // Handle non-binding patterns (literals, placeholders) and bindings.
4657
    match pattern.value {
4658
        case ast::NodeValue::Placeholder => {
4659
            // Placeholder matches without introducing bindings.
4660
        }
4661
        case ast::NodeValue::Ident(_) => {
4662
            match mode {
4663
                case IdentMode::Bind => try bindPatternVar(self, pattern, scrutineeTy, matchBy),
4664
                case IdentMode::Compare => try checkAssignable(self, pattern, scrutineeTy),
4665
            }
4666
        }
4667
        else => {
4668
            // Literals and other expressions: check type compatibility.
4669
            try checkAssignable(self, pattern, scrutineeTy);
4670
        }
4671
    }
4672
}
4673
4674
/// Analyze a traditional `while` loop.
4675
unsafe fn resolveWhile(self: &mut Resolver, node: *ast::Node, loopNode: ast::While) -> Type
4676
    throws (ResolveError)
4677
{
4678
    try checkBoolean(self, loopNode.condition);
4679
    let loopTy = try visitLoop(self, loopNode.body);
4680
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4681
4682
    if loopNode.condition.value == ast::NodeValue::Bool(true) {
4683
        return setNodeType(self, node, loopTy);
4684
    }
4685
    return setNodeType(self, node, Type::Void);
4686
}
4687
4688
/// Analyze a `while let` loop with pattern binding.
4689
unsafe fn resolveWhileLet(self: &mut Resolver, node: *ast::Node, loopNode: ast::WhileLet) -> Type
4690
    throws (ResolveError)
4691
{
4692
    enterScope(self, node);
4693
    try resolvePatternMatch(self, node, &loopNode.pattern);
4694
4695
    try visitLoop(self, loopNode.body);
4696
    exitScope(self);
4697
4698
    try visitOptional(self, loopNode.elseBranch, Type::Void);
4699
4700
    return setNodeType(self, node, Type::Void);
4701
}
4702
4703
/// Analyze a `for` loop, binding iteration variables.
4704
unsafe fn resolveFor(self: &mut Resolver, node: *ast::Node, forStmt: ast::For) -> Type
4705
    throws (ResolveError)
4706
{
4707
    let iterableTy = try infer(self, forStmt.iterable);
4708
4709
    // Extract binding names for the lowerer.
4710
    let mut bindingName: ?*[u8] = nil;
4711
    if let case ast::NodeValue::Ident(name) = forStmt.binding.value {
4712
        set bindingName = name;
4713
    }
4714
    let mut indexName: ?*[u8] = nil;
4715
    if let idx = forStmt.index {
4716
        if let case ast::NodeValue::Ident(name) = idx.value {
4717
            set indexName = name;
4718
        }
4719
    }
4720
    // Extract item type and store pre-computed loop metadata for the lowerer.
4721
    let mut itemTy: Type = undefined;
4722
    match iterableTy {
4723
        case Type::Slice { item, class, .. } => {
4724
            if class == types::PointerClass::Unsafe {
4725
                try requireUnsafe(self, forStmt.iterable);
4726
            }
4727
            set itemTy = *item;
4728
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4729
                elemType: item, length: nil, bindingName, indexName
4730
            });
4731
        }
4732
        case Type::Range { start, .. } => {
4733
            // Iterable ranges must have a start, and since we enforce type
4734
            // equality for start and end, that is always the item type.
4735
            let valType = start else {
4736
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4737
            };
4738
            let case ast::NodeValue::Range(range) = forStmt.iterable.value else {
4739
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4740
            };
4741
            set itemTy = *valType;
4742
4743
            setForLoopInfo(self, node, ForLoopInfo::Range {
4744
                valType, range, bindingName, indexName
4745
            });
4746
        }
4747
        case Type::Array(arrayInfo) => {
4748
            set itemTy = *arrayInfo.item;
4749
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4750
                elemType: arrayInfo.item,
4751
                length: arrayInfo.length,
4752
                bindingName,
4753
                indexName,
4754
            });
4755
        }
4756
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
4757
    }
4758
    enterScope(self, node);
4759
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
4760
4761
    if let pat = forStmt.index {
4762
        try bindForLoopPattern(self, pat, Type::U32, false);
4763
    }
4764
    // The lowerer always creates at least one internal variable for iteration,
4765
    // even when the binding is a placeholder or no explicit index is given.
4766
    if let owner = self.currentFnNode {
4767
        set self.nodeData.entries[owner.id].localCount += 1;
4768
    }
4769
    try visitLoop(self, forStmt.body);
4770
    exitScope(self);
4771
4772
    try visitOptional(self, forStmt.elseBranch, Type::Void);
4773
4774
    return setNodeType(self, node, Type::Void);
4775
}
4776
4777
/// Get the node within a pattern that carries the `UnionVariant` extra.
4778
/// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the
4779
/// type name, and for `Call` it is the callee.
4780
export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node {
4781
    match pattern.value {
4782
        case ast::NodeValue::ScopeAccess(_) => return pattern,
4783
        case ast::NodeValue::RecordLit(lit) => return lit.typeName,
4784
        case ast::NodeValue::Call(call) => return call.callee,
4785
        else => return nil,
4786
    }
4787
}
4788
4789
/// Get the i-th sub-pattern element from a compound pattern.
4790
/// For `RecordLit` this is the i-th field's value; for `Call` it is the
4791
/// i-th argument.
4792
fn patternSubElement(pattern: *ast::Node, idx: u32) -> ?*ast::Node {
4793
    match pattern.value {
4794
        case ast::NodeValue::RecordLit(lit) => {
4795
            if idx < lit.fields.len as u32 {
4796
                if let case ast::NodeValue::RecordLitField(field) = lit.fields[idx].value {
4797
                    return field.value;
4798
                }
4799
            }
4800
        }
4801
        case ast::NodeValue::Call(call) => {
4802
            if idx < call.args.len as u32 {
4803
                return call.args[idx];
4804
            }
4805
        }
4806
        else => {}
4807
    }
4808
    return nil;
4809
}
4810
4811
/// Get the number of sub-pattern elements in a compound pattern.
4812
fn patternSubCount(pattern: *ast::Node) -> u32 {
4813
    match pattern.value {
4814
        case ast::NodeValue::RecordLit(lit) => return lit.fields.len as u32,
4815
        case ast::NodeValue::Call(call) => return call.args.len as u32,
4816
        else => return 0,
4817
    }
4818
}
4819
4820
/// Check whether a pattern contains nested sub-patterns that further
4821
/// refine the match beyond the outer variant (e.g. nested union variant
4822
/// tests or literal comparisons). Used to allow the same outer variant
4823
/// to appear in multiple match arms.
4824
fn hasNestedRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
4825
    for i in 0..patternSubCount(pattern) {
4826
        if let sub = patternSubElement(pattern, i) {
4827
            if isRefiningPattern(self, sub) {
4828
                return true;
4829
            }
4830
        }
4831
    }
4832
    return false;
4833
}
4834
4835
/// Check whether a single pattern node is a refining pattern that tests
4836
/// a value rather than just binding it. Union variants, literals, and
4837
/// scope accesses are refining; identifiers, placeholders, and plain
4838
/// record destructurings are not.
4839
fn isRefiningPattern(self: &Resolver, pattern: *ast::Node) -> bool {
4840
    match pattern.value {
4841
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
4842
            return false,
4843
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
4844
            if let keyNode = patternVariantKeyNode(pattern) {
4845
                if let case NodeExtra::UnionVariant { .. } = self.nodeData.entries[keyNode.id].extra {
4846
                    return true;
4847
                }
4848
            }
4849
            // Plain record destructuring / non-variant call is not directly
4850
            // refining; recurse to check sub-patterns.
4851
            return hasNestedRefiningPattern(self, pattern);
4852
        }
4853
        case ast::NodeValue::ArrayLit(items) => {
4854
            for item in items {
4855
                if isRefiningPattern(self, item) {
4856
                    return true;
4857
                }
4858
            }
4859
            return false;
4860
        }
4861
        case ast::NodeValue::ScopeAccess(_) =>
4862
            return true,
4863
        else =>
4864
            return true,
4865
    }
4866
}
4867
4868
/// Check whether any pattern in a case prong matches unconditionally.
4869
/// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies.
4870
/// Note: top-level identifiers in `case` are comparisons, not bindings,
4871
/// so they do not count as wildcards.
4872
fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool {
4873
    for pattern in patterns {
4874
        match pattern.value {
4875
            case ast::NodeValue::Placeholder => return true,
4876
            case ast::NodeValue::ArrayLit(items) => {
4877
                if isIrrefutableArrayPattern(items) {
4878
                    return true;
4879
                }
4880
            }
4881
            else => {}
4882
        }
4883
    }
4884
    return false;
4885
}
4886
4887
/// Check whether all elements of an array pattern are irrefutable.
4888
/// Inside array patterns, identifiers are bindings, not comparisons.
4889
fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool {
4890
    for item in items {
4891
        match item.value {
4892
            case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {}
4893
            case ast::NodeValue::ArrayLit(inner) => {
4894
                if not isIrrefutableArrayPattern(inner) {
4895
                    return false;
4896
                }
4897
            }
4898
            else => return false,
4899
        }
4900
    }
4901
    return true;
4902
}
4903
4904
/// Analyze a match prong, checking for duplicate catch-alls. Returns the
4905
/// unified match type.
4906
unsafe fn resolveMatchProng(
4907
    self: &mut Resolver,
4908
    prongNode: *ast::Node,
4909
    prong: ast::MatchProng,
4910
    subjectTy: Type,
4911
    state: &mut MatchState,
4912
    matchType: Type,
4913
    matchBy: MatchBy
4914
) -> Type throws (ResolveError) {
4915
    // Whether this prong is catch-all.
4916
    let mut isCatchAll = false;
4917
4918
    if prong.guard <> nil {
4919
        set state.isConst = false;
4920
    } else {
4921
        match prong.arm {
4922
            case ast::ProngArm::Binding(_),
4923
                 ast::ProngArm::Else => set isCatchAll = true,
4924
            case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
4925
        }
4926
    }
4927
    if isCatchAll {
4928
        if state.catchAll {
4929
            throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
4930
        }
4931
        set state.catchAll = true;
4932
    }
4933
    setProngCatchAll(self, prongNode, isCatchAll);
4934
4935
    return try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
4936
}
4937
4938
/// Analyze a `match` expression. Dispatches to specialized functions based on
4939
/// the subject type.
4940
unsafe fn resolveMatch(self: &mut Resolver, node: *ast::Node, sw: ast::Match) -> Type
4941
    throws (ResolveError)
4942
{
4943
    let subjectTy = try infer(self, sw.subject);
4944
    if isUnsafePointerType(subjectTy) {
4945
        try requireUnsafe(self, sw.subject);
4946
    }
4947
    let subject = unwrapMatchSubject(subjectTy);
4948
4949
    if let case Type::Optional(inner) = subject.effectiveTy {
4950
        try resolveMatchOptional(self, node, sw, inner, subject.by);
4951
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
4952
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4953
    } else {
4954
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4955
    }
4956
4957
    // Mark last non-guarded prong as exhaustive.
4958
    let lastProng = sw.prongs[sw.prongs.len - 1];
4959
    let case ast::NodeValue::MatchProng(p) = lastProng.value
4960
        else panic "resolveMatch: expected match prong";
4961
    if p.guard == nil {
4962
        setProngCatchAll(self, lastProng, true);
4963
    }
4964
    let ty = typeFor(self, node) else {
4965
        return Type::Void;
4966
    };
4967
    return ty;
4968
}
4969
4970
/// Analyze a `match` expression on an optional subject.
4971
unsafe fn resolveMatchOptional(
4972
    self: &mut Resolver,
4973
    node: *ast::Node,
4974
    sw: ast::Match,
4975
    innerTy: *Type,
4976
    matchBy: MatchBy
4977
) -> Type throws (ResolveError)
4978
{
4979
    let subjectTy = Type::Optional(innerTy);
4980
    let prongs = sw.prongs;
4981
    let mut hasValue = false;
4982
    let mut hasNil = false;
4983
    let mut catchAll = false;
4984
    let mut matchType = Type::Never;
4985
4986
    for prongNode in prongs {
4987
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
4988
            else panic "resolveMatchOptional: expected match prong";
4989
4990
        let mut isCatchAll = false;
4991
        if prong.guard == nil {
4992
            match prong.arm {
4993
                case ast::ProngArm::Else => set isCatchAll = true,
4994
                case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
4995
                case ast::ProngArm::Binding(_) => {
4996
                    // For optionals, a binding does *not* always match.
4997
                }
4998
            }
4999
        }
5000
        if isCatchAll {
5001
            if catchAll {
5002
                throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
5003
            }
5004
            set catchAll = true;
5005
        }
5006
        setProngCatchAll(self, prongNode, isCatchAll);
5007
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
5008
5009
        // Track coverage. Guarded prongs don't count as covering a case.
5010
        if prong.guard == nil {
5011
            if let case ast::ProngArm::Binding(_) = prong.arm {
5012
                if hasValue {
5013
                    throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern);
5014
                }
5015
                set hasValue = true;
5016
            } else if let case ast::ProngArm::Case(patterns) = prong.arm {
5017
                for pat in patterns {
5018
                    if let case ast::NodeValue::Nil = pat.value {
5019
                        if hasNil {
5020
                            throw emitError(self, pat, ErrorKind::DuplicateMatchPattern);
5021
                        }
5022
                        set hasNil = true;
5023
                    }
5024
                }
5025
            }
5026
        }
5027
    }
5028
5029
    // Check exhaustiveness.
5030
    if not catchAll {
5031
        if not hasValue {
5032
            throw emitError(self, node, ErrorKind::OptionalMatchMissingValue);
5033
        }
5034
        if not hasNil {
5035
            throw emitError(self, node, ErrorKind::OptionalMatchMissingNil);
5036
        }
5037
    } else if hasValue and hasNil {
5038
        throw emitError(self, node, ErrorKind::UnreachableElse);
5039
    }
5040
    return setNodeType(self, node, matchType);
5041
}
5042
5043
/// Analyze a `match` expression on a union subject.
5044
unsafe fn resolveMatchUnion(
5045
    self: &mut Resolver,
5046
    node: *ast::Node,
5047
    sw: ast::Match,
5048
    subjectTy: Type,
5049
    info: UnionType,
5050
    matchBy: MatchBy
5051
) -> Type throws (ResolveError) {
5052
    let prongs = sw.prongs;
5053
    let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS];
5054
    let mut coveredCount: u32 = 0;
5055
    let mut state = MatchState { catchAll: false, isConst: false };
5056
    let mut matchType = Type::Never;
5057
5058
    for prongNode in prongs {
5059
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
5060
            else panic "resolveMatchUnion: expected match prong";
5061
5062
        set matchType = try resolveMatchProng(self, prongNode, prong, subjectTy, &mut state, matchType, matchBy);
5063
5064
        // Guarded prongs don't count as covering. Patterns with nested
5065
        // refining sub-patterns (e.g. matching different inner union variants)
5066
        // don't count as duplicates or as fully covering.
5067
        if prong.guard == nil {
5068
            if let case ast::ProngArm::Case(patterns) = prong.arm {
5069
                for pattern in patterns {
5070
                    if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra {
5071
                        if not hasNestedRefiningPattern(self, pattern) {
5072
                            if covered[ix] {
5073
                                throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern);
5074
                            }
5075
                            set covered[ix] = true;
5076
                            set coveredCount += 1;
5077
                        }
5078
                    }
5079
                }
5080
            }
5081
        }
5082
    }
5083
    // Check that all variants are covered.
5084
    if not state.catchAll {
5085
        for variant, i in info.variants {
5086
            if not covered[i] {
5087
                throw emitError(
5088
                    self, node, ErrorKind::UnionMatchNonExhaustive(variant.name)
5089
                );
5090
            }
5091
        }
5092
    } else if coveredCount == info.variants.len as u32 {
5093
        throw emitError(self, node, ErrorKind::UnreachableElse);
5094
    }
5095
    return setNodeType(self, node, matchType);
5096
}
5097
5098
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
5099
/// booleans must cover both `true` and `false`, other types require a catch-all.
5100
unsafe fn resolveMatchGeneric(self: &mut Resolver, node: *ast::Node, sw: ast::Match, subjectTy: Type) -> Type
5101
    throws (ResolveError)
5102
{
5103
    let prongs = sw.prongs;
5104
    let mut state = MatchState { catchAll: false, isConst: true };
5105
    let mut matchType = Type::Never;
5106
    let mut hasTrue = false;
5107
    let mut hasFalse = false;
5108
    let mut hasConstCase = false;
5109
5110
    for prongNode in prongs {
5111
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
5112
            else panic "resolveMatchGeneric: expected match prong";
5113
5114
        set matchType = try resolveMatchProng(
5115
            self, prongNode, prong, subjectTy, &mut state, matchType, MatchBy::Value
5116
        );
5117
        // Track boolean coverage. Guarded prongs don't count as covering.
5118
        if let case ast::ProngArm::Case(patterns) = prong.arm {
5119
            for p in patterns {
5120
                if prong.guard == nil {
5121
                    if let case ast::NodeValue::Bool(val) = p.value {
5122
                        if (val and hasTrue) or (not val and hasFalse) {
5123
                            throw emitError(self, p, ErrorKind::DuplicateMatchPattern);
5124
                        }
5125
                        if val {
5126
                            set hasTrue = true;
5127
                        } else {
5128
                            set hasFalse = true;
5129
                        }
5130
                    }
5131
                }
5132
                // Scalar constant patterns allow the match to be lowered
5133
                // to a switch instruction.
5134
                if let c = constValueEntry(self, p) {
5135
                    match c {
5136
                        case ConstValue::Bool(_), ConstValue::Char(_), ConstValue::Int(_) =>
5137
                            set hasConstCase = true,
5138
                        else =>
5139
                            set state.isConst = false,
5140
                    }
5141
                }
5142
            }
5143
        }
5144
    }
5145
5146
    // Check exhaustiveness.
5147
    if not state.catchAll {
5148
        if let case Type::Bool = subjectTy {
5149
            if not hasTrue {
5150
                throw emitError(self, node, ErrorKind::BoolMatchMissing(true));
5151
            }
5152
            if not hasFalse {
5153
                throw emitError(self, node, ErrorKind::BoolMatchMissing(false));
5154
            }
5155
        } else {
5156
            throw emitError(self, node, ErrorKind::MatchNonExhaustive);
5157
        }
5158
    } else if let case Type::Bool = subjectTy {
5159
        if hasTrue and hasFalse {
5160
            throw emitError(self, node, ErrorKind::UnreachableElse);
5161
        }
5162
    }
5163
    setMatchConst(self, node, state.isConst and hasConstCase);
5164
5165
    return setNodeType(self, node, matchType);
5166
}
5167
5168
/// Analyze a single `match` prong branch. Returns the unified match type.
5169
unsafe fn visitMatchProng(
5170
    self: &mut Resolver,
5171
    node: *ast::Node,
5172
    prongNode: ast::MatchProng,
5173
    subjectTy: Type,
5174
    matchType: Type,
5175
    matchBy: MatchBy
5176
) -> Type throws (ResolveError) {
5177
    enterScope(self, node);
5178
    let prongTy = try resolveMatchProngBody(self, prongNode, subjectTy, matchBy) catch e {
5179
        exitScope(self);
5180
        throw e;
5181
    };
5182
    exitScope(self);
5183
    setNodeType(self, node, prongTy);
5184
5185
    return unifyBranches(matchType, prongTy);
5186
}
5187
5188
/// Analyze the contents of a `match` prong while inside the prong scope.
5189
unsafe fn resolveMatchProngBody(
5190
    self: &mut Resolver,
5191
    prong: ast::MatchProng,
5192
    subjectTy: Type,
5193
    matchBy: MatchBy
5194
) -> Type throws (ResolveError) {
5195
    match prong.arm {
5196
        case ast::ProngArm::Binding(pat) => {
5197
            // For optionals, bind the unwrapped inner type.
5198
            let mut bindTy = subjectTy;
5199
            if let case Type::Optional(inner) = subjectTy {
5200
                set bindTy = *inner;
5201
            }
5202
            try bindPatternVar(self, pat, bindTy, matchBy);
5203
        }
5204
        case ast::ProngArm::Case(patterns) => {
5205
            for pattern in patterns {
5206
                try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy);
5207
            }
5208
        }
5209
        case ast::ProngArm::Else => {}
5210
    }
5211
    if let g = prong.guard {
5212
        try checkBoolean(self, g);
5213
    }
5214
    return try visit(self, prong.body, Type::Void);
5215
}
5216
5217
/// Ensure a scope access pattern references a compatible union variant.
5218
unsafe fn resolveUnionScopePattern(
5219
    self: &mut Resolver,
5220
    pattern: *ast::Node,
5221
    access: ast::Access,
5222
    subjectTy: Type,
5223
    unionType: UnionType
5224
) throws (ResolveError) {
5225
    let patternTy = try visit(self, pattern, subjectTy);
5226
    if not isComparable(patternTy, subjectTy) {
5227
        throw emitTypeMismatch(self, pattern, TypeMismatch {
5228
            expected: subjectTy,
5229
            actual: patternTy,
5230
        });
5231
    }
5232
    let case NodeExtra::UnionVariant { ordinal: index, .. } = self.nodeData.entries[pattern.id].extra else {
5233
        throw emitError(self, pattern, ErrorKind::Internal);
5234
    };
5235
    let variant = &unionType.variants[index];
5236
    // If this variant has a payload, throw an error, since the user hasn't
5237
    // provided one.
5238
    if variant.valueType <> Type::Void {
5239
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
5240
    }
5241
}
5242
5243
/// Validate and bind a union constructor call used as a `match` pattern.
5244
unsafe fn resolveUnionCallPattern(
5245
    self: &mut Resolver,
5246
    pattern: *ast::Node,
5247
    call: ast::Call,
5248
    subjectTy: Type,
5249
    unionType: UnionType,
5250
    matchBy: MatchBy
5251
) throws (ResolveError) {
5252
    let calleeTy = try checkEqual(self, call.callee, subjectTy);
5253
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
5254
        throw emitError(self, call.callee, ErrorKind::Internal);
5255
    };
5256
    let variant = &unionType.variants[index];
5257
    // Copy variant index to the pattern node for the lowerer.
5258
    setVariantInfo(self, pattern, index, tag);
5259
5260
    if variant.valueType <> Type::Void {
5261
        try bindUnionPatternPayload(self, pattern, call, variant.name, variant.valueType, matchBy);
5262
    } else {
5263
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5264
    }
5265
}
5266
5267
/// Bind the payload introduced by a union constructor pattern.
5268
unsafe fn bindUnionPatternPayload(
5269
    self: &mut Resolver,
5270
    pattern: *ast::Node,
5271
    call: ast::Call,
5272
    variantName: *[u8],
5273
    payloadTy: Type,
5274
    matchBy: MatchBy
5275
) throws (ResolveError) {
5276
    if call.args.len == 0 {
5277
        throw emitError(
5278
            self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName)
5279
        );
5280
    }
5281
    // All variant payloads are records.
5282
    let recInfo = getRecord(payloadTy)
5283
        else panic "bindUnionPatternPayload: payload is not a record";
5284
5285
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5286
}
5287
5288
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
5289
unsafe fn bindPatternVar(self: &mut Resolver, binding: *ast::Node, ty: Type, matchBy: MatchBy)
5290
    throws (ResolveError)
5291
{
5292
    let mut bindTy = ty;
5293
    match matchBy {
5294
        case MatchBy::Value => {}
5295
        case MatchBy::Ref => set bindTy = Type::Pointer {
5296
            class: types::PointerClass::Ref,
5297
            target: allocType(self, ty),
5298
            mutable: false,
5299
        },
5300
        case MatchBy::MutRef => set bindTy = Type::Pointer {
5301
            class: types::PointerClass::Ref,
5302
            target: allocType(self, ty),
5303
            mutable: true,
5304
        },
5305
    }
5306
    match binding.value {
5307
        case ast::NodeValue::Placeholder => {
5308
            // Nothing to do.
5309
        }
5310
        case ast::NodeValue::Ident(_) => {
5311
            try bindValueIdent(self, binding, binding, bindTy, false, 0, 0);
5312
        }
5313
        else => {
5314
            // Nested pattern: recursively resolve (record destructuring,
5315
            // union variant, scope access, call, literals, etc).
5316
            try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy);
5317
        }
5318
    }
5319
}
5320
5321
/// Bind record pattern fields to variables in the current scope.
5322
unsafe fn bindRecordPatternFields(
5323
    self: &mut Resolver,
5324
    pattern: *ast::Node,
5325
    recInfo: RecordType,
5326
    matchBy: MatchBy
5327
) throws (ResolveError) {
5328
    match pattern.value {
5329
        case ast::NodeValue::Call(call) => {
5330
            // Unlabeled patterns: `S(x, y)`.
5331
            try checkRecordArity(self, call.args, recInfo, pattern);
5332
5333
            for binding, i in call.args {
5334
                let fieldType = recInfo.fields[i].fieldType;
5335
                try bindPatternVar(self, binding, fieldType, matchBy);
5336
            }
5337
        }
5338
        case ast::NodeValue::RecordLit(lit) => {
5339
            // Labeled patterns: `T { x, y }` or `T { x: binding }`.
5340
            if not lit.ignoreRest {
5341
                try checkRecordArity(self, lit.fields, recInfo, pattern);
5342
            }
5343
            for fieldNode in lit.fields {
5344
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5345
                    else panic "expected RecordLitField";
5346
5347
                // Brace patterns require labeled fields.
5348
                let label = field.label else panic "expected labeled field";
5349
                let fieldName = try nodeName(self, label);
5350
                let fieldIndex = findRecordField(&recInfo.fields[..], fieldName)
5351
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
5352
                let fieldType = recInfo.fields[fieldIndex].fieldType;
5353
                // Store field index for the lowerer.
5354
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5355
                try bindPatternVar(self, field.value, fieldType, matchBy);
5356
            }
5357
        }
5358
        else => throw emitError(self, pattern, ErrorKind::Internal)
5359
    }
5360
}
5361
5362
/// Validate and bind a record literal pattern for matching labeled union variants.
5363
unsafe fn resolveUnionRecordPattern(
5364
    self: &mut Resolver,
5365
    pattern: *ast::Node,
5366
    lit: ast::RecordLit,
5367
    subjectTy: Type,
5368
    unionType: UnionType,
5369
    matchBy: MatchBy
5370
) throws (ResolveError) {
5371
    let typeName = lit.typeName else {
5372
        throw emitError(self, pattern, ErrorKind::Internal);
5373
    };
5374
    // Verify the type matches the subject.
5375
    let patternTy = try visit(self, typeName, subjectTy);
5376
    if not isComparable(patternTy, subjectTy) {
5377
        throw emitTypeMismatch(self, pattern, TypeMismatch {
5378
            expected: subjectTy,
5379
            actual: patternTy,
5380
        });
5381
    }
5382
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[typeName.id].extra else {
5383
        throw emitError(self, typeName, ErrorKind::Internal);
5384
    };
5385
    let variant = &unionType.variants[index];
5386
5387
    // Copy variant index to the pattern node for the lowerer.
5388
    setVariantInfo(self, pattern, index, tag);
5389
5390
    if variant.valueType == Type::Void {
5391
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5392
    }
5393
    let recInfo = getRecord(variant.valueType)
5394
        else panic "resolveUnionRecordPattern: payload is not a record";
5395
5396
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
5397
}
5398
5399
/// Analyze a pattern appearing in a union case.
5400
unsafe fn resolveUnionPattern(
5401
    self: &mut Resolver,
5402
    pattern: *ast::Node,
5403
    subjectTy: Type,
5404
    unionType: UnionType,
5405
    matchBy: MatchBy
5406
) throws (ResolveError) {
5407
    match pattern.value {
5408
        case ast::NodeValue::ScopeAccess(access) =>
5409
            try resolveUnionScopePattern(self, pattern, access, subjectTy, unionType),
5410
        case ast::NodeValue::Call(call) =>
5411
            try resolveUnionCallPattern(self, pattern, call, subjectTy, unionType, matchBy),
5412
        case ast::NodeValue::RecordLit(lit) =>
5413
            try resolveUnionRecordPattern(self, pattern, lit, subjectTy, unionType, matchBy),
5414
        else => {
5415
            let patternTy = try visit(self, pattern, subjectTy);
5416
            throw emitTypeMismatch(self, pattern, TypeMismatch {
5417
                expected: subjectTy,
5418
                actual: patternTy,
5419
            });
5420
        }
5421
    }
5422
}
5423
5424
/// Return whether a case pattern introduces value bindings.
5425
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
5426
    match pattern.value {
5427
        case ast::NodeValue::Ident(_) => return nested,
5428
        case ast::NodeValue::Call(call) => {
5429
            for arg in call.args {
5430
                if casePatternIntroducesBindings(arg, true) {
5431
                    return true;
5432
                }
5433
            }
5434
        }
5435
        case ast::NodeValue::RecordLit(lit) => {
5436
            for fieldNode in lit.fields {
5437
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5438
                    else continue;
5439
                if casePatternIntroducesBindings(field.value, true) {
5440
                    return true;
5441
                }
5442
            }
5443
        }
5444
        case ast::NodeValue::ArrayLit(items) => {
5445
            for item in items {
5446
                if casePatternIntroducesBindings(item, true) {
5447
                    return true;
5448
                }
5449
            }
5450
        }
5451
        else => {}
5452
    }
5453
    return false;
5454
}
5455
5456
/// Analyze a `let-else` guard.
5457
unsafe fn resolveLetElse(self: &mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
5458
    throws (ResolveError)
5459
{
5460
    let pat = letElse.pattern;
5461
    let exprTy = try infer(self, pat.scrutinee);
5462
5463
    match pat.kind {
5464
        case ast::PatternKind::Binding => {
5465
            // Simple binding requires an optional expression.
5466
            let case Type::Optional(inner) = exprTy else {
5467
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
5468
            };
5469
            let payloadTy = *inner;
5470
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
5471
            // The `else` branch supplies the binding when the optional is nil.
5472
            try checkAssignable(self, letElse.elseBranch, payloadTy);
5473
5474
            return setNodeType(self, node, Type::Void);
5475
        }
5476
        case ast::PatternKind::Case => {
5477
            // Resolve the failure path before introducing success-only bindings.
5478
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
5479
            try resolveCasePattern(
5480
                self,
5481
                pat.pattern,
5482
                exprTy,
5483
                IdentMode::Compare,
5484
                MatchBy::Value,
5485
            );
5486
            if let guardExpr = pat.guard {
5487
                try checkBoolean(self, guardExpr);
5488
            }
5489
            if elseTy <> Type::Never and
5490
               casePatternIntroducesBindings(pat.pattern, false)
5491
            {
5492
                throw emitError(
5493
                    self,
5494
                    letElse.elseBranch,
5495
                    ErrorKind::LinearLetElseMustTerminate,
5496
                );
5497
            }
5498
        }
5499
    }
5500
    return setNodeType(self, node, Type::Void);
5501
}
5502
5503
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
5504
unsafe fn resolveBuiltinCall(
5505
    self: &mut Resolver,
5506
    node: *ast::Node,
5507
    kind: ast::Builtin,
5508
    args: *[*ast::Node]
5509
) -> Type throws (ResolveError) {
5510
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
5511
    if kind == ast::Builtin::SliceOf {
5512
        if args.len <> 2 and args.len <> 3 {
5513
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5514
                expected: 2,
5515
                actual: args.len as u32,
5516
            }));
5517
        }
5518
        let ptrType = try visit(self, args[0], Type::Unknown);
5519
        let case Type::Pointer { class, target, mutable } = ptrType else {
5520
            throw emitError(self, node, ErrorKind::ExpectedPointer);
5521
        };
5522
        let _ = try checkAssignable(self, args[1], Type::U32);
5523
        if args.len == 3 {
5524
            let _ = try checkAssignable(self, args[2], Type::U32);
5525
        }
5526
        try requireUnsafe(self, node);
5527
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
5528
    }
5529
    if args.len <> 1 {
5530
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
5531
            expected: 1,
5532
            actual: args.len as u32,
5533
        }));
5534
    }
5535
5536
    let ty = try resolveValueType(self, args[0]);
5537
    // Ensure the type body is resolved before computing layout.
5538
    // TODO: Somehow, ensuring the type is resolved should just happen all
5539
    // the time, lazily.
5540
    try ensureTypeResolved(self, ty, args[0]);
5541
    // TODO: This should be stored in `symbol` instead of having to recompute it.
5542
    // That way there's a canonical place to look for code gen.
5543
    let layout = getTypeLayout(ty);
5544
5545
    // Evaluate the built-in.
5546
    let mut value: u32 = undefined;
5547
    match kind {
5548
        case ast::Builtin::SizeOf => {
5549
            set value = layout.size;
5550
        },
5551
        case ast::Builtin::AlignOf => {
5552
            set value = layout.alignment;
5553
        },
5554
        case ast::Builtin::SliceOf => {
5555
            panic "unreachable: @sliceOf handled above";
5556
        }
5557
    }
5558
    // Record as constant value for constant folding.
5559
    setNodeConstValue(self, node, ConstValue::Int(ConstInt {
5560
        magnitude: value as u64,
5561
        bits: 32,
5562
        signed: false,
5563
        negative: false,
5564
    }));
5565
    return setNodeType(self, node, Type::U32);
5566
}
5567
5568
/// Validate call arguments against a function type: check argument count,
5569
/// type-check each argument, and verify that throwing functions use `try`.
5570
unsafe fn checkCallArgs(self: &mut Resolver, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
5571
    throws (ResolveError)
5572
{
5573
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
5574
        throw emitError(self, node, ErrorKind::MissingTry);
5575
    }
5576
    if call.args.len <> info.paramTypes.len as u32 {
5577
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5578
            expected: info.paramTypes.len as u32,
5579
            actual: call.args.len,
5580
        }));
5581
    }
5582
    for argNode, i in call.args {
5583
        let expectedTy = *info.paramTypes[i];
5584
5585
        try checkAssignable(self, argNode, expectedTy);
5586
    }
5587
}
5588
5589
/// Analyze a function call expression.
5590
unsafe fn resolveCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, ctx: CallCtx) -> Type
5591
    throws (ResolveError)
5592
{
5593
    // Intercept method calls on slices before inferring the callee.
5594
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5595
        let parentTy = try infer(self, access.parent);
5596
        if isUnsafePointerType(parentTy) {
5597
            try requireUnsafe(self, access.parent);
5598
        }
5599
        let subjectTy = autoDeref(parentTy);
5600
5601
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5602
            let methodName = try nodeName(self, access.child);
5603
            if methodName == "append" {
5604
                return try resolveSliceAppend(
5605
                    self, node, access.parent, parentTy, call.args, item, mutable
5606
                );
5607
            }
5608
            if methodName == "delete" {
5609
                return try resolveSliceDelete(
5610
                    self, node, access.parent, call.args, item, mutable
5611
                );
5612
            }
5613
        }
5614
    }
5615
    let calleeTy = try infer(self, call.callee);
5616
    if let case Type::Fn(info) = calleeTy {
5617
        try checkUnsafeCall(self, call.callee, info);
5618
    }
5619
5620
    // Check if callee is a union variant and dispatch to constructor handler.
5621
    // TODO: Move this out. We should decide on this earlier, based on the callee.
5622
    if let calleeSym = symbolFor(self, call.callee) {
5623
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
5624
            // TODO: Don't pass the callee type, pass the union type by getting it from
5625
            // the symbol.
5626
            let declSym = symbolFor(self, decl) else panic;
5627
            let case SymbolData::Type(ty) = declSym.data else panic;
5628
5629
            return try resolveUnionConstructorCall(self, node, call, ty);
5630
        }
5631
        // Check if callee is an unlabeled record type for constructor call syntax.
5632
        if let case SymbolData::Type(ty) = calleeSym.data {
5633
            // Ensure the record body is resolved before checking if labeled.
5634
            try ensureNominalResolved(self, ty, call.callee);
5635
            if let case NominalType::Record(recInfo) = *ty {
5636
                if not recInfo.labeled {
5637
                    return try resolveRecordConstructorCall(self, node, call, ty);
5638
                }
5639
            }
5640
        }
5641
    }
5642
5643
    // Check if we have a trait method call, ie. callee is a trait object.
5644
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5645
        let mut parentTy = Type::Unknown;
5646
        if let t = typeFor(self, access.parent) {
5647
            set parentTy = t;
5648
        }
5649
        let subjectTy = autoDeref(parentTy);
5650
5651
        if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy {
5652
            let methodName = try nodeName(self, access.child);
5653
            let method = findTraitMethod(traitInfo, methodName)
5654
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
5655
5656
            // Reject mutable-receiver methods called on immutable trait objects.
5657
            if method.mutable and not objMutable {
5658
                throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5659
            }
5660
            try checkCallArgs(self, node, call, method.fnType, ctx);
5661
            setTraitMethodCall(self, node, traitInfo, method.index);
5662
5663
            return setNodeType(self, node, *method.fnType.returnType);
5664
        }
5665
5666
        // Check for a standalone method call on a concrete type.
5667
        if let case Type::Nominal(_) = subjectTy {
5668
            let methodName = try nodeName(self, access.child);
5669
            if let method = findMethod(self, subjectTy, methodName) {
5670
                // Reject mutable-receiver methods on immutable bindings.
5671
                // If the parent is already a mutable pointer, the receiver is fine.
5672
                // Otherwise, check that the parent can yield a mutable borrow.
5673
                if method.mutable {
5674
                    if not try canMutateThrough(self, access.parent) {
5675
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5676
                    }
5677
                }
5678
                // Check arguments (excluding receiver).
5679
                try checkCallArgs(self, node, call, method.fnType, ctx);
5680
                set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
5681
5682
                return setNodeType(self, node, *method.fnType.returnType);
5683
            }
5684
        }
5685
    }
5686
    let case Type::Fn(info) = calleeTy else {
5687
        throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch {
5688
            expected: Type::Unknown,
5689
            actual: calleeTy,
5690
        }));
5691
    };
5692
    try checkCallArgs(self, node, call, info, ctx);
5693
    // Associate function type to callee.
5694
    setNodeType(self, call.callee, calleeTy);
5695
5696
    // Associate return type to call.
5697
    return setNodeType(self, node, *info.returnType);
5698
}
5699
5700
/// Check the allocator layout and the callback ABI used by slice append.
5701
unsafe fn isSliceAllocator(ty: Type) -> bool {
5702
    let case Type::Nominal(NominalType::Record(rec)) = ty else return false;
5703
    if rec.fields.len <> 2 or not rec.labeled {
5704
        return false;
5705
    }
5706
    let func = rec.fields[0];
5707
    let ctx = rec.fields[1];
5708
    let funcName = func.name else return false;
5709
    let ctxName = ctx.name else return false;
5710
    if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or
5711
       func.offset <> 0 or ctx.offset <> 8
5712
    {
5713
        return false;
5714
    }
5715
    let case Type::Fn(callback) = func.fieldType else return false;
5716
    let case Type::Pointer { target, .. } = ctx.fieldType else return false;
5717
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 {
5718
        return false;
5719
    }
5720
    if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or
5721
       *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32
5722
    {
5723
        return false;
5724
    }
5725
    let case Type::Pointer { class, target: result, mutable } = *callback.returnType
5726
        else return false;
5727
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
5728
}
5729
5730
/// Resolve `slice.append(val, allocator)`.
5731
unsafe fn resolveSliceAppend(
5732
    self: &mut Resolver,
5733
    node: *ast::Node,
5734
    parent: *ast::Node,
5735
    parentType: Type,
5736
    args: *[*ast::Node],
5737
    elemType: *Type,
5738
    mutable: bool
5739
) -> Type throws (ResolveError) {
5740
    if not mutable {
5741
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5742
    }
5743
    if args.len <> 2 {
5744
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5745
            expected: 2,
5746
            actual: args.len as u32,
5747
        }));
5748
    }
5749
    // First argument must be assignable to the element type.
5750
    try checkAssignable(self, args[0], *elemType);
5751
    // The allocator stores its callback and context at fixed offsets.
5752
    let allocatorTy = try infer(self, args[1]);
5753
    if let case Type::Nominal(info) = allocatorTy {
5754
        try ensureNominalResolved(self, info, args[1]);
5755
    }
5756
    if not isSliceAllocator(allocatorTy) {
5757
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
5758
    }
5759
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
5760
5761
    // Return the parent's type so the caller can rebind:
5762
    return setNodeType(self, node, parentType);
5763
}
5764
5765
/// Resolve `slice.delete(index)`.
5766
unsafe fn resolveSliceDelete(
5767
    self: &mut Resolver,
5768
    node: *ast::Node,
5769
    parent: *ast::Node,
5770
    args: *[*ast::Node],
5771
    elemType: *Type,
5772
    mutable: bool
5773
) -> Type throws (ResolveError) {
5774
    if not mutable {
5775
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
5776
    }
5777
    if args.len <> 1 {
5778
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
5779
            expected: 1,
5780
            actual: args.len as u32,
5781
        }));
5782
    }
5783
    try checkAssignable(self, args[0], Type::U32);
5784
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType };
5785
5786
    return setNodeType(self, node, Type::Void);
5787
}
5788
5789
/// Analyze an assignment expression.
5790
unsafe fn resolveAssign(self: &mut Resolver, node: *ast::Node, assign: ast::Assign) -> Type
5791
    throws (ResolveError)
5792
{
5793
    // Slice assignment: `slice[range] = value`.
5794
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
5795
        if let case ast::NodeValue::Range(range) = index.value {
5796
            try infer(self, index);
5797
            let containerTy = try infer(self, container);
5798
            if not try canMutateThrough(self, container) {
5799
                throw emitError(self, container, ErrorKind::ImmutableBinding);
5800
            }
5801
            let subjectTy = autoDeref(containerTy);
5802
            try checkSliceRangeIndices(self, range);
5803
5804
            let mut item: *Type = undefined;
5805
            let mut capacity: ?u32 = nil;
5806
5807
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
5808
                if not sliceMutable {
5809
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
5810
                }
5811
                set item = sliceItem;
5812
            } else {
5813
                match subjectTy {
5814
                    case Type::Array(a) => {
5815
                        try validateArraySliceBounds(self, range, a.length, node);
5816
                        set item = a.item;
5817
                        set capacity = a.length;
5818
                    }
5819
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5820
                }
5821
            }
5822
            // RHS is either a fill value or a source slice.
5823
            let rhsTy = try infer(self, assign.right);
5824
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
5825
                if *sourceItem <> *item {
5826
                    throw emitTypeMismatch(
5827
                        self,
5828
                        assign.right,
5829
                        TypeMismatch { expected: *item, actual: *sourceItem },
5830
                    );
5831
                }
5832
            } else {
5833
                try checkAssignable(self, assign.right, *item);
5834
            }
5835
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
5836
            setNodeType(self, assign.left, *item);
5837
5838
            return setNodeType(self, node, Type::Void);
5839
        }
5840
    }
5841
    let leftTy = try infer(self, assign.left);
5842
5843
    // Check if the left-hand side can be assigned to by checking if it's a mutable location.
5844
    if not try canBorrowMutFrom(self, assign.left) {
5845
        throw emitError(self, assign.left, ErrorKind::ImmutableBinding);
5846
    }
5847
    try checkAssignable(self, assign.right, leftTy);
5848
5849
    return setNodeType(self, node, leftTy);
5850
}
5851
5852
/// Ensure slice range bounds are valid `u32` values.
5853
unsafe fn checkSliceRangeIndices(self: &mut Resolver, range: ast::Range) throws (ResolveError) {
5854
    if let start = range.start {
5855
        try checkIndex(self, start);
5856
    }
5857
    if let end = range.end {
5858
        try checkIndex(self, end);
5859
    }
5860
}
5861
5862
/// Emit an error when a slice range with compile-tyime values exceeds the array length.
5863
unsafe fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
5864
    let mut startVal: ?u32 = nil;
5865
    let mut endVal: ?u32 = length;
5866
5867
    if let startNode = range.start {
5868
        if let val = constSliceIndex(self, startNode) {
5869
            set startVal = val;
5870
        }
5871
    }
5872
    if let endNode = range.end {
5873
        if let val = constSliceIndex(self, endNode) {
5874
            set endVal = val;
5875
        }
5876
    }
5877
    if let val = startVal; val > length {
5878
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5879
    }
5880
    if let val = endVal; val > length {
5881
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5882
    }
5883
    if let start = startVal {
5884
        if let end = endVal; start > end {
5885
            throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
5886
        }
5887
    }
5888
}
5889
5890
/// Check that an index expression has an unsigned integer type.
5891
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
5892
/// Smaller types are widened to `u32` via a numeric cast coercion.
5893
unsafe fn checkIndex(self: &mut Resolver, indexNode: *ast::Node) throws (ResolveError) {
5894
    let indexTy = try visit(self, indexNode, Type::U32);
5895
    if indexTy == Type::Int or indexTy == Type::U32 {
5896
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
5897
        return;
5898
    }
5899
    match indexTy {
5900
        case Type::U8, Type::U16 => {
5901
            setNodeCoercion(self, indexNode, Coercion::NumericCast {
5902
                from: indexTy, to: Type::U32,
5903
            });
5904
        }
5905
        else => {
5906
            throw emitTypeMismatch(self, indexNode, TypeMismatch {
5907
                expected: Type::U32,
5908
                actual: indexTy,
5909
            });
5910
        }
5911
    }
5912
}
5913
5914
/// Analyze an array or slice subscript expression.
5915
unsafe fn resolveSubscript(self: &mut Resolver, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
5916
    throws (ResolveError)
5917
{
5918
    // Range subscripts always require `&` to form a slice.
5919
    if let case ast::NodeValue::Range(range) = indexNode.value {
5920
        let _ = try infer(self, indexNode);
5921
        let _ = try infer(self, container);
5922
        try checkSliceRangeIndices(self, range);
5923
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
5924
    }
5925
    let containerTy = try infer(self, container);
5926
    if isUnsafePointerType(containerTy) {
5927
        try requireUnsafe(self, container);
5928
    }
5929
    try checkIndex(self, indexNode);
5930
    let subjectTy = autoDeref(containerTy);
5931
    if let case Type::Slice { item, .. } = subjectTy {
5932
        return setNodeType(self, node, *item);
5933
    }
5934
5935
    match subjectTy {
5936
        case Type::Array(arrayInfo) => {
5937
            return setNodeType(self, node, *arrayInfo.item);
5938
        }
5939
        else => {
5940
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
5941
        }
5942
    }
5943
}
5944
5945
/// Find a record field by name.
5946
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
5947
    for field, i in fields {
5948
        if let name = field.name {
5949
            if name == fieldName {
5950
                return i;
5951
            }
5952
        }
5953
    }
5954
    return nil;
5955
}
5956
5957
/// Analyze a union constructor call with payload.
5958
unsafe fn resolveUnionConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
5959
    throws (ResolveError)
5960
{
5961
    // Get the union nominal type.
5962
    let case NominalType::Union(unionType) = *unionNominal
5963
        else panic "resolveUnionConstructorCall: not a union type";
5964
5965
    // Callee was already visited; get the variant index it set.
5966
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
5967
        throw emitError(self, call.callee, ErrorKind::Internal);
5968
    };
5969
    let variant = &unionType.variants[index];
5970
5971
    // Associate variant index with `call` node for the lowerer.
5972
    setVariantInfo(self, node, index, tag);
5973
5974
    // Check if this variant expects a payload.
5975
    let payloadType = variant.valueType;
5976
    if payloadType <> Type::Void {
5977
        let recInfo = getRecord(payloadType)
5978
            else panic "resolveUnionVariantConstructor: payload is not a record";
5979
        try checkRecordConstructorArgs(self, node, call.args, recInfo);
5980
    } else {
5981
        if call.args.len > 0 {
5982
            throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
5983
        }
5984
    }
5985
    return setNodeType(self, node, Type::Nominal(unionNominal));
5986
}
5987
5988
/// Analyze an unlabeled record constructor call.
5989
///
5990
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
5991
/// number of arguments matches the record's field count and that each argument
5992
/// is assignable to its corresponding field type.
5993
unsafe fn resolveRecordConstructorCall(self: &mut Resolver, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
5994
    throws (ResolveError)
5995
{
5996
    let case NominalType::Record(recInfo) = *recordType
5997
        else panic "resolveRecordConstructorCall: not a record type";
5998
5999
    try checkRecordConstructorArgs(self, node, call.args, recInfo);
6000
    return setNodeType(self, node, Type::Nominal(recordType));
6001
}
6002
6003
/// Resolve the type name of a record literal, handling both record types and
6004
/// union variant payloads like `Union::Variant { ... }`.
6005
unsafe fn resolveRecordLitType(
6006
    self: &mut Resolver, node: *ast::Node, typeIdent: *ast::Node
6007
) -> ResolvedRecordLitType
6008
    throws (ResolveError)
6009
{
6010
    // Check if this is a scope access that might be a union variant.
6011
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
6012
        let scope = self.scope;
6013
        let sym = try resolveAccess(self, typeIdent, access, scope);
6014
6015
        // Check if resolved symbol is a union variant.
6016
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
6017
            // Get the union type from the variant's declaration.
6018
            let declSym = symbolFor(self, decl)
6019
                else throw emitError(self, node, ErrorKind::Internal);
6020
            let case SymbolData::Type(unionNominalType) = declSym.data
6021
                else throw emitError(self, node, ErrorKind::Internal);
6022
6023
            // Get the variant's payload type.
6024
            let case Type::Nominal(payloadInfo) = type
6025
                else throw emitError(self, node, ErrorKind::ExpectedRecord);
6026
6027
            // Store the variant index for the lowerer.
6028
            setVariantInfo(self, node, ordinal, index);
6029
6030
            return ResolvedRecordLitType {
6031
                recordType: payloadInfo,
6032
                resultType: Type::Nominal(unionNominalType),
6033
            };
6034
        }
6035
        // Not a variant, must be a type.
6036
        let case SymbolData::Type(ty) = sym.data
6037
            else throw emitError(self, node, ErrorKind::ExpectedRecord);
6038
        return ResolvedRecordLitType {
6039
            recordType: ty,
6040
            resultType: Type::Nominal(ty),
6041
        };
6042
    }
6043
    // Simple identifier, resolve as type name.
6044
    let tyInfo = try resolveTypeName(self, typeIdent);
6045
    return ResolvedRecordLitType {
6046
        recordType: tyInfo,
6047
        resultType: Type::Nominal(tyInfo),
6048
    };
6049
}
6050
6051
/// Analyze a record literal expression.
6052
unsafe fn resolveRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6053
    throws (ResolveError)
6054
{
6055
    // If no type name, infer an anonymous tuple type.
6056
    let typeIdent = lit.typeName else {
6057
        return try resolveAnonRecordLit(self, node, lit, hint);
6058
    };
6059
    // Resolve the type name, handling both record types and union variants.
6060
    let resolved = try resolveRecordLitType(self, node, typeIdent);
6061
    let tyInfo = resolved.recordType;
6062
    let resultType = resolved.resultType;
6063
6064
    // Lazily resolve record body if not yet done.
6065
    try ensureNominalResolved(self, tyInfo, typeIdent);
6066
    let case NominalType::Record(recordType) = *tyInfo
6067
        else throw emitError(self, node, ErrorKind::ExpectedRecord);
6068
6069
    // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax.
6070
    if not recordType.labeled {
6071
        throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch);
6072
    }
6073
    // Check field count. With `{ .. }` syntax, fewer fields are allowed.
6074
    if lit.fields.len > recordType.fields.len {
6075
        throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
6076
            expected: recordType.fields.len as u32,
6077
            actual: lit.fields.len,
6078
        }));
6079
    }
6080
    if not lit.ignoreRest and lit.fields.len < recordType.fields.len {
6081
        let missingName = recordType.fields[lit.fields.len].name else panic;
6082
        throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
6083
    }
6084
6085
    // Fields must be in declaration order.
6086
    for fieldNode, idx in lit.fields {
6087
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
6088
            else panic "resolveRecordLit: expected field node value";
6089
        let label = fieldArg.label
6090
            else panic "resolveRecordLit: expected labeled field";
6091
        let fieldName = try nodeName(self, label);
6092
        let expected = recordType.fields[idx];
6093
        let expectedName = expected.name else panic;
6094
6095
        if fieldName <> expectedName {
6096
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
6097
                field: fieldName,
6098
                prev: expectedName,
6099
            });
6100
        }
6101
        setRecordFieldIndex(self, fieldNode, idx);
6102
        try checkAssignable(self, fieldArg.value, expected.fieldType);
6103
        setNodeType(self, fieldNode, expected.fieldType);
6104
    }
6105
    return setNodeType(self, node, resultType);
6106
}
6107
6108
/// Analyze an anonymous record literal, checking fields against the hint type.
6109
unsafe fn resolveAnonRecordLit(self: &mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
6110
    throws (ResolveError)
6111
{
6112
    // Unwrap optional hint to get the inner record type.
6113
    let mut innerHint = hint;
6114
    if let case Type::Optional(inner) = hint {
6115
        set innerHint = *inner;
6116
    }
6117
    let mut hintInfo: ?RecordType = nil;
6118
    if let case Type::Nominal(info) = innerHint {
6119
        try ensureNominalResolved(self, info, node);
6120
        if let case NominalType::Record(s) = *info {
6121
            set hintInfo = s;
6122
        }
6123
    }
6124
    let targetInfo = hintInfo else {
6125
        throw emitError(self, node, ErrorKind::CannotInferType);
6126
    };
6127
6128
    // Check field count.
6129
    if lit.fields.len <> targetInfo.fields.len {
6130
        if lit.fields.len < targetInfo.fields.len {
6131
            let missingName = targetInfo.fields[lit.fields.len].name else panic;
6132
            throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
6133
        } else {
6134
            throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
6135
                expected: targetInfo.fields.len as u32,
6136
                actual: lit.fields.len,
6137
            }));
6138
        }
6139
    }
6140
6141
    // Fields must be in declaration order.
6142
    for fieldNode, idx in lit.fields {
6143
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
6144
            else panic "resolveAnonRecordLit: expected field node value";
6145
        let label = fieldArg.label
6146
            else panic "resolveAnonRecordLit: expected labeled field";
6147
        let fieldName = try nodeName(self, label);
6148
        let expected = targetInfo.fields[idx];
6149
        let expectedName = expected.name else panic;
6150
6151
        if fieldName <> expectedName {
6152
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
6153
                field: fieldName,
6154
                prev: expectedName,
6155
            });
6156
        }
6157
        setRecordFieldIndex(self, fieldNode, idx);
6158
        let fieldType = try visit(self, fieldArg.value, expected.fieldType);
6159
6160
        try expectAssignable(self, expected.fieldType, fieldType, fieldArg.value);
6161
        setNodeType(self, fieldNode, fieldType);
6162
    }
6163
    return setNodeType(self, node, innerHint);
6164
}
6165
6166
/// Analyze an array literal expression.
6167
unsafe fn resolveArrayLit(self: &mut Resolver, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
6168
    throws (ResolveError)
6169
{
6170
    let length = items.len;
6171
    let mut expectedTy: Type = Type::Unknown;
6172
6173
    if let case Type::Array(ary) = hint {
6174
        set expectedTy = *ary.item;
6175
    } else if let case Type::Optional(inner) = hint {
6176
        if let case Type::Array(ary) = *inner {
6177
            set expectedTy = *ary.item;
6178
        }
6179
    };
6180
    for itemNode in items {
6181
        let itemTy = try visit(self, itemNode, expectedTy);
6182
        assert itemTy <> Type::Unknown;
6183
6184
        // Set the expected type to the first type we encounter.
6185
        if expectedTy == Type::Unknown {
6186
            set expectedTy = itemTy;
6187
        } else {
6188
            try expectAssignable(self, expectedTy, itemTy, itemNode);
6189
        }
6190
    }
6191
    if expectedTy == Type::Unknown {
6192
        throw emitError(self, node, ErrorKind::CannotInferType);
6193
    };
6194
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
6195
    return setNodeType(self, node, arrayTy);
6196
}
6197
6198
/// Analyze an array repeat literal expression.
6199
unsafe fn resolveArrayRepeat(self: &mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
6200
    throws (ResolveError)
6201
{
6202
    let mut itemHint = hint;
6203
    if let case Type::Array(ary) = hint {
6204
        set itemHint = *ary.item;
6205
    } else if let case Type::Optional(inner) = hint {
6206
        if let case Type::Array(ary) = *inner {
6207
            set itemHint = *ary.item;
6208
        }
6209
    }
6210
    let valueTy = try visit(self, lit.item, itemHint);
6211
    let count = try checkSizeInt(self, lit.count);
6212
    let arrayTy = Type::Array(ArrayType {
6213
        item: allocType(self, valueTy),
6214
        length: count,
6215
    });
6216
    return setNodeType(self, node, arrayTy);
6217
}
6218
6219
/// Resolve union variant access.
6220
unsafe fn resolveUnionVariantAccess(
6221
    self: &mut Resolver,
6222
    node: *ast::Node,
6223
    access: ast::Access,
6224
    unionType: UnionType,
6225
    variantName: *[u8]
6226
) -> *unsafe mut Symbol throws (ResolveError) {
6227
    // Look up the variant in the union's nominal type.
6228
    for i in 0..unionType.variants.len {
6229
        let variant = &unionType.variants[i];
6230
        if variant.name == variantName {
6231
            let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data
6232
                else panic "resolveUnionVariantAccess: expected variant symbol";
6233
6234
            // Associate the variant symbol with the child node.
6235
            setNodeSymbol(self, access.child, variant.symbol);
6236
            setNodeSymbol(self, node, variant.symbol);
6237
6238
            // Store the variant index for the lowerer.
6239
            setVariantInfo(self, node, ordinal, index);
6240
6241
            return variant.symbol;
6242
        }
6243
    }
6244
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
6245
}
6246
6247
/// Analyze a scope access expression.
6248
unsafe fn resolveScopeAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6249
    throws (ResolveError)
6250
{
6251
    let scope = self.scope;
6252
    let sym = try resolveAccess(self, node, access, scope);
6253
    try checkStaticAccess(self, node, sym);
6254
    let mut ty: Type = undefined;
6255
6256
    match sym.data {
6257
        case SymbolData::Value { type, .. } => {
6258
            setNodeSymbol(self, node, sym);
6259
            set ty = type;
6260
        }
6261
        case SymbolData::Constant { type, value } => {
6262
            // Propagate the constant value.
6263
            if let val = value {
6264
                setNodeConstValue(self, node, val);
6265
            }
6266
            setNodeSymbol(self, node, sym);
6267
            set ty = type;
6268
        }
6269
        case SymbolData::Type(t) => {
6270
            setNodeSymbol(self, node, sym);
6271
            set ty = Type::Nominal(t);
6272
        }
6273
        case SymbolData::Variant { index, .. } => {
6274
            let ty = typeFor(self, node)
6275
                else throw emitError(self, node, ErrorKind::Internal);
6276
            // For unions without payload, store the variant index as a constant.
6277
            if isVoidUnion(ty) {
6278
                setNodeConstValue(self, node, ConstValue::Int(ConstInt {
6279
                    magnitude: index as u64,
6280
                    bits: 32,
6281
                    signed: false,
6282
                    negative: false,
6283
                }));
6284
            }
6285
            return setNodeType(self, node, ty);
6286
        }
6287
        case SymbolData::Module { .. } => {
6288
            throw emitError(self, node, ErrorKind::UnexpectedModuleName);
6289
        }
6290
        case SymbolData::Trait(_) => { // Trait names are not values.
6291
            throw emitError(self, node, ErrorKind::UnexpectedTraitName);
6292
        }
6293
    }
6294
    return setNodeType(self, node, ty);
6295
}
6296
6297
/// Analyze a field access expression.
6298
unsafe fn resolveFieldAccess(self: &mut Resolver, node: *ast::Node, access: ast::Access) -> Type
6299
    throws (ResolveError)
6300
{
6301
    let parentTy = try infer(self, access.parent);
6302
    if isUnsafePointerType(parentTy) {
6303
        try requireUnsafe(self, access.parent);
6304
    }
6305
    let subjectTy = autoDeref(parentTy);
6306
6307
    if let case Type::Slice { class, item, mutable } = subjectTy {
6308
        let fieldNode = access.child;
6309
        let fieldName = try nodeName(self, fieldNode);
6310
        if mem::eq(fieldName, PTR_FIELD) {
6311
            try requireUnsafe(self, node);
6312
            setRecordFieldIndex(self, fieldNode, 0);
6313
            return setNodeType(
6314
                self,
6315
                node,
6316
                Type::Pointer { class, target: item, mutable },
6317
            );
6318
        }
6319
        if mem::eq(fieldName, LEN_FIELD) {
6320
            setRecordFieldIndex(self, fieldNode, 1);
6321
            return setNodeType(self, node, Type::U32);
6322
        }
6323
        if mem::eq(fieldName, CAP_FIELD) {
6324
            setRecordFieldIndex(self, fieldNode, 2);
6325
            return setNodeType(self, node, Type::U32);
6326
        }
6327
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
6328
    }
6329
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
6330
        let fieldName = try nodeName(self, access.child);
6331
        let method = findTraitMethod(traitInfo, fieldName)
6332
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6333
        return setNodeType(self, node, Type::Fn(method.fnType));
6334
    }
6335
6336
    match subjectTy {
6337
        case Type::Nominal(NominalType::Record(recordType)) => {
6338
            let fieldNode = access.child;
6339
            let fieldName = try nodeName(self, fieldNode);
6340
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
6341
                let fieldTy = recordType.fields[fieldIndex].fieldType;
6342
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6343
                return setNodeType(self, node, fieldTy);
6344
            }
6345
            // Not a field: check for a standalone method.
6346
            if let method = findMethod(self, subjectTy, fieldName) {
6347
                return setNodeType(self, node, Type::Fn(method.fnType));
6348
            }
6349
            throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6350
        }
6351
        case Type::Array(arrayInfo) => {
6352
            let fieldNode = access.child;
6353
            let fieldName = try nodeName(self, fieldNode);
6354
6355
            if mem::eq(fieldName, LEN_FIELD) {
6356
                let lengthConst = constInt(arrayInfo.length as u64, 32, false, false);
6357
                setNodeConstValue(self, node, lengthConst);
6358
6359
                return setNodeType(self, node, Type::U32);
6360
            }
6361
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
6362
        }
6363
6364
        else => {
6365
            // Check for standalone methods on any nominal type (e.g. unions).
6366
            if let case Type::Nominal(_) = subjectTy {
6367
                let fieldName = try nodeName(self, access.child);
6368
                if let method = findMethod(self, subjectTy, fieldName) {
6369
                    return setNodeType(self, node, Type::Fn(method.fnType));
6370
                }
6371
            }
6372
            throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
6373
        }
6374
    }
6375
}
6376
6377
/// Check target mutability for implicit pointer access.
6378
unsafe fn canMutateThrough(self: &mut Resolver, node: *ast::Node) -> bool
6379
    throws (ResolveError)
6380
{
6381
    let ty = try infer(self, node);
6382
    match ty {
6383
        case Type::Pointer { mutable, .. } => return mutable,
6384
        case Type::Slice { mutable, .. } => return mutable,
6385
        else => return try canBorrowMutFrom(self, node),
6386
    }
6387
}
6388
6389
/// Determine whether an expression can yield a mutable location for borrowing.
6390
unsafe fn canBorrowMutFrom(self: &mut Resolver, node: *ast::Node) -> bool
6391
    throws (ResolveError)
6392
{
6393
    match node.value {
6394
        case ast::NodeValue::Ident(name) => {
6395
            let sym = findValueSymbol(self.scope, name)
6396
                else return false;
6397
            let case SymbolData::Value { mutable, .. } = sym.data
6398
                else return false;
6399
            return mutable;
6400
        }
6401
        case ast::NodeValue::FieldAccess(access) => {
6402
            let parentTy = try infer(self, access.parent);
6403
            if let case Type::Slice { .. } = autoDeref(parentTy) {
6404
                try requireUnsafe(self, node);
6405
            }
6406
            return try canMutateThrough(self, access.parent);
6407
        }
6408
        case ast::NodeValue::ScopeAccess(_) => {
6409
            // Module-qualified access to a top-level symbol. A `static`
6410
            // binds as a mutable value; a `constant` does not.
6411
            let _ = try infer(self, node);
6412
            let sym = nodeData(self, node).sym
6413
                else return false;
6414
6415
            if let case SymbolData::Value { mutable, .. } = sym.data {
6416
                return mutable;
6417
            }
6418
            return false;
6419
        }
6420
        case ast::NodeValue::Subscript { container, .. } => {
6421
            let containerTy = try infer(self, container);
6422
            // Subscript auto-derefs pointers, so check the actual indexed type.
6423
            let subjectTy = autoDeref(containerTy);
6424
6425
            if let case Type::Slice { mutable, .. } = subjectTy {
6426
                return mutable;
6427
            }
6428
            if let case Type::Array(_) = subjectTy {
6429
                return try canMutateThrough(self, container);
6430
            }
6431
            return false;
6432
        }
6433
        case ast::NodeValue::ArrayLit(_),
6434
             ast::NodeValue::ArrayRepeatLit(_) =>
6435
        {
6436
            return true;
6437
        }
6438
        case ast::NodeValue::Call(_) => {
6439
            // A call returning `*mut T` (or `&mut [T]`) yields a
6440
            // mutable place. Non-pointer returns cannot be mutably borrowed.
6441
            let ty = try infer(self, node);
6442
            if let case Type::Pointer { mutable, .. } = ty {
6443
                return mutable;
6444
            }
6445
            if let case Type::Slice { mutable, .. } = ty {
6446
                return mutable;
6447
            }
6448
            return false;
6449
        }
6450
        case ast::NodeValue::Deref(inner) => {
6451
            let innerTy = try infer(self, inner);
6452
6453
            if let case Type::Pointer { mutable, .. } = innerTy {
6454
                return mutable;
6455
            }
6456
            if let case Type::Slice { mutable, .. } = innerTy {
6457
                return mutable;
6458
            }
6459
            // Record deref: mutability depends on the inner binding.
6460
            if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy {
6461
                if not recInfo.labeled and recInfo.fields.len == 1 {
6462
                    return try canBorrowMutFrom(self, inner);
6463
                }
6464
            }
6465
            return false;
6466
        }
6467
        else => {
6468
            return false;
6469
        }
6470
    }
6471
}
6472
6473
/// Return the storage class of an addressed location.
6474
unsafe fn addressStorageClass(self: &Resolver, node: *ast::Node) -> types::PointerClass {
6475
    match node.value {
6476
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
6477
            if let sym = symbolFor(self, node) {
6478
                match sym.node.value {
6479
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
6480
                        return types::PointerClass::Owned,
6481
                    else => {}
6482
                }
6483
            }
6484
        }
6485
        case ast::NodeValue::FieldAccess(access) => {
6486
            if let ty = typeFor(self, access.parent) {
6487
                if let case Type::Pointer { class, .. } = ty {
6488
                    return class;
6489
                }
6490
            }
6491
            return addressStorageClass(self, access.parent);
6492
        }
6493
        case ast::NodeValue::Subscript { container, .. } => {
6494
            if let ty = typeFor(self, container) {
6495
                if let case Type::Slice { class, .. } = autoDeref(ty) {
6496
                    return class;
6497
                }
6498
                if let case Type::Pointer { class, .. } = ty {
6499
                    return class;
6500
                }
6501
            }
6502
            return addressStorageClass(self, container);
6503
        }
6504
        case ast::NodeValue::Deref(target) => {
6505
            if let ty = typeFor(self, target) {
6506
                if let case Type::Pointer { class, .. } = ty {
6507
                    return class;
6508
                }
6509
            }
6510
            return addressStorageClass(self, target);
6511
        }
6512
        else => {}
6513
    }
6514
    return types::PointerClass::Ref;
6515
}
6516
6517
/// Select an address type without extending the target storage lifetime.
6518
unsafe fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass
6519
    throws (ResolveError)
6520
{
6521
    if isUnsafePointerType(hint) {
6522
        try requireUnsafe(self, target);
6523
        return types::PointerClass::Unsafe;
6524
    }
6525
    if isRefType(hint) {
6526
        return types::PointerClass::Ref;
6527
    }
6528
    match target.value {
6529
        case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => {
6530
            if isConstExpr(self, target) {
6531
                return types::PointerClass::Owned;
6532
            }
6533
        }
6534
        else => {}
6535
    }
6536
    return addressStorageClass(self, target);
6537
}
6538
6539
/// Analyze an address-of expression.
6540
unsafe fn resolveAddressOf(self: &mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
6541
    throws (ResolveError)
6542
{
6543
    if addr.mutable {
6544
        if not try canBorrowMutFrom(self, addr.target) {
6545
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6546
        }
6547
    }
6548
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
6549
        if let case ast::NodeValue::Range(range) = index.value {
6550
            let containerTy = try infer(self, container);
6551
            let subjectTy = autoDeref(containerTy);
6552
6553
            try checkSliceRangeIndices(self, range);
6554
6555
            let mut item: *Type = undefined;
6556
            let mut capacity: ?u32 = nil;
6557
6558
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
6559
                if addr.mutable and not sliceMutable {
6560
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6561
                }
6562
                set item = sliceItem;
6563
            } else {
6564
                match subjectTy {
6565
                    case Type::Array(arrayInfo) => {
6566
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
6567
                        set item = arrayInfo.item;
6568
                        set capacity = arrayInfo.length;
6569
                    }
6570
                    else => {
6571
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
6572
                    }
6573
                }
6574
            }
6575
            let class = try addressClass(self, addr.target, hint);
6576
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
6577
            let alloc = allocType(self, sliceTy);
6578
            setSliceRangeInfo(self, node, SliceRangeInfo {
6579
                itemType: item,
6580
                mutable: addr.mutable,
6581
                capacity,
6582
            });
6583
            setNodeType(self, addr.target, *alloc);
6584
            return setNodeType(self, node, *alloc);
6585
        }
6586
    }
6587
    // Derive a hint for the target type from the slice hint.
6588
    let mut targetHint: Type = Type::Unknown;
6589
    if let case Type::Slice { item, .. } = hint {
6590
        set targetHint = Type::Array(ArrayType { item, length: 0 });
6591
    }
6592
    let targetTy = try visit(self, addr.target, targetHint);
6593
    let class = try addressClass(self, addr.target, hint);
6594
6595
    // Mark local variable symbols as address-taken so the lowerer
6596
    // allocates a stack slot eagerly.
6597
    if let case ast::NodeValue::Ident(name) = addr.target.value {
6598
        if let sym = findValueSymbol(self.scope, name) {
6599
            match &mut sym.data {
6600
                case SymbolData::Value { addressTaken, .. } => {
6601
                    set *addressTaken = true;
6602
                }
6603
                else => {}
6604
            }
6605
        }
6606
    }
6607
6608
    if let case Type::Array(arrayInfo) = targetTy {
6609
        match addr.target.value {
6610
            case ast::NodeValue::ArrayLit(_),
6611
                 ast::NodeValue::ArrayRepeatLit(_) =>
6612
            {
6613
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.mutable };
6614
                return setNodeType(self, node, *allocType(self, sliceTy));
6615
            }
6616
            else => {}
6617
        }
6618
    }
6619
    let pointerTy = Type::Pointer {
6620
        class, target: allocType(self, targetTy), mutable: addr.mutable,
6621
    };
6622
    return setNodeType(self, node, pointerTy);
6623
}
6624
6625
/// Analyze a dereference expression.
6626
unsafe fn resolveDeref(self: &mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
6627
    throws (ResolveError)
6628
{
6629
    let operandTy = try visit(self, targetNode, hint);
6630
    if let case Type::Pointer { class, target, .. } = operandTy {
6631
        if class == types::PointerClass::Unsafe {
6632
            try requireUnsafe(self, targetNode);
6633
        }
6634
        // Disallow dereferencing opaque pointers.
6635
        if *target == Type::Opaque {
6636
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
6637
        }
6638
        return setNodeType(self, node, *target);
6639
    }
6640
    // Auto-deref for single-field unlabeled records.
6641
    if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy {
6642
        if not recInfo.labeled and recInfo.fields.len == 1 {
6643
            let fieldTy = recInfo.fields[0].fieldType;
6644
            setRecordFieldIndex(self, node, 0);
6645
            return setNodeType(self, node, fieldTy);
6646
        }
6647
    }
6648
    throw emitError(self, targetNode, ErrorKind::ExpectedPointer);
6649
}
6650
6651
/// Check if a type is a pointer to opaque.
6652
fn isOpaquePointer(ty: Type) -> bool {
6653
    if let case Type::Pointer { target, .. } = ty {
6654
        return *target == Type::Opaque;
6655
    }
6656
    return false;
6657
}
6658
6659
/// Check if a type is an opaque slice.
6660
fn isOpaqueSlice(ty: Type) -> bool {
6661
    if let case Type::Slice { item, .. } = ty {
6662
        return *item == Type::Opaque;
6663
    }
6664
    return false;
6665
}
6666
6667
/// Check if an `as` cast between two types is valid.
6668
unsafe fn isValidCast(source: Type, target: Type) -> bool {
6669
    // Allow identity casts.
6670
    if source == target {
6671
        return true;
6672
    }
6673
    // Allow numeric to numeric.
6674
    if isNumericType(source) and isNumericType(target) {
6675
        return true;
6676
    }
6677
    // Allow `void` union to numeric.
6678
    // TODO: Check that variant index fits in target type.
6679
    if isVoidUnion(source) and isNumericType(target) {
6680
        return true;
6681
    }
6682
    // Allow address to numeric.
6683
    if let case Type::Slice { .. } = source {
6684
        // Disallow slice to numeric; slices are fat pointers.
6685
    } else if isAddressType(source) and isNumericType(target) {
6686
        return true;
6687
    }
6688
    // Allow pointer casts if one side is `*opaque` or target types are castable.
6689
    if let case Type::Pointer {
6690
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6691
    } = source {
6692
        if let case Type::Pointer {
6693
            class: targetClass, target: targetTarget, mutable: targetMutable,
6694
        } = target {
6695
            if sourceClass <> targetClass {
6696
                return false;
6697
            }
6698
            if targetMutable and not sourceMutable {
6699
                return false;
6700
            }
6701
            if isOpaquePointer(source) or isOpaquePointer(target) {
6702
                return true;
6703
            }
6704
            return isValidCast(*sourceTarget, *targetTarget);
6705
        }
6706
    }
6707
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
6708
    // or element types are castable.
6709
    if let case Type::Slice {
6710
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6711
    } = source {
6712
        if let case Type::Slice {
6713
            class: targetClass, item: targetItem, mutable: targetMutable,
6714
        } = target {
6715
            if sourceClass <> targetClass {
6716
                return false;
6717
            }
6718
            if targetMutable and not sourceMutable {
6719
                return false;
6720
            }
6721
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
6722
                return true;
6723
            }
6724
            if *targetItem == Type::U8 {
6725
                return true;
6726
            }
6727
            return isValidCast(*sourceItem, *targetItem);
6728
        }
6729
    }
6730
    return false;
6731
}
6732
6733
/// Analyze an `as` cast expression.
6734
unsafe fn resolveAs(self: &mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6735
    throws (ResolveError)
6736
{
6737
    let targetTy = try infer(self, expr.type);
6738
    let sourceTy = try visit(self, expr.value, targetTy);
6739
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6740
        try requireUnsafe(self, node);
6741
    }
6742
6743
    assert sourceTy <> Type::Unknown;
6744
    assert targetTy <> Type::Unknown;
6745
6746
    let mut valid = isValidCast(sourceTy, targetTy);
6747
    if let case Type::Pointer {
6748
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6749
    } = sourceTy {
6750
        if let case Type::Pointer {
6751
            class: targetClass, target: targetTarget, mutable: targetMutable,
6752
        } = targetTy {
6753
            if sourceClass == types::PointerClass::Ref and
6754
               targetClass == types::PointerClass::Unsafe and
6755
               (not targetMutable or sourceMutable) and
6756
               isValidCast(*sourceTarget, *targetTarget)
6757
            {
6758
                set valid = true;
6759
            }
6760
        }
6761
    }
6762
    if let case Type::Slice {
6763
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6764
    } = sourceTy {
6765
        if let case Type::Slice {
6766
            class: targetClass, item: targetItem, mutable: targetMutable,
6767
        } = targetTy {
6768
            if sourceClass == types::PointerClass::Ref and
6769
               targetClass == types::PointerClass::Unsafe and
6770
               (not targetMutable or sourceMutable) and
6771
               isValidCast(*sourceItem, *targetItem)
6772
            {
6773
                set valid = true;
6774
            }
6775
        }
6776
    }
6777
    if valid {
6778
        if let case Type::Pointer { target: sourceTarget, .. } = sourceTy {
6779
            if let case Type::Pointer { target: targetTarget, .. } = targetTy {
6780
                if *targetTarget <> Type::Opaque and not typesEqual(*sourceTarget, *targetTarget) {
6781
                    try requireUnsafe(self, node);
6782
                }
6783
            }
6784
        }
6785
        if let case Type::Slice { item: sourceItem, .. } = sourceTy {
6786
            if let case Type::Slice { item: targetItem, .. } = targetTy {
6787
                if *targetItem <> Type::Opaque and not typesEqual(*sourceItem, *targetItem) {
6788
                    try requireUnsafe(self, node);
6789
                }
6790
            }
6791
        }
6792
        // Propagate the constant value after applying the cast's target-width
6793
        // truncation and signed interpretation.
6794
        if let value = constValueEntry(self, expr.value) {
6795
            if let case ConstValue::Int(i) = value {
6796
                setNodeConstValue(self, node, castConstInt(i, targetTy));
6797
            }
6798
        }
6799
        return setNodeType(self, node, targetTy);
6800
    }
6801
    throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
6802
        from: sourceTy,
6803
        to: targetTy,
6804
    }));
6805
}
6806
6807
/// Analyze a range expression.
6808
unsafe fn resolveRange(self: &mut Resolver, node: *ast::Node, range: ast::Range) -> Type
6809
    throws (ResolveError)
6810
{
6811
    let mut start: ?*Type = nil;
6812
    let mut end: ?*Type = nil;
6813
6814
    if let s = range.start {
6815
        let startTy = try checkNumeric(self, s);
6816
6817
        if let e = range.end {
6818
            let endTy = try checkNumeric(self, e);
6819
            let mut resolvedTy = startTy;
6820
6821
            // Infer unsuffixed integer literals from the opposite bound.
6822
            if startTy == Type::Int and endTy <> Type::Int {
6823
                let _ = try checkAssignable(self, s, endTy);
6824
                set resolvedTy = endTy;
6825
            } else if endTy == Type::Int and startTy <> Type::Int {
6826
                let _ = try checkAssignable(self, e, startTy);
6827
                set resolvedTy = startTy;
6828
            } else {
6829
                let _ = try checkAssignable(self, e, startTy);
6830
            }
6831
            set start = allocType(self, resolvedTy);
6832
            set end = allocType(self, resolvedTy);
6833
        } else {
6834
            set start = allocType(self, startTy);
6835
        }
6836
    } else if let e = range.end {
6837
        set end = allocType(self, try checkNumeric(self, e));
6838
    }
6839
    return setNodeType(self, node, Type::Range { start, end });
6840
}
6841
6842
/// Analyze a `try` expression and its handlers.
6843
/// The `expected` type is used to determine if the value is discarded (`Void`)
6844
/// or if the catch expression needs type checking.
6845
unsafe fn resolveTry(self: &mut Resolver, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
6846
    throws (ResolveError)
6847
{
6848
    let call = tryExpr.expr;
6849
    let case ast::NodeValue::Call(callExpr) = call.value
6850
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
6851
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try);
6852
6853
    // TODO: It's annoying that we need to re-fetch the function type after
6854
    // analyzing the call.
6855
    let calleeTy = typeFor(self, callExpr.callee)
6856
        else return setNodeType(self, node, resultTy);
6857
    let case Type::Fn(calleeInfo) = calleeTy
6858
        else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
6859
6860
    if calleeInfo.throwList.len == 0 {
6861
        throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
6862
    }
6863
    // If we're not catching the error, nor panicking on error, nor returning
6864
    // optional, then the current function must be able to propagate it.
6865
    let mut tryResultTy = resultTy;
6866
    if tryExpr.returnsOptional {
6867
        // `try?` converts errors to `nil` and wraps the result in an optional.
6868
        if let case Type::Optional(_) = resultTy {
6869
            // Already optional, no wrapping needed.
6870
        } else {
6871
            set tryResultTy = Type::Optional(allocType(self, resultTy));
6872
        }
6873
    } else if tryExpr.catches.len > 0 {
6874
        // `try ... catch` -- one or more catch clauses.
6875
        set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint);
6876
    } else if not tryExpr.shouldPanic {
6877
        let fnInfo = self.currentFn
6878
            else throw emitError(self, node, ErrorKind::TryRequiresThrows);
6879
        if fnInfo.throwList.len == 0 {
6880
            throw emitError(self, node, ErrorKind::TryRequiresThrows);
6881
        }
6882
        // Check that *all* thrown errors of the callee can be propagated by
6883
        // the caller.
6884
        for throwTy in calleeInfo.throwList {
6885
            let mut found = false;
6886
6887
            for callerThrowTy in fnInfo.throwList {
6888
                if callerThrowTy == throwTy {
6889
                    set found = true;
6890
                    break;
6891
                }
6892
            }
6893
            if not found {
6894
                throw emitError(self, node, ErrorKind::TryIncompatibleError);
6895
            }
6896
        }
6897
    }
6898
    return setNodeType(self, node, tryResultTy);
6899
}
6900
6901
/// Check that a `catch` body is assignable to the expected result type, but only
6902
/// in expression context (`hint` is neither `Unknown` nor `Void`).
6903
unsafe fn checkCatchBody(self: &mut Resolver, body: *ast::Node, resultTy: Type, hint: Type)
6904
    throws (ResolveError)
6905
{
6906
    if hint <> Type::Unknown and hint <> Type::Void {
6907
        try checkAssignable(self, body, resultTy);
6908
    }
6909
}
6910
6911
/// Resolve catch clauses for a `try ... catch` expression.
6912
///
6913
/// For a single untyped catch (with or without binding), resolves the catch
6914
/// body and returns the result type. Multi-error callees with inferred bindings
6915
/// are rejected; you must use typed catches.
6916
unsafe fn resolveTryCatches(
6917
    self: &mut Resolver,
6918
    node: *ast::Node,
6919
    catches: *[*ast::Node],
6920
    calleeInfo: *FnType,
6921
    resultTy: Type,
6922
    hint: Type
6923
) -> Type throws (ResolveError) {
6924
    let firstNode = catches[0];
6925
    let case ast::NodeValue::CatchClause(first) = firstNode.value else
6926
        throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode));
6927
6928
    // Typed catches: dispatch to dedicated handler.
6929
    if first.typeNode <> nil {
6930
        return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint);
6931
    }
6932
    // Single untyped catch clause.
6933
    if let binding = first.binding {
6934
        if calleeInfo.throwList.len > 1 {
6935
            throw emitError(self, binding, ErrorKind::TryCatchMultiError);
6936
        }
6937
        enterScope(self, node);
6938
6939
        let errTy = *calleeInfo.throwList[0];
6940
        try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
6941
    }
6942
    let bodyTy = try visit(self, first.body, resultTy);
6943
6944
    if let _ = first.binding {
6945
        exitScope(self);
6946
    }
6947
    try checkCatchBody(self, first.body, resultTy, hint);
6948
6949
    return bodyTy if resultTy == Type::Never else resultTy;
6950
}
6951
6952
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
6953
///
6954
/// Validates that each type annotation is in the callee's throw list, that
6955
/// there are no duplicate catch types, and that the clauses are exhaustive.
6956
unsafe fn resolveTypedCatches(
6957
    self: &mut Resolver,
6958
    node: *ast::Node,
6959
    catches: *[*ast::Node],
6960
    calleeInfo: *FnType,
6961
    resultTy: Type,
6962
    hint: Type
6963
) -> Type throws (ResolveError) {
6964
    // Track which of the callee's throw types have been covered.
6965
    let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS];
6966
    let mut hasCatchAll = false;
6967
    let mut catchTy = Type::Never;
6968
6969
    for clauseNode in catches {
6970
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value else
6971
            throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode));
6972
6973
        if let typeNode = clause.typeNode {
6974
            // Typed catch clause: validate against callee's throw list.
6975
            let errTy = try infer(self, typeNode);
6976
            let mut foundIdx: ?u32 = nil;
6977
6978
            for throwType, j in calleeInfo.throwList {
6979
                if errTy == *throwType {
6980
                    set foundIdx = j;
6981
                    break;
6982
                }
6983
            }
6984
            let idx = foundIdx else {
6985
                throw emitError(self, typeNode, ErrorKind::TryIncompatibleError);
6986
            };
6987
            if covered[idx] {
6988
                throw emitError(self, typeNode, ErrorKind::TryCatchDuplicateType);
6989
            }
6990
            set covered[idx] = true;
6991
6992
            // Bind the error variable if present.
6993
            if let binding = clause.binding {
6994
                enterScope(self, clauseNode);
6995
                try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
6996
            }
6997
        } else {
6998
            // Catch-all clause with no type annotation or binding.
6999
            set hasCatchAll = true;
7000
        }
7001
        // Resolve the catch body and check assignability.
7002
        let bodyTy = try visit(self, clause.body, resultTy);
7003
        if bodyTy <> Type::Never { set catchTy = Type::Void; }
7004
        // Only typed clauses can have bindings.
7005
        if let _ = clause.binding {
7006
            exitScope(self);
7007
        }
7008
        try checkCatchBody(self, clause.body, resultTy, hint);
7009
    }
7010
7011
    // Check exhaustiveness: all callee error types must be covered.
7012
    if not hasCatchAll {
7013
        for i in 0..calleeInfo.throwList.len {
7014
            if not covered[i] {
7015
                throw emitError(self, node, ErrorKind::TryCatchNonExhaustive);
7016
            }
7017
        }
7018
    }
7019
    return catchTy if resultTy == Type::Never else resultTy;
7020
}
7021
7022
/// Analyze a `throw` statement.
7023
unsafe fn resolveThrow(self: &mut Resolver, node: *ast::Node, expr: *ast::Node) -> Type
7024
    throws (ResolveError)
7025
{
7026
    let fnInfo = self.currentFn
7027
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
7028
    if fnInfo.throwList.len == 0 {
7029
        throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
7030
    }
7031
    let throwTy = try infer(self, expr);
7032
    for errTy in fnInfo.throwList {
7033
        if let coerce = isAssignable(self, *errTy, throwTy, expr) {
7034
            setNodeCoercion(self, expr, coerce);
7035
            return setNodeType(self, node, Type::Never);
7036
        }
7037
    }
7038
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
7039
}
7040
7041
/// Analyze a `return` statement.
7042
unsafe fn resolveReturn(self: &mut Resolver, node: *ast::Node, retVal: ?*ast::Node) -> Type
7043
    throws (ResolveError)
7044
{
7045
    let f = self.currentFn
7046
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
7047
    let expected = *f.returnType;
7048
7049
    if let val = retVal {
7050
        let _actualTy = try checkAssignable(self, val, expected);
7051
    } else if expected <> Type::Void {
7052
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void });
7053
    }
7054
    // In throwing functions, return values are wrapped in the success variant.
7055
    if f.throwList.len > 0 {
7056
        setNodeCoercion(self, node, Coercion::ResultWrap);
7057
    }
7058
    return setNodeType(self, node, Type::Never);
7059
}
7060
7061
/// Convert a [`ConstInt`] to its two's-complement bit pattern.
7062
fn constIntToBits(c: ConstInt) -> u64 {
7063
    return (0 - c.magnitude) if c.negative else c.magnitude;
7064
}
7065
7066
/// Convert a [`ConstInt`] to its signed two's-complement representation.
7067
fn constIntToSigned(c: ConstInt) -> i64 {
7068
    return constIntToBits(c) as i64;
7069
}
7070
7071
/// Build a [`ConstInt`] from a signed result, preserving bit width and signedness.
7072
fn constIntFromSigned(value: i64, bits: u8, signed: bool) -> ConstInt {
7073
    if value < 0 {
7074
        // Compute magnitude without signed overflow.
7075
        let uval = value as u64;
7076
        return ConstInt {
7077
            magnitude: 0 - uval,
7078
            bits,
7079
            signed,
7080
            negative: true,
7081
        };
7082
    }
7083
    return ConstInt {
7084
        magnitude: value as u64,
7085
        bits,
7086
        signed,
7087
        negative: false,
7088
    };
7089
}
7090
7091
/// Build a [`ConstInt`] from a two's-complement bit pattern.
7092
fn constIntFromBits(raw: u64, bits: u8, signed: bool) -> ConstInt {
7093
    let mask = parser::U64_MAX if bits == 64 else parser::U64_MAX >> (64 - bits) as u64;
7094
    let truncated = raw & mask;
7095
7096
    if signed {
7097
        let signBit = (mask >> 1) + 1;
7098
        if (truncated & signBit) <> 0 {
7099
            return ConstInt {
7100
                magnitude: (0 - truncated) & mask,
7101
                bits,
7102
                signed,
7103
                negative: true,
7104
            };
7105
        }
7106
    }
7107
    return ConstInt { magnitude: truncated, bits, signed, negative: false };
7108
}
7109
7110
/// Try to fold a binary operation on two integer constants.
7111
/// Returns the resulting constant value if successful.
7112
fn foldIntBinOp(op: ast::BinaryOp, left: ConstInt, right: ConstInt) -> ?ConstValue {
7113
    // Use the wider bit width and propagate signedness.
7114
    let mut bits = left.bits;
7115
    if right.bits > bits {
7116
        set bits = right.bits;
7117
    }
7118
    let signed = left.signed or right.signed;
7119
    let l = constIntToSigned(left);
7120
    let r = constIntToSigned(right);
7121
7122
    match op {
7123
        // Shift counts are masked to the left operand's width, matching
7124
        // the runtime word instructions.
7125
        case ast::BinaryOp::Shl => {
7126
            let raw = constIntToBits(left);
7127
            let shamt = constIntToBits(right) % left.bits as u64;
7128
            return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed));
7129
        },
7130
        case ast::BinaryOp::Shr => {
7131
            let shamt = constIntToBits(right) % left.bits as u64;
7132
            if left.signed {
7133
                let shifted = constIntToSigned(left) >> shamt as i64;
7134
                return ConstValue::Int(
7135
                    constIntFromBits(shifted as u64, left.bits, true)
7136
                );
7137
            }
7138
            return ConstValue::Int(
7139
                constIntFromBits(left.magnitude >> shamt, left.bits, false)
7140
            );
7141
        },
7142
        case ast::BinaryOp::Eq  => return ConstValue::Bool(l == r),
7143
        case ast::BinaryOp::Ne  => return ConstValue::Bool(l <> r),
7144
        case ast::BinaryOp::Lt =>
7145
            return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude),
7146
        case ast::BinaryOp::Gt =>
7147
            return ConstValue::Bool(l > r if signed else left.magnitude > right.magnitude),
7148
        case ast::BinaryOp::Lte =>
7149
            return ConstValue::Bool(l <= r if signed else left.magnitude <= right.magnitude),
7150
        case ast::BinaryOp::Gte =>
7151
            return ConstValue::Bool(l >= r if signed else left.magnitude >= right.magnitude),
7152
        case ast::BinaryOp::Add => return ConstValue::Int(constIntFromSigned(l + r, bits, signed)),
7153
        case ast::BinaryOp::Sub => return ConstValue::Int(constIntFromSigned(l - r, bits, signed)),
7154
        case ast::BinaryOp::Mul => return ConstValue::Int(constIntFromSigned(l * r, bits, signed)),
7155
        case ast::BinaryOp::Div => {
7156
            if signed {
7157
                if r == 0 {
7158
                    return nil;
7159
                }
7160
                return ConstValue::Int(constIntFromSigned(l / r, bits, true));
7161
            }
7162
            if right.magnitude == 0 {
7163
                return nil;
7164
            }
7165
            return constInt(left.magnitude / right.magnitude, bits, false, false);
7166
        },
7167
        case ast::BinaryOp::Mod => {
7168
            if signed {
7169
                if r == 0 {
7170
                    return nil;
7171
                }
7172
                return ConstValue::Int(constIntFromSigned(l % r, bits, true));
7173
            }
7174
            if right.magnitude == 0 {
7175
                return nil;
7176
            }
7177
            return constInt(left.magnitude % right.magnitude, bits, false, false);
7178
        },
7179
        case ast::BinaryOp::BitAnd => return ConstValue::Int(constIntFromSigned(l & r, bits, signed)),
7180
        case ast::BinaryOp::BitOr  => return ConstValue::Int(constIntFromSigned(l | r, bits, signed)),
7181
        case ast::BinaryOp::BitXor => return ConstValue::Int(constIntFromSigned(l ^ r, bits, signed)),
7182
        else => return nil,
7183
    }
7184
}
7185
7186
/// Try to constant-fold a binary operation on two resolved operands.
7187
/// Only folds when the result type is concrete.
7188
fn tryFoldBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
7189
    let leftVal = constValueEntry(self, binop.left)
7190
        else return;
7191
    let rightVal = constValueEntry(self, binop.right)
7192
        else return;
7193
7194
    // Fold integer binary ops.
7195
    if let case ConstValue::Int(leftInt) = leftVal {
7196
        if let case ConstValue::Int(rightInt) = rightVal {
7197
            if let result = foldIntBinOp(binop.op, leftInt, rightInt) {
7198
                setNodeConstValue(self, node, result);
7199
            }
7200
            return;
7201
        }
7202
    }
7203
7204
    // Fold boolean binary ops.
7205
    if let case ConstValue::Bool(l) = leftVal {
7206
        if let case ConstValue::Bool(r) = rightVal {
7207
            match binop.op {
7208
                case ast::BinaryOp::And => setNodeConstValue(self, node, ConstValue::Bool(l and r)),
7209
                case ast::BinaryOp::Or => setNodeConstValue(self, node, ConstValue::Bool(l or r)),
7210
                case ast::BinaryOp::Eq => setNodeConstValue(self, node, ConstValue::Bool(l == r)),
7211
                case ast::BinaryOp::Ne,
7212
                     ast::BinaryOp::Xor => setNodeConstValue(self, node, ConstValue::Bool(l <> r)),
7213
                else => {}
7214
            }
7215
        }
7216
    }
7217
}
7218
7219
/// Analyze a binary expression.
7220
unsafe fn resolveBinOp(self: &mut Resolver, node: *ast::Node, binop: ast::BinOp) -> Type
7221
    throws (ResolveError)
7222
{
7223
    let mut resultTy = Type::Unknown;
7224
7225
    match binop.op {
7226
        case ast::BinaryOp::And,
7227
             ast::BinaryOp::Or,
7228
             ast::BinaryOp::Xor =>
7229
        {
7230
            try checkBoolean(self, binop.left);
7231
            try checkBoolean(self, binop.right);
7232
7233
            set resultTy = Type::Bool;
7234
        },
7235
        case ast::BinaryOp::Eq,
7236
             ast::BinaryOp::Ne =>
7237
        {
7238
            let leftTy = try infer(self, binop.left);
7239
            let rightTy = try visit(self, binop.right, leftTy);
7240
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
7241
                try requireUnsafe(self, node);
7242
            }
7243
7244
            if not isComparable(leftTy, rightTy) {
7245
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
7246
                    expected: leftTy,
7247
                    actual: rightTy,
7248
                });
7249
            }
7250
            // When comparing `T == ?T`, record a coercion on the
7251
            // non-optional side so the lowerer lifts it before comparing.
7252
            // We use the already-optional type from the other side rather than
7253
            // constructing a new optional, so that e.g. `?u8 == 42` coerces
7254
            // `42` to `?u8` (not `?i32`). We also record OptionalLift directly
7255
            // rather than using expectAssignable, because comparisons should
7256
            // allow e.g. `?*mut T == *T` where mutability differs.
7257
            if let case Type::Optional(_) = leftTy {
7258
                if not isOptionalType(rightTy) {
7259
                    setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy));
7260
                }
7261
            } else if let case Type::Optional(_) = rightTy {
7262
                setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy));
7263
            }
7264
            set resultTy = Type::Bool;
7265
        },
7266
        else => {
7267
            // Check for pointer arithmetic before numeric check.
7268
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
7269
                let leftTy = try infer(self, binop.left);
7270
                let rightTy = try visit(self, binop.right, leftTy);
7271
7272
                // Allow arithmetic on owning pointers and unsafe pointers, but
7273
                // never on references.
7274
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
7275
                    if *leftTarget == Type::Opaque {
7276
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7277
                    }
7278
                    if leftClass <> types::PointerClass::Ref
7279
                        and isNumericType(rightTy)
7280
                    {
7281
                        try requireUnsafe(self, node);
7282
                        return setNodeType(self, node, leftTy);
7283
                    }
7284
                }
7285
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
7286
                    if *rightTarget == Type::Opaque {
7287
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
7288
                    }
7289
                    if binop.op == ast::BinaryOp::Add
7290
                        and rightClass <> types::PointerClass::Ref
7291
                        and isNumericType(leftTy)
7292
                    {
7293
                        try requireUnsafe(self, node);
7294
                        return setNodeType(self, node, rightTy);
7295
                    }
7296
                }
7297
            }
7298
            let leftTy = try checkNumeric(self, binop.left);
7299
            let rightTy = try checkNumeric(self, binop.right);
7300
7301
            let mut operandTy = leftTy;
7302
            if leftTy <> rightTy {
7303
                if leftTy == Type::Int {
7304
                    set operandTy = rightTy;
7305
                } else if rightTy <> Type::Int {
7306
                    throw emitTypeMismatch(self, binop.right, TypeMismatch {
7307
                        expected: leftTy,
7308
                        actual: rightTy,
7309
                    });
7310
                }
7311
            }
7312
7313
            // Ordering comparisons return `bool`, not the operand type.
7314
            match binop.op {
7315
                case ast::BinaryOp::Lt, ast::BinaryOp::Gt,
7316
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
7317
                    set resultTy = Type::Bool,
7318
                else =>
7319
                    set resultTy = operandTy,
7320
            }
7321
7322
        }
7323
    };
7324
    // Try constant folding after both operands are resolved.
7325
    tryFoldBinOp(self, node, binop, resultTy);
7326
7327
    return setNodeType(self, node, resultTy);
7328
}
7329
7330
/// Analyze a unary expression.
7331
unsafe fn resolveUnOp(self: &mut Resolver, node: *ast::Node, unop: ast::UnOp) -> Type
7332
    throws (ResolveError)
7333
{
7334
    let mut resultTy = Type::Unknown;
7335
7336
    match unop.op {
7337
        case ast::UnaryOp::Not => {
7338
            set resultTy = try checkBoolean(self, unop.value);
7339
            if let value = constValueEntry(self, unop.value) {
7340
                if let case ConstValue::Bool(val) = value {
7341
                    setNodeConstValue(self, node, ConstValue::Bool(not val));
7342
                }
7343
            }
7344
        },
7345
        case ast::UnaryOp::Neg => {
7346
            // TODO: Check that we're allowed to use `-` here? Should negation
7347
            // only be valid for signed integers?
7348
            set resultTy = try checkNumeric(self, unop.value);
7349
            if let value = constValueEntry(self, unop.value) {
7350
                // Get the constant expression for the value, flip the sign,
7351
                // and store that new expression on the unary op node.
7352
                if let case ConstValue::Int(intVal) = value {
7353
                    setNodeConstValue(
7354
                        self,
7355
                        node,
7356
                        constInt(intVal.magnitude, intVal.bits, true, not intVal.negative)
7357
                    );
7358
                }
7359
            }
7360
        },
7361
        case ast::UnaryOp::BitNot => {
7362
            set resultTy = try checkNumeric(self, unop.value);
7363
            if let value = constValueEntry(self, unop.value) {
7364
                if let case ConstValue::Int(intVal) = value {
7365
                    let signed = constIntToSigned(intVal);
7366
                    let inverted = constIntFromSigned(-(signed + 1), intVal.bits, intVal.signed);
7367
                    setNodeConstValue(self, node, ConstValue::Int(inverted));
7368
                }
7369
            }
7370
        },
7371
    };
7372
    return setNodeType(self, node, resultTy);
7373
}
7374
7375
/// Resolve a type signature node and set its type.
7376
unsafe fn inferTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7377
    throws (ResolveError)
7378
{
7379
    let resolved = try resolveTypeSig(self, node, sig);
7380
7381
    return setNodeType(self, node, resolved);
7382
}
7383
7384
/// Convert a type signature node into a type value.
7385
unsafe fn resolveTypeSig(self: &mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
7386
    throws (ResolveError)
7387
{
7388
    match sig {
7389
        case ast::TypeSig::Void => {
7390
            return Type::Void;
7391
        }
7392
        case ast::TypeSig::Never => {
7393
            return Type::Never;
7394
        }
7395
        case ast::TypeSig::Opaque => {
7396
            return Type::Opaque;
7397
        }
7398
        case ast::TypeSig::Bool => {
7399
            return Type::Bool;
7400
        }
7401
        case ast::TypeSig::Integer { width, sign } => {
7402
            let u = sign == ast::Signedness::Unsigned;
7403
            match width {
7404
                case 1 => return Type::U8 if u else Type::I8,
7405
                case 2 => return Type::U16 if u else Type::I16,
7406
                case 4 => return Type::U32 if u else Type::I32,
7407
                case 8 => return Type::U64 if u else Type::I64,
7408
                else => {
7409
                    panic "resolveTypeSig: invalid integer width";
7410
                }
7411
            }
7412
        }
7413
        case ast::TypeSig::Array { itemType, length } => {
7414
            let item = try infer(self, itemType);
7415
            let length = try checkSizeInt(self, length);
7416
7417
            return Type::Array(ArrayType { item: allocType(self, item), length });
7418
        }
7419
        case ast::TypeSig::Slice { class, itemType, mutable } => {
7420
            let item = try infer(self, itemType);
7421
            return Type::Slice {
7422
                class,
7423
                item: allocType(self, item),
7424
                mutable,
7425
            };
7426
        }
7427
        case ast::TypeSig::Pointer { class, valueType, mutable } => {
7428
            let target = try infer(self, valueType);
7429
            return Type::Pointer {
7430
                class,
7431
                target: allocType(self, target),
7432
                mutable,
7433
            };
7434
        }
7435
        case ast::TypeSig::Optional { valueType } => {
7436
            let payload = try infer(self, valueType);
7437
            return Type::Optional(allocType(self, payload));
7438
        }
7439
        case ast::TypeSig::Nominal(name) => {
7440
            let ty = try resolveTypeName(self, name);
7441
            return Type::Nominal(ty);
7442
        }
7443
        case ast::TypeSig::Record { fields, labeled } => {
7444
            let mut recordType = try resolveRecordFields(self, node, fields, labeled);
7445
            set recordType.declaredCopy = true;
7446
            for field in recordType.fields {
7447
                if not isCopy(field.fieldType) {
7448
                    set recordType.declaredCopy = false;
7449
                }
7450
            }
7451
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
7452
            return Type::Nominal(nominalTy);
7453
        }
7454
        case ast::TypeSig::Fn { sig: t, isUnsafe } => {
7455
            let a = alloc::arenaAllocator(&mut self.arena);
7456
            let mut paramTypes: *mut [*Type] = &mut [];
7457
            let mut throwList: *mut [*Type] = &mut [];
7458
7459
            if t.params.len > MAX_FN_PARAMS {
7460
                throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
7461
                    expected: MAX_FN_PARAMS,
7462
                    actual: t.params.len,
7463
                }));
7464
            }
7465
            if t.throwList.len > MAX_FN_THROWS {
7466
                throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
7467
                    expected: MAX_FN_THROWS,
7468
                    actual: t.throwList.len,
7469
                }));
7470
            }
7471
7472
            for paramNode in t.params {
7473
                let paramTy = try resolveValueType(self, paramNode);
7474
                paramTypes.append(allocType(self, paramTy), a);
7475
            }
7476
            for tyNode in t.throwList {
7477
                let throwTy = try resolveValueType(self, tyNode);
7478
                try ensureStorableType(self, tyNode, throwTy);
7479
                throwList.append(allocType(self, throwTy), a);
7480
            }
7481
            let mut retType = allocType(self, Type::Void);
7482
            if let ret = t.returnType {
7483
                let resolvedRet = try resolveValueType(self, ret);
7484
                try ensureStorableType(self, ret, resolvedRet);
7485
                set retType = allocType(self, resolvedRet);
7486
            }
7487
            let fnType = FnType {
7488
                paramTypes: &paramTypes[..],
7489
                returnType: retType,
7490
                throwList: &throwList[..],
7491
                isUnsafe,
7492
            };
7493
            return Type::Fn(allocFnType(self, fnType));
7494
        }
7495
        // Resolve an opaque trait object signature.
7496
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
7497
            let sym = try resolveNamePath(self, traitName);
7498
            let case SymbolData::Trait(traitInfo) = sym.data
7499
                else throw emitError(self, traitName, ErrorKind::Internal);
7500
            setNodeSymbol(self, traitName, sym);
7501
7502
            return Type::TraitObject { class, traitInfo, mutable };
7503
        }
7504
    }
7505
}
7506
7507
/// Check if a type can be used for inferrence.
7508
fn isTypeInferrable(type: Type) -> bool {
7509
    if let case Type::Pointer { target, .. } = type {
7510
        return isTypeInferrable(*target);
7511
    }
7512
    match type {
7513
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
7514
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
7515
        case Type::Optional(opt) => return isTypeInferrable(*opt),
7516
        else => return true,
7517
    }
7518
}
7519
7520
/// Analyze a standalone expression by wrapping it in a synthetic function.
7521
export unsafe fn resolveExpr(
7522
    self: &mut Resolver, expr: *ast::Node, arena: &mut ast::NodeArena
7523
) -> Diagnostics throws (ResolveError) {
7524
    let a = alloc::arenaAllocator(&mut arena.arena);
7525
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
7526
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
7527
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
7528
7529
    let case ast::NodeValue::Block(block) = module.modBody.value
7530
        else panic "resolveExpr: expected block for module body";
7531
    enterScope(self, module.modBody);
7532
    try resolveModuleDecls(self, &block) catch {
7533
        return diagnostics(self);
7534
    };
7535
    try resolveModuleDefs(self, &block) catch {
7536
        return diagnostics(self);
7537
    };
7538
    exitScope(self);
7539
7540
    return diagnostics(self);
7541
}
7542
7543
/// Analyze a parsed module root, ie. a block of top-level statements.
7544
export unsafe fn resolveModuleRoot(self: &mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) {
7545
    let case ast::NodeValue::Block(block) = root.value
7546
        else panic "resolveModuleRoot: expected block for module root";
7547
7548
    enterScope(self, root);
7549
    try resolveModuleDecls(self, &block) catch {
7550
        return diagnostics(self);
7551
    };
7552
    try resolveModuleDefs(self, &block) catch {
7553
        return diagnostics(self);
7554
    };
7555
    exitScope(self);
7556
    setNodeType(self, root, Type::Void);
7557
7558
    return diagnostics(self);
7559
}
7560
7561
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
7562
/// and scopes for them, and also binds type names in each module so that cross-module
7563
/// type references work regardless of declaration order.
7564
unsafe fn resolveModuleGraph(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7565
    try bindTypeNames(self, block);
7566
7567
    for node in block.statements {
7568
        if let case ast::NodeValue::Mod(decl) = node.value {
7569
            try resolveModGraph(self, node, decl);
7570
        }
7571
    }
7572
}
7573
7574
/// Bind all type names in a module.
7575
/// Skips declarations that have already been bound.
7576
unsafe fn bindTypeNames(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7577
    for node in block.statements {
7578
        match node.value {
7579
            case ast::NodeValue::RecordDecl(decl) => {
7580
                if symbolFor(self, node) == nil {
7581
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7582
                }
7583
            }
7584
            case ast::NodeValue::UnionDecl(decl) => {
7585
                if symbolFor(self, node) == nil {
7586
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
7587
                }
7588
            }
7589
            case ast::NodeValue::TraitDecl { name, attrs, .. } => {
7590
                if symbolFor(self, node) == nil {
7591
                    try bindTraitName(self, node, name, attrs) catch {};
7592
                }
7593
            }
7594
            else => {}
7595
        }
7596
    }
7597
}
7598
7599
/// Resolve all type bodies in a module.
7600
unsafe fn resolveTypeBodies(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7601
    for node in block.statements {
7602
        match node.value {
7603
            case ast::NodeValue::RecordDecl(decl) => {
7604
                try resolveRecordBody(self, node, decl) catch {
7605
                    // Continue resolving other types even if one fails.
7606
                };
7607
            }
7608
            case ast::NodeValue::UnionDecl(decl) => {
7609
                try resolveUnionBody(self, node, decl) catch {
7610
                    // Continue resolving other types even if one fails.
7611
                };
7612
            }
7613
            case ast::NodeValue::TraitDecl { supertraits, methods, .. } => {
7614
                try resolveTraitBody(self, node, supertraits, methods) catch {
7615
                    // Continue resolving other types even if one fails.
7616
                };
7617
            }
7618
            else => {
7619
                // Ignore other declarations.
7620
            }
7621
        }
7622
    }
7623
}
7624
7625
/// Analyze module declarations. This pass processes all top-level statements. When it hits
7626
/// a `mod` statement, it recurses inside the module, analyzing its statements. Module import
7627
/// statements (`use`) are processed here, and make use of the module graph established in the
7628
/// previous pass.
7629
///
7630
/// This function uses a two-phase approach:
7631
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
7632
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
7633
unsafe fn resolveModuleDecls(res: &mut Resolver, block: &ast::Block) throws (ResolveError) {
7634
    // Phase 1: Bind all type names as placeholders.
7635
    try bindTypeNames(res, block);
7636
    // Phase 2: Process imports so names available from the module graph can
7637
    // be used in function signatures.
7638
    for node in block.statements {
7639
        if let case ast::NodeValue::Use(decl) = node.value {
7640
            try resolveUse(res, node, decl);
7641
        }
7642
    }
7643
    // Phase 3: Bind function signatures so that function references are
7644
    // available in constant and static initializers.
7645
    for node in block.statements {
7646
        if let case ast::NodeValue::FnDecl(decl) = node.value {
7647
            try resolveFnDecl(res, node, decl);
7648
        }
7649
    }
7650
    // Phase 4: Process constants before submodules, so that child modules
7651
    // can reference parent constants via `super::`.
7652
    for node in block.statements {
7653
        if let case ast::NodeValue::ConstDecl(_) = node.value {
7654
            try infer(res, node);
7655
        }
7656
    }
7657
    // Phase 5: Process submodule declarations -- recurses into child modules.
7658
    // Child modules may trigger on-demand type resolution via
7659
    // [`ensureNominalResolved`] which switches to the declaring module's
7660
    // scope.
7661
    for node in block.statements {
7662
        if let case ast::NodeValue::Mod(decl) = node.value {
7663
            try resolveModDecl(res, node, decl);
7664
        }
7665
    }
7666
    // Phase 5b: Process wildcard imports after submodules are resolved,
7667
    // so that transitive re-exports (export use foo::*) are visible.
7668
    for node in block.statements {
7669
        if let case ast::NodeValue::Use(decl) = node.value {
7670
            if decl.wildcard {
7671
                try resolveUse(res, node, decl);
7672
            }
7673
        }
7674
    }
7675
    // Phase 6: Resolve type bodies (record fields, union variants).
7676
    try resolveTypeBodies(res, block);
7677
    // Phase 7: Process all other declarations (statics, etc.).
7678
    for stmt in block.statements {
7679
        try visitDecl(res, stmt);
7680
    }
7681
}
7682
7683
/// Find a tracked binding by symbol identity.
7684
unsafe fn findLinearBinding(env: &LinearEnv, sym: *unsafe mut Symbol) -> ?u32 {
7685
    for i in 0..env.len {
7686
        if env.symbols[i] == sym {
7687
            return i;
7688
        }
7689
    }
7690
    return nil;
7691
}
7692
7693
/// Return whether a tracked binding is still available.
7694
fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool {
7695
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7696
}
7697
7698
/// Add a local binding when its resolved type moves by value.
7699
unsafe fn addLinearBinding(checker: &mut LinearChecker, env: &mut LinearEnv, node: *ast::Node)
7700
    throws (ResolveError)
7701
{
7702
    let sym = symbolFor(checker.resolver, node) else return;
7703
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7704
    if not isMoveOnly(ty) {
7705
        return;
7706
    }
7707
    if env.len >= MAX_LINEAR_BINDINGS {
7708
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7709
    }
7710
    set env.symbols[env.len] = sym;
7711
    set env.available |= (1 as u64) << (env.len as u64);
7712
    set env.len += 1;
7713
}
7714
7715
/// Mark a tracked binding as uninitialized.
7716
unsafe fn markLinearBindingUnavailable(self: &mut Resolver, env: &mut LinearEnv, node: *ast::Node) {
7717
    let sym = symbolFor(self, node) else return;
7718
    let index = findLinearBinding(env, sym) else return;
7719
    set env.available &= ~((1 as u64) << (index as u64));
7720
}
7721
7722
/// Require exact-use bindings introduced after `start` to be consumed.
7723
unsafe fn finishLinearScope(
7724
    checker: &mut LinearChecker,
7725
    env: &mut LinearEnv,
7726
    start: u32,
7727
) throws (ResolveError) {
7728
    if not env.terminated {
7729
        for i in start..env.len {
7730
            if linearBindingAvailable(env, i) {
7731
                let sym = env.symbols[i];
7732
                let case SymbolData::Value { type: ty, .. } = sym.data
7733
                    else panic "finishLinearScope: expected value symbol";
7734
                if isLinear(ty) {
7735
                    throw emitError(
7736
                        checker.resolver,
7737
                        sym.node,
7738
                        ErrorKind::LinearNotConsumed(sym.name),
7739
                    );
7740
                }
7741
            }
7742
        }
7743
    }
7744
    set env.len = start;
7745
}
7746
7747
/// Require a tracked identifier to remain available for any access.
7748
unsafe fn checkLinearIdent(
7749
    checker: &mut LinearChecker,
7750
    env: &mut LinearEnv,
7751
    node: *ast::Node,
7752
) throws (ResolveError) {
7753
    let sym = symbolFor(checker.resolver, node) else return;
7754
    let index = findLinearBinding(env, sym) else return;
7755
    if not linearBindingAvailable(env, index) {
7756
        let case SymbolData::Value { type: ty, .. } = sym.data
7757
            else panic "consumeLinearIdent: expected value symbol";
7758
        let kind = ErrorKind::LinearUseAfterConsume(sym.name) if isLinear(ty)
7759
            else ErrorKind::AffineUseAfterMove(sym.name);
7760
        throw emitError(checker.resolver, node, kind);
7761
    }
7762
}
7763
7764
/// Move or consume a tracked identifier once.
7765
unsafe fn consumeLinearIdent(
7766
    checker: &mut LinearChecker,
7767
    env: &mut LinearEnv,
7768
    node: *ast::Node,
7769
) throws (ResolveError) {
7770
    try checkLinearIdent(checker, env, node);
7771
    let sym = symbolFor(checker.resolver, node) else return;
7772
    let index = findLinearBinding(env, sym) else return;
7773
    set env.available &= ~((1 as u64) << (index as u64));
7774
}
7775
7776
/// Merge ownership availability across two live branches.
7777
/// Validate both inputs before writing to an output that can alias either input.
7778
unsafe fn joinLinearBranches(
7779
    checker: &mut LinearChecker,
7780
    env: &mut LinearEnv,
7781
    left: &LinearEnv,
7782
    right: &LinearEnv,
7783
    node: *ast::Node,
7784
) throws (ResolveError) {
7785
    if left.terminated and right.terminated {
7786
        set *env = *left;
7787
        set env.terminated = true;
7788
        return;
7789
    }
7790
    if left.terminated {
7791
        set *env = *right;
7792
        return;
7793
    }
7794
    if right.terminated {
7795
        set *env = *left;
7796
        return;
7797
    }
7798
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7799
    let mut available = left.available;
7800
    for i in 0..left.len {
7801
        if linearBindingAvailable(left, i) <> linearBindingAvailable(right, i) {
7802
            let sym = left.symbols[i];
7803
            let case SymbolData::Value { type: ty, .. } = sym.data
7804
                else panic "joinLinearBranches: expected value symbol";
7805
            if isLinear(ty) {
7806
                throw emitError(
7807
                    checker.resolver,
7808
                    node,
7809
                    ErrorKind::LinearBranchMismatch(sym.name),
7810
                );
7811
            }
7812
            set available &= ~((1 as u64) << (i as u64));
7813
        }
7814
    }
7815
    set *env = *left;
7816
    set env.available = available;
7817
}
7818
7819
/// Require all available exact-use bindings to be consumed at a function exit.
7820
unsafe fn finishLinearExit(
7821
    checker: &mut LinearChecker,
7822
    env: &mut LinearEnv,
7823
) throws (ResolveError) {
7824
    if env.terminated {
7825
        return;
7826
    }
7827
    for i in 0..env.len {
7828
        if linearBindingAvailable(env, i) {
7829
            let sym = env.symbols[i];
7830
            let case SymbolData::Value { type: ty, .. } = sym.data
7831
                else panic "finishLinearExit: expected value symbol";
7832
            if isLinear(ty) {
7833
                throw emitError(
7834
                    checker.resolver,
7835
                    sym.node,
7836
                    ErrorKind::LinearNotConsumed(sym.name),
7837
                );
7838
            }
7839
        }
7840
    }
7841
    set env.terminated = true;
7842
}
7843
7844
/// Find the local root borrowed or consumed by an argument expression.
7845
fn linearRootSymbol(self: &mut Resolver, node: *ast::Node) -> ?*unsafe mut Symbol {
7846
    match node.value {
7847
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) =>
7848
            return symbolFor(self, node),
7849
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
7850
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7851
        case ast::NodeValue::FieldAccess(access) =>
7852
            return linearRootSymbol(self, access.parent),
7853
        case ast::NodeValue::Subscript { container, .. } =>
7854
            return linearRootSymbol(self, container),
7855
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
7856
        else => return nil,
7857
    }
7858
}
7859
7860
/// Return the initializer that supplies a local reference's storage.
7861
unsafe fn localReferenceSource(sym: *unsafe mut Symbol) -> ?*ast::Node {
7862
    let case SymbolData::Value { type: ty, .. } = sym.data else return nil;
7863
    if isRefType(ty) {
7864
        if let case ast::NodeValue::Let(binding) = sym.node.value {
7865
            return binding.value;
7866
        }
7867
    }
7868
    return nil;
7869
}
7870
7871
/// Resolve a place through reference locals without extending its storage lifetime.
7872
unsafe fn borrowPlace(self: &mut Resolver, node: *ast::Node) -> BorrowPlace {
7873
    let mut place = BorrowPlace { root: nil, fields: undefined, len: 0, precise: true };
7874
    match node.value {
7875
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
7876
            let sym = symbolFor(self, node) else return place;
7877
            if let source = localReferenceSource(sym) {
7878
                return borrowPlace(self, source);
7879
            }
7880
            set place.root = sym;
7881
        }
7882
        case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target),
7883
        case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value),
7884
        case ast::NodeValue::FieldAccess(access) => {
7885
            set place = borrowPlace(self, access.parent);
7886
            if let ty = typeFor(self, access.parent) {
7887
                if let case Type::Pointer { .. } = ty; not isRefType(ty) and place.len > 0 {
7888
                    set place.len = 0;
7889
                    set place.precise = false;
7890
                }
7891
                if let case Type::Nominal(NominalType::Record(_)) = autoDeref(ty);
7892
                    place.precise and place.len < MAX_BORROW_FIELDS
7893
                {
7894
                    if let index = recordFieldIndexFor(self, access.child) {
7895
                        set place.fields[place.len] = index;
7896
                        set place.len += 1;
7897
                        return place;
7898
                    }
7899
                }
7900
            }
7901
            set place.precise = false;
7902
        }
7903
        case ast::NodeValue::Subscript { container, .. } => {
7904
            set place = borrowPlace(self, container);
7905
            if let ty = typeFor(self, container) {
7906
                if let case Type::Slice { class, .. } = autoDeref(ty); class <> types::PointerClass::Ref {
7907
                    set place.len = 0;
7908
                }
7909
            }
7910
            set place.precise = false;
7911
        }
7912
        case ast::NodeValue::Deref(target) => {
7913
            set place = borrowPlace(self, target);
7914
            if let ty = typeFor(self, target); not isRefType(ty) and place.len > 0 {
7915
                set place.len = 0;
7916
                set place.precise = false;
7917
            }
7918
        }
7919
        else => {}
7920
    }
7921
    return place;
7922
}
7923
7924
/// Two places overlap unless distinct inline fields prove separation.
7925
fn placesOverlap(left: &BorrowPlace, right: &BorrowPlace) -> bool {
7926
    if left.root == nil or left.root <> right.root {
7927
        return false;
7928
    }
7929
    let count = left.len if left.len < right.len else right.len;
7930
    for i in 0..count {
7931
        if left.fields[i] <> right.fields[i] {
7932
            return false;
7933
        }
7934
    }
7935
    return true;
7936
}
7937
7938
/// Check whether access uses a reference or one of its lexical reborrows.
7939
unsafe fn usesLocalLoan(self: &mut Resolver, node: *ast::Node, binding: *unsafe mut Symbol) -> bool {
7940
    let root = linearRootSymbol(self, node) else return false;
7941
    if root == binding {
7942
        return true;
7943
    }
7944
    let source = localReferenceSource(root) else return false;
7945
    return usesLocalLoan(self, source, binding);
7946
}
7947
7948
/// Reject accesses that conflict with a reference in an active lexical scope.
7949
unsafe fn checkLocalLoans(checker: &mut LinearChecker, node: *ast::Node, exclusive: bool)
7950
    throws (ResolveError)
7951
{
7952
    let place = borrowPlace(checker.resolver, node);
7953
    let root = place.root else return;
7954
    for i in 0..checker.localLen {
7955
        let loan = checker.locals[i];
7956
        let mut throughBinding = false;
7957
        if let binding = loan.binding {
7958
            set throughBinding = usesLocalLoan(checker.resolver, node, binding);
7959
        }
7960
        if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
7961
            and not throughBinding
7962
        {
7963
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
7964
        }
7965
    }
7966
}
7967
7968
/// Retain existing storage until its immutable reference binding leaves scope.
7969
unsafe fn addLocalLoan(checker: &mut LinearChecker, node: *ast::Node, binding: ast::Let)
7970
    throws (ResolveError)
7971
{
7972
    let ty = typeFor(checker.resolver, binding.ident) else return;
7973
    if not isRefType(ty) {
7974
        return;
7975
    }
7976
    let place = borrowPlace(checker.resolver, binding.value);
7977
    if place.root == nil {
7978
        throw emitError(checker.resolver, node, ErrorKind::RefBinding);
7979
    }
7980
    if checker.localLen >= MAX_LINEAR_BINDINGS {
7981
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7982
    }
7983
    let sym = symbolFor(checker.resolver, node) else panic "reference without binding";
7984
    let exclusive = isExclusiveArgument(ty);
7985
    try checkLocalLoans(checker, binding.value, exclusive);
7986
    set checker.locals[checker.localLen] = LocalLoan { binding: sym, place, exclusive };
7987
    set checker.localLen += 1;
7988
}
7989
7990
/// Protect a pattern source until its reference bindings leave scope.
7991
unsafe fn addPatternLoan(checker: &mut LinearChecker, subject: *ast::Node)
7992
    throws (ResolveError)
7993
{
7994
    let place = borrowPlace(checker.resolver, subject);
7995
    if place.root == nil {
7996
        return;
7997
    }
7998
    if checker.loanLen >= MAX_LINEAR_BINDINGS {
7999
        throw emitError(checker.resolver, subject, ErrorKind::Internal);
8000
    }
8001
    set checker.loans[checker.loanLen] = place;
8002
    set checker.loanLen += 1;
8003
}
8004
8005
/// Reject a write, mutable loan, or ownership transfer of a pattern source.
8006
unsafe fn checkPatternLoan(checker: &mut LinearChecker, node: *ast::Node)
8007
    throws (ResolveError)
8008
{
8009
    let place = borrowPlace(checker.resolver, node);
8010
    let root = place.root else return;
8011
    for i in 0..checker.loanLen {
8012
        if placesOverlap(&checker.loans[i], &place) {
8013
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
8014
        }
8015
    }
8016
}
8017
8018
/// Return whether a parameter can mutate or consume its argument's storage.
8019
unsafe fn isExclusiveArgument(ty: Type) -> bool {
8020
    match ty {
8021
        case Type::Pointer { mutable, .. } => return mutable,
8022
        case Type::Slice { mutable, .. } => return mutable,
8023
        case Type::TraitObject { mutable, .. } => return mutable,
8024
        else => return isMoveOnly(ty),
8025
    }
8026
}
8027
8028
/// Add the value identifiers introduced by a pattern.
8029
/// Return whether the pattern introduces references to its source storage.
8030
unsafe fn addLinearPatternBindings(
8031
    checker: &mut LinearChecker,
8032
    env: &mut LinearEnv,
8033
    pattern: *ast::Node,
8034
) -> bool throws (ResolveError) {
8035
    let mut hasReferences = false;
8036
    match pattern.value {
8037
        case ast::NodeValue::Ident(_) => {
8038
            try addLinearBinding(checker, env, pattern);
8039
            if let ty = typeFor(checker.resolver, pattern) {
8040
                return isRefType(ty);
8041
            }
8042
        }
8043
        case ast::NodeValue::Call(call) => {
8044
            for arg in call.args {
8045
                if try addLinearPatternBindings(checker, env, arg) {
8046
                    set hasReferences = true;
8047
                }
8048
            }
8049
        }
8050
        case ast::NodeValue::RecordLit(lit) => {
8051
            for fieldNode in lit.fields {
8052
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
8053
                    else panic "addLinearPatternBindings: expected field";
8054
                if try addLinearPatternBindings(checker, env, field.value) {
8055
                    set hasReferences = true;
8056
                }
8057
            }
8058
        }
8059
        case ast::NodeValue::ArrayLit(items) => {
8060
            for item in items {
8061
                if try addLinearPatternBindings(checker, env, item) {
8062
                    set hasReferences = true;
8063
                }
8064
            }
8065
        }
8066
        else => {}
8067
    }
8068
    return hasReferences;
8069
}
8070
8071
/// Check a lexical block and exact-use of locals introduced in it.
8072
unsafe fn checkLinearBlock(
8073
    checker: &mut LinearChecker,
8074
    env: &mut LinearEnv,
8075
    node: *ast::Node,
8076
) throws (ResolveError) {
8077
    let start = env.len;
8078
    let localStart = checker.localLen;
8079
    let case ast::NodeValue::Block(block) = node.value
8080
        else panic "checkLinearBlock: expected block";
8081
    for stmt in block.statements {
8082
        if env.terminated {
8083
            break;
8084
        }
8085
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
8086
    }
8087
    try finishLinearScope(checker, env, start);
8088
    set checker.localLen = localStart;
8089
}
8090
8091
/// Push a repeated-control-flow boundary.
8092
/// Initialize all loop state at this depth before increasing `loopDepth`.
8093
fn enterLinearLoop(checker: &mut LinearChecker, env: &LinearEnv) {
8094
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
8095
    let depth = checker.loopDepth;
8096
    set checker.loopMarks[depth] = env.len;
8097
    set checker.loopAvailable[depth] = env.available;
8098
    set checker.loopExitAvailable[depth] = env.available;
8099
    set checker.loopHasNaturalExit[depth] = false;
8100
    set checker.loopBreakSeen[depth] = false;
8101
    set checker.loopDepth += 1;
8102
}
8103
8104
/// Require a repeated body's outer bindings to match its entry state.
8105
unsafe fn checkLinearLoopBackEdge(
8106
    checker: &mut LinearChecker,
8107
    env: &LinearEnv,
8108
    node: *ast::Node,
8109
) throws (ResolveError) {
8110
    if env.terminated {
8111
        return;
8112
    }
8113
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
8114
    let depth = checker.loopDepth - 1;
8115
    let mark = checker.loopMarks[depth];
8116
    let entryAvailable = checker.loopAvailable[depth];
8117
    for i in 0..mark {
8118
        let bit = (1 as u64) << (i as u64);
8119
        if (env.available & bit) <> (entryAvailable & bit) {
8120
            let sym = env.symbols[i];
8121
            throw emitError(
8122
                checker.resolver,
8123
                node,
8124
                ErrorKind::LinearBranchMismatch(sym.name),
8125
            );
8126
        }
8127
    }
8128
}
8129
8130
/// Record the ownership state of a loop's condition-false exit.
8131
fn setLinearLoopNaturalExit(checker: &mut LinearChecker, env: &LinearEnv) {
8132
    assert checker.loopDepth > 0, "linear loop exit outside loop";
8133
    let depth = checker.loopDepth - 1;
8134
    set checker.loopExitAvailable[depth] = env.available;
8135
    set checker.loopHasNaturalExit[depth] = true;
8136
}
8137
8138
/// Require a break exit to agree with every other exit from this loop.
8139
unsafe fn checkLinearLoopBreak(
8140
    checker: &mut LinearChecker,
8141
    env: &LinearEnv,
8142
    node: *ast::Node,
8143
) throws (ResolveError) {
8144
    assert checker.loopDepth > 0, "linear loop break outside loop";
8145
    let depth = checker.loopDepth - 1;
8146
    let mark = checker.loopMarks[depth];
8147
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
8148
        let expected = checker.loopExitAvailable[depth];
8149
        for i in 0..mark {
8150
            let bit = (1 as u64) << (i as u64);
8151
            if (env.available & bit) <> (expected & bit) {
8152
                let sym = env.symbols[i];
8153
                throw emitError(
8154
                    checker.resolver,
8155
                    node,
8156
                    ErrorKind::LinearBranchMismatch(sym.name),
8157
                );
8158
            }
8159
        }
8160
    } else {
8161
        set checker.loopExitAvailable[depth] = env.available;
8162
    }
8163
    set checker.loopBreakSeen[depth] = true;
8164
}
8165
8166
/// Pop a repeated-control-flow boundary.
8167
fn exitLinearLoop(checker: &mut LinearChecker) {
8168
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
8169
    set checker.loopDepth -= 1;
8170
}
8171
8172
/// Check a conditional and merge its ownership states.
8173
unsafe fn checkLinearIf(
8174
    checker: &mut LinearChecker,
8175
    env: &mut LinearEnv,
8176
    node: *ast::Node,
8177
    conditional: ast::If,
8178
) throws (ResolveError) {
8179
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
8180
    let base = *env;
8181
    let mut thenEnv = base;
8182
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
8183
    let mut elseEnv = base;
8184
    if let branch = conditional.elseBranch {
8185
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
8186
    }
8187
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8188
}
8189
8190
/// Check an expression conditional and merge its ownership states.
8191
unsafe fn checkLinearCondExpr(
8192
    checker: &mut LinearChecker,
8193
    env: &mut LinearEnv,
8194
    node: *ast::Node,
8195
    conditional: ast::CondExpr,
8196
    usage: LinearUse,
8197
) throws (ResolveError) {
8198
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
8199
    let base = *env;
8200
    let mut thenEnv = base;
8201
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
8202
    let mut elseEnv = base;
8203
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
8204
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8205
}
8206
8207
/// Pointer patterns borrow their subject; value patterns consume it.
8208
unsafe fn patternSubjectUse(self: &Resolver, subject: *ast::Node) -> LinearUse {
8209
    if let ty = typeFor(self, subject) {
8210
        if let case Type::Pointer { .. } = ty {
8211
            return LinearUse::Borrow;
8212
        }
8213
    }
8214
    return LinearUse::Consume;
8215
}
8216
8217
/// Check a match expression, including ownership transferred into patterns.
8218
unsafe fn checkLinearMatch(
8219
    checker: &mut LinearChecker,
8220
    env: &mut LinearEnv,
8221
    node: *ast::Node,
8222
    matchExpr: ast::Match,
8223
) throws (ResolveError) {
8224
    try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject));
8225
    let base = *env;
8226
    let mut haveResult = false;
8227
    let mut result = base;
8228
    for prongNode in matchExpr.prongs {
8229
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
8230
            else panic "checkLinearMatch: expected prong";
8231
        let mut branch = base;
8232
        let bindingsStart = branch.len;
8233
        let loanStart = checker.loanLen;
8234
        match prong.arm {
8235
            case ast::ProngArm::Case(patterns) => {
8236
                for pattern in patterns {
8237
                    if try addLinearPatternBindings(checker, &mut branch, pattern) {
8238
                        try addPatternLoan(checker, matchExpr.subject);
8239
                    }
8240
                }
8241
            }
8242
            case ast::ProngArm::Binding(binding) => {
8243
                if try addLinearPatternBindings(checker, &mut branch, binding) {
8244
                    try addPatternLoan(checker, matchExpr.subject);
8245
                }
8246
            }
8247
            case ast::ProngArm::Else => {}
8248
        }
8249
        if prong.guard <> nil {
8250
            for i in bindingsStart..branch.len {
8251
                let sym = branch.symbols[i];
8252
                let case SymbolData::Value { type: ty, .. } = sym.data
8253
                    else panic "checkLinearMatch: expected value symbol";
8254
                if isLinear(ty) {
8255
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
8256
                }
8257
            }
8258
        }
8259
        if let guard = prong.guard {
8260
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
8261
        }
8262
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
8263
        try finishLinearScope(checker, &mut branch, bindingsStart);
8264
        set checker.loanLen = loanStart;
8265
        if haveResult {
8266
            let previous = result;
8267
        try joinLinearBranches(checker, &mut result, &previous, &branch, node);
8268
        } else {
8269
            set result = branch;
8270
            set haveResult = true;
8271
        }
8272
    }
8273
    if haveResult {
8274
        set *env = result;
8275
    }
8276
}
8277
8278
/// Check call-scoped loans and argument ownership transfers.
8279
unsafe fn checkLinearCall(
8280
    checker: &mut LinearChecker,
8281
    env: &mut LinearEnv,
8282
    node: *ast::Node,
8283
    call: ast::Call,
8284
) throws (ResolveError) {
8285
    let localStart = checker.localLen;
8286
    match checker.resolver.nodeData.entries[node.id].extra {
8287
        case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => {
8288
            let case ast::NodeValue::FieldAccess(access) = call.callee.value
8289
                else panic "slice mutation without receiver";
8290
            try checkPatternLoan(checker, access.parent);
8291
            try checkLocalLoans(checker, access.parent, true);
8292
        }
8293
        else => {}
8294
    }
8295
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
8296
    let mut fnInfo: ?*FnType = nil;
8297
    match checker.resolver.nodeData.entries[node.id].extra {
8298
        case NodeExtra::TraitMethodCall { traitInfo, methodIndex } =>
8299
            set fnInfo = traitInfo.methods[methodIndex].fnType,
8300
        case NodeExtra::MethodCall { method } => set fnInfo = method.fnType,
8301
        else => {
8302
            if let calleeTy = typeFor(checker.resolver, call.callee) {
8303
                if let case Type::Fn(info) = calleeTy {
8304
                    set fnInfo = info;
8305
                }
8306
            }
8307
        }
8308
    }
8309
    let info = fnInfo else {
8310
        for arg in call.args {
8311
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
8312
        }
8313
        return;
8314
    };
8315
    let mut arguments: [*ast::Node; MAX_FN_PARAMS + 1] = undefined;
8316
    let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined;
8317
    let mut argumentsLen: u32 = 0;
8318
8319
    // Method function types exclude their implicit receiver. Account for it
8320
    // explicitly so owning receivers are consumed and reference receivers
8321
    // participate in call-scoped loan conflict checks.
8322
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
8323
        let mut receiverClass = types::PointerClass::Unsafe;
8324
        let mut receiverMutable = false;
8325
        let mut haveReceiver = false;
8326
        match checker.resolver.nodeData.entries[node.id].extra {
8327
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
8328
                let method = &traitInfo.methods[methodIndex];
8329
                set receiverClass = method.receiverClass;
8330
                set receiverMutable = method.mutable;
8331
                set haveReceiver = true;
8332
            }
8333
            case NodeExtra::MethodCall { method } => {
8334
                set receiverClass = method.receiverClass;
8335
                set receiverMutable = method.mutable;
8336
                set haveReceiver = true;
8337
            }
8338
            else => {}
8339
        }
8340
        if haveReceiver {
8341
            try checkLocalLoans(checker, access.parent,
8342
                receiverMutable or receiverClass == types::PointerClass::Owned);
8343
            if receiverMutable or receiverClass == types::PointerClass::Owned {
8344
                try checkPatternLoan(checker, access.parent);
8345
            }
8346
            if receiverClass <> types::PointerClass::Unsafe {
8347
                set arguments[argumentsLen] = access.parent;
8348
                set exclusive[argumentsLen] =
8349
                    receiverClass == types::PointerClass::Owned or receiverMutable;
8350
                set argumentsLen += 1;
8351
            }
8352
            if receiverClass == types::PointerClass::Ref {
8353
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
8354
                if createsExplicitBorrow(access.parent) {
8355
                    try retainCallLoan(checker, access.parent, receiverMutable);
8356
                }
8357
            } else if receiverClass == types::PointerClass::Owned {
8358
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
8359
            }
8360
        }
8361
    }
8362
8363
    for arg, i in call.args {
8364
        let expected = *info.paramTypes[i];
8365
        let argExclusive = isExclusiveArgument(expected);
8366
        if argExclusive {
8367
            try checkPatternLoan(checker, arg);
8368
        }
8369
        if not isUnsafePointerType(expected) {
8370
            for j in 0..argumentsLen {
8371
                if exclusive[j] or argExclusive {
8372
                    if let name = callArgumentConflict(checker.resolver, arguments[j], arg) {
8373
                        throw emitError(checker.resolver, arg, ErrorKind::BorrowConflict(name));
8374
                    }
8375
                }
8376
            }
8377
            set arguments[argumentsLen] = arg;
8378
            set exclusive[argumentsLen] = argExclusive;
8379
            set argumentsLen += 1;
8380
        }
8381
        try checkLocalLoans(checker, arg, argExclusive);
8382
        if isRefType(expected) {
8383
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
8384
        } else {
8385
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
8386
        }
8387
        if isRefType(expected) and createsExplicitBorrow(arg) {
8388
            try retainCallLoan(checker, arg, argExclusive);
8389
        }
8390
    }
8391
    set checker.localLen = localStart;
8392
    if *info.returnType == Type::Never and info.throwList.len == 0 {
8393
        set env.terminated = true;
8394
    }
8395
}
8396
8397
/// Return the storage name when two call arguments can address the same place.
8398
unsafe fn callArgumentConflict(
8399
    self: &mut Resolver, left: *ast::Node, right: *ast::Node
8400
) -> ?*[u8] {
8401
    match left.value {
8402
        case ast::NodeValue::CondExpr(cond) => {
8403
            if let name = callArgumentConflict(self, cond.thenExpr, right) {
8404
                return name;
8405
            }
8406
            return callArgumentConflict(self, cond.elseExpr, right);
8407
        }
8408
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, cast.value, right),
8409
        else => {}
8410
    }
8411
    match right.value {
8412
        case ast::NodeValue::CondExpr(cond) => {
8413
            if let name = callArgumentConflict(self, left, cond.thenExpr) {
8414
                return name;
8415
            }
8416
            return callArgumentConflict(self, left, cond.elseExpr);
8417
        }
8418
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, left, cast.value),
8419
        else => {}
8420
    }
8421
    let leftPlace = borrowPlace(self, left);
8422
    let rightPlace = borrowPlace(self, right);
8423
    if placesOverlap(&leftPlace, &rightPlace) {
8424
        let root = rightPlace.root else panic "callArgumentConflict: overlap without root";
8425
        return root.name;
8426
    }
8427
    return nil;
8428
}
8429
8430
/// Return whether evaluating an argument creates an explicit address borrow.
8431
fn createsExplicitBorrow(node: *ast::Node) -> bool {
8432
    match node.value {
8433
        case ast::NodeValue::AddressOf(_) => return true,
8434
        case ast::NodeValue::As(cast) => return createsExplicitBorrow(cast.value),
8435
        case ast::NodeValue::CondExpr(cond) =>
8436
            return createsExplicitBorrow(cond.thenExpr) or createsExplicitBorrow(cond.elseExpr),
8437
        else => return false,
8438
    }
8439
}
8440
8441
/// Protect explicit address arguments until their call begins.
8442
unsafe fn retainCallLoan(
8443
    checker: &mut LinearChecker, node: *ast::Node, exclusive: bool
8444
) throws (ResolveError) {
8445
    match node.value {
8446
        case ast::NodeValue::CondExpr(cond) => {
8447
            try retainCallLoan(checker, cond.thenExpr, exclusive);
8448
            try retainCallLoan(checker, cond.elseExpr, exclusive);
8449
            return;
8450
        }
8451
        case ast::NodeValue::As(cast) => {
8452
            try retainCallLoan(checker, cast.value, exclusive);
8453
            return;
8454
        }
8455
        else => {}
8456
    }
8457
    let place = borrowPlace(checker.resolver, node);
8458
    if place.root == nil {
8459
        return;
8460
    }
8461
    if checker.localLen >= MAX_LINEAR_BINDINGS {
8462
        throw emitError(checker.resolver, node, ErrorKind::Internal);
8463
    }
8464
    set checker.locals[checker.localLen] = LocalLoan { binding: nil, place, exclusive };
8465
    set checker.localLen += 1;
8466
}
8467
8468
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
8469
unsafe fn checkLinearIfLet(
8470
    checker: &mut LinearChecker,
8471
    env: &mut LinearEnv,
8472
    node: *ast::Node,
8473
    conditional: ast::IfLet,
8474
) throws (ResolveError) {
8475
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
8476
        isLinear(subjectTy)
8477
    {
8478
        throw emitError(
8479
            checker.resolver,
8480
            conditional.pattern.scrutinee,
8481
            ErrorKind::LinearPartialMove,
8482
        );
8483
    }
8484
    try checkLinearNode(
8485
        checker,
8486
        env,
8487
        conditional.pattern.scrutinee,
8488
        patternSubjectUse(checker.resolver, conditional.pattern.scrutinee),
8489
    );
8490
    let base = *env;
8491
    let mut thenEnv = base;
8492
    let bindingsStart = thenEnv.len;
8493
    let loanStart = checker.loanLen;
8494
    if try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern) {
8495
        try addPatternLoan(checker, conditional.pattern.scrutinee);
8496
    }
8497
    if let guard = conditional.pattern.guard {
8498
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
8499
    }
8500
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
8501
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
8502
    set checker.loanLen = loanStart;
8503
    let mut elseEnv = base;
8504
    if let branch = conditional.elseBranch {
8505
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
8506
    }
8507
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
8508
}
8509
8510
/// Check one expression or statement under an ownership-use context.
8511
unsafe fn checkLinearNode(
8512
    checker: &mut LinearChecker,
8513
    env: &mut LinearEnv,
8514
    node: *ast::Node,
8515
    usage: LinearUse,
8516
) throws (ResolveError) {
8517
    if env.terminated {
8518
        return;
8519
    }
8520
    if usage <> LinearUse::Locate {
8521
        match node.value {
8522
            case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_),
8523
                 ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. },
8524
                 ast::NodeValue::Deref(_) => {
8525
                let mut exclusive = usage == LinearUse::Place;
8526
                if usage == LinearUse::Consume {
8527
                    if let ty = typeFor(checker.resolver, node) {
8528
                        set exclusive = isExclusiveArgument(ty);
8529
                    }
8530
                }
8531
                try checkLocalLoans(checker, node, exclusive);
8532
            }
8533
            else => {}
8534
        }
8535
    }
8536
    match node.value {
8537
        case ast::NodeValue::Ident(_) => {
8538
            if usage <> LinearUse::Place {
8539
                try checkLinearIdent(checker, env, node);
8540
            }
8541
            if usage == LinearUse::Consume {
8542
                if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) {
8543
                    try checkPatternLoan(checker, node);
8544
                }
8545
                try consumeLinearIdent(checker, env, node);
8546
            }
8547
        }
8548
        case ast::NodeValue::ExprStmt(expr) => {
8549
            if let exprTy = typeFor(checker.resolver, expr) {
8550
                if isLinear(exprTy) {
8551
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
8552
                }
8553
            }
8554
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8555
        }
8556
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
8557
        case ast::NodeValue::Let(binding) => {
8558
            let mut isUndefined = false;
8559
            if let case ast::NodeValue::Undef = binding.value.value {
8560
                set isUndefined = true;
8561
            }
8562
            if isUndefined {
8563
                if let bindingTy = typeFor(checker.resolver, binding.ident);
8564
                    isLinear(bindingTy)
8565
                {
8566
                    throw emitError(
8567
                        checker.resolver,
8568
                        binding.value,
8569
                        ErrorKind::LinearUndefined,
8570
                    );
8571
                }
8572
            }
8573
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
8574
            if env.terminated {
8575
                return;
8576
            }
8577
            try addLinearBinding(checker, env, node);
8578
            try addLocalLoan(checker, node, binding);
8579
            if isUndefined {
8580
                markLinearBindingUnavailable(checker.resolver, env, node);
8581
            }
8582
        }
8583
        case ast::NodeValue::Assign(assign) => {
8584
            try checkPatternLoan(checker, assign.left);
8585
            let mut target: ?u32 = nil;
8586
            let mut targetLinear = false;
8587
            if let leftTy = typeFor(checker.resolver, assign.left) {
8588
                if isMoveOnly(leftTy) {
8589
                    set targetLinear = isLinear(leftTy);
8590
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
8591
                        if let sym = symbolFor(checker.resolver, assign.left) {
8592
                            set target = findLinearBinding(env, sym);
8593
                        }
8594
                    }
8595
                    if targetLinear and target == nil {
8596
                        throw emitError(
8597
                            checker.resolver,
8598
                            assign.left,
8599
                            ErrorKind::LinearOverwrite,
8600
                        );
8601
                    }
8602
                }
8603
            }
8604
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
8605
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
8606
            if let index = target {
8607
                if targetLinear and linearBindingAvailable(env, index) {
8608
                    throw emitError(
8609
                        checker.resolver,
8610
                        assign.left,
8611
                        ErrorKind::LinearOverwrite,
8612
                    );
8613
                }
8614
                set env.available |= (1 as u64) << (index as u64);
8615
            }
8616
        }
8617
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
8618
        case ast::NodeValue::AddressOf(addr) => {
8619
            try checkLocalLoans(checker, addr.target, addr.mutable);
8620
            if addr.mutable {
8621
                try checkPatternLoan(checker, addr.target);
8622
            }
8623
            try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
8624
        }
8625
        case ast::NodeValue::Deref(target) => {
8626
            if let resultTy = typeFor(checker.resolver, node) {
8627
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8628
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8629
                }
8630
            }
8631
            try checkLinearNode(checker, env, target, LinearUse::Locate);
8632
        }
8633
        case ast::NodeValue::FieldAccess(access) => {
8634
            if let resultTy = typeFor(checker.resolver, node) {
8635
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8636
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8637
                }
8638
            }
8639
            try checkLinearNode(checker, env, access.parent, LinearUse::Locate);
8640
        }
8641
        case ast::NodeValue::ScopeAccess(_) => {}
8642
        case ast::NodeValue::Subscript { container, index } => {
8643
            if let resultTy = typeFor(checker.resolver, node) {
8644
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
8645
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
8646
                }
8647
            }
8648
            try checkLinearNode(checker, env, container, LinearUse::Locate);
8649
            try checkLinearNode(checker, env, index, LinearUse::Consume);
8650
        }
8651
        case ast::NodeValue::RecordLit(lit) => {
8652
            for fieldNode in lit.fields {
8653
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
8654
                    else panic "checkLinearNode: expected field";
8655
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
8656
            }
8657
        }
8658
        case ast::NodeValue::ArrayLit(items) => {
8659
            for item in items {
8660
                try checkLinearNode(checker, env, item, LinearUse::Consume);
8661
            }
8662
        }
8663
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
8664
            if let itemTy = typeFor(checker.resolver, repeat.item) {
8665
                if not isCopy(itemTy) {
8666
                    throw emitError(
8667
                        checker.resolver,
8668
                        repeat.item,
8669
                        ErrorKind::LinearDiscard,
8670
                    );
8671
                }
8672
            }
8673
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
8674
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
8675
        }
8676
        case ast::NodeValue::BinOp(op) => {
8677
            if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or {
8678
                try checkLinearNode(checker, env, op.left, LinearUse::Consume);
8679
                let skipped = *env;
8680
                let mut evaluated = skipped;
8681
                try checkLinearNode(checker, &mut evaluated, op.right, LinearUse::Consume);
8682
                try joinLinearBranches(checker, env, &skipped, &evaluated, node);
8683
                return;
8684
            }
8685
            let mut operandUse = LinearUse::Consume;
8686
            match op.op {
8687
                case ast::BinaryOp::Eq, ast::BinaryOp::Ne,
8688
                     ast::BinaryOp::Lt, ast::BinaryOp::Gt,
8689
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
8690
                    set operandUse = LinearUse::Observe,
8691
                else => {}
8692
            }
8693
            try checkLinearNode(checker, env, op.left, operandUse);
8694
            try checkLinearNode(checker, env, op.right, operandUse);
8695
        }
8696
        case ast::NodeValue::UnOp(op) => {
8697
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
8698
        }
8699
        case ast::NodeValue::As(expr) => {
8700
            let mut castUse = usage;
8701
            if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) {
8702
                set castUse = LinearUse::Observe;
8703
            }
8704
            try checkLinearNode(checker, env, expr.value, castUse);
8705
        }
8706
        case ast::NodeValue::Range(range) => {
8707
            if let start = range.start {
8708
                try checkLinearNode(checker, env, start, LinearUse::Consume);
8709
            }
8710
            if let end = range.end {
8711
                try checkLinearNode(checker, env, end, LinearUse::Consume);
8712
            }
8713
        }
8714
        case ast::NodeValue::BuiltinCall { args, .. } => {
8715
            for arg in args {
8716
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
8717
            }
8718
        }
8719
        case ast::NodeValue::If(conditional) => {
8720
            try checkLinearIf(checker, env, node, conditional);
8721
        }
8722
        case ast::NodeValue::CondExpr(conditional) => {
8723
            try checkLinearCondExpr(checker, env, node, conditional, usage);
8724
        }
8725
        case ast::NodeValue::IfLet(conditional) => {
8726
            try checkLinearIfLet(checker, env, node, conditional);
8727
        }
8728
        case ast::NodeValue::LetElse(binding) => {
8729
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
8730
                isLinear(subjectTy)
8731
            {
8732
                throw emitError(
8733
                    checker.resolver,
8734
                    binding.pattern.scrutinee,
8735
                    ErrorKind::LinearPartialMove,
8736
                );
8737
            }
8738
            try checkLinearNode(
8739
                checker,
8740
                env,
8741
                binding.pattern.scrutinee,
8742
                patternSubjectUse(checker.resolver, binding.pattern.scrutinee),
8743
            );
8744
            let base = *env;
8745
            let mut guardedEnv = base;
8746
            if let guard = binding.pattern.guard {
8747
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
8748
            }
8749
            let mut successEnv = guardedEnv;
8750
            try addLinearPatternBindings(
8751
                checker,
8752
                &mut successEnv,
8753
                binding.pattern.pattern,
8754
            );
8755
            let mut fallbackEnv = base;
8756
            try checkLinearNode(
8757
                checker,
8758
                &mut fallbackEnv,
8759
                binding.elseBranch,
8760
                LinearUse::Consume,
8761
            );
8762
            if binding.pattern.guard <> nil {
8763
                let mut guardFallbackEnv = guardedEnv;
8764
                try checkLinearNode(
8765
                    checker,
8766
                    &mut guardFallbackEnv,
8767
                    binding.elseBranch,
8768
                    LinearUse::Consume,
8769
                );
8770
                let previous = fallbackEnv;
8771
                try joinLinearBranches(
8772
                    checker,
8773
                    &mut fallbackEnv,
8774
                    &previous,
8775
                    &guardFallbackEnv,
8776
                    binding.elseBranch,
8777
                );
8778
            }
8779
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8780
                try addLinearPatternBindings(
8781
                    checker,
8782
                    &mut fallbackEnv,
8783
                    binding.pattern.pattern,
8784
                );
8785
            }
8786
            try joinLinearBranches(checker, env, &successEnv, &fallbackEnv, node);
8787
        }
8788
        case ast::NodeValue::Match(matchExpr) => {
8789
            try checkLinearMatch(checker, env, node, matchExpr);
8790
        }
8791
        case ast::NodeValue::Try(tryExpr) => {
8792
            try checkLinearNode(checker, env, tryExpr.expr, usage);
8793
            let success = *env;
8794
            if let resultTy = typeFor(checker.resolver, tryExpr.expr); resultTy == Type::Never {
8795
                if not tryExpr.returnsOptional and (tryExpr.catches.len > 0 or tryExpr.shouldPanic) {
8796
                    set env.terminated = true;
8797
                }
8798
            }
8799
            for catchNode in tryExpr.catches {
8800
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
8801
                    else panic "checkLinearNode: expected catch";
8802
                let mut branch = success;
8803
                let start = branch.len;
8804
                if let binding = catchClause.binding {
8805
                    try addLinearBinding(checker, &mut branch, binding);
8806
                }
8807
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8808
                try finishLinearScope(checker, &mut branch, start);
8809
                let previous = *env;
8810
                try joinLinearBranches(checker, env, &previous, &branch, node);
8811
            }
8812
        }
8813
        case ast::NodeValue::While(whileStmt) => {
8814
            enterLinearLoop(checker, env);
8815
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8816
            let conditionExit = *env;
8817
            setLinearLoopNaturalExit(checker, &conditionExit);
8818
            let mut bodyEnv = conditionExit;
8819
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8820
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8821
            exitLinearLoop(checker);
8822
            set *env = conditionExit;
8823
            if let elseBranch = whileStmt.elseBranch {
8824
                let mut elseEnv = conditionExit;
8825
                try checkLinearNode(
8826
                    checker,
8827
                    &mut elseEnv,
8828
                    elseBranch,
8829
                    LinearUse::Discard,
8830
                );
8831
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8832
            }
8833
        }
8834
        case ast::NodeValue::WhileLet(whileStmt) => {
8835
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8836
                isLinear(subjectTy)
8837
            {
8838
                throw emitError(
8839
                    checker.resolver,
8840
                    whileStmt.pattern.scrutinee,
8841
                    ErrorKind::LinearPartialMove,
8842
                );
8843
            }
8844
            let base = *env;
8845
            enterLinearLoop(checker, env);
8846
            let mut bodyEnv = base;
8847
            try checkLinearNode(
8848
                checker,
8849
                &mut bodyEnv,
8850
                whileStmt.pattern.scrutinee,
8851
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
8852
            );
8853
            let mut conditionExit = bodyEnv;
8854
            let start = bodyEnv.len;
8855
            let loanStart = checker.loanLen;
8856
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
8857
                try addPatternLoan(checker, whileStmt.pattern.scrutinee);
8858
            }
8859
            if let guard = whileStmt.pattern.guard {
8860
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8861
                let mut guardExit = bodyEnv;
8862
                try finishLinearScope(checker, &mut guardExit, start);
8863
                let previous = conditionExit;
8864
                try joinLinearBranches(
8865
                    checker,
8866
                    &mut conditionExit,
8867
                    &previous,
8868
                    &guardExit,
8869
                    guard,
8870
                );
8871
            }
8872
            setLinearLoopNaturalExit(checker, &conditionExit);
8873
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8874
            try finishLinearScope(checker, &mut bodyEnv, start);
8875
            set checker.loanLen = loanStart;
8876
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8877
            exitLinearLoop(checker);
8878
            set *env = conditionExit;
8879
            if let elseBranch = whileStmt.elseBranch {
8880
                let mut elseEnv = conditionExit;
8881
                try checkLinearNode(
8882
                    checker,
8883
                    &mut elseEnv,
8884
                    elseBranch,
8885
                    LinearUse::Discard,
8886
                );
8887
                try joinLinearBranches(checker, env, &conditionExit, &elseEnv, node);
8888
            }
8889
        }
8890
        case ast::NodeValue::For(forStmt) => {
8891
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8892
                if isLinear(iterableTy) {
8893
                    throw emitError(
8894
                        checker.resolver,
8895
                        forStmt.iterable,
8896
                        ErrorKind::LinearPartialMove,
8897
                    );
8898
                }
8899
            }
8900
            try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
8901
            let base = *env;
8902
            enterLinearLoop(checker, env);
8903
            setLinearLoopNaturalExit(checker, &base);
8904
            let mut bodyEnv = base;
8905
            let start = bodyEnv.len;
8906
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
8907
            if let index = forStmt.index {
8908
                try addLinearBinding(checker, &mut bodyEnv, index);
8909
            }
8910
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
8911
            try finishLinearScope(checker, &mut bodyEnv, start);
8912
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
8913
            exitLinearLoop(checker);
8914
            set *env = base;
8915
            if let elseBranch = forStmt.elseBranch {
8916
                let mut elseEnv = base;
8917
                try checkLinearNode(
8918
                    checker,
8919
                    &mut elseEnv,
8920
                    elseBranch,
8921
                    LinearUse::Discard,
8922
                );
8923
                try joinLinearBranches(checker, env, &base, &elseEnv, node);
8924
            }
8925
        }
8926
        case ast::NodeValue::Loop { body } => {
8927
            let base = *env;
8928
            enterLinearLoop(checker, env);
8929
            let mut bodyEnv = base;
8930
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
8931
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
8932
            let depth = checker.loopDepth - 1;
8933
            let breakSeen = checker.loopBreakSeen[depth];
8934
            let exitAvailable = checker.loopExitAvailable[depth];
8935
            exitLinearLoop(checker);
8936
            set *env = base;
8937
            if breakSeen {
8938
                set env.available = exitAvailable;
8939
            } else {
8940
                set env.terminated = true;
8941
            }
8942
        }
8943
        case ast::NodeValue::Break => {
8944
            assert checker.loopDepth > 0, "linear loop control outside loop";
8945
            let start = checker.loopMarks[checker.loopDepth - 1];
8946
            try finishLinearScope(checker, env, start);
8947
            try checkLinearLoopBreak(checker, env, node);
8948
            set env.terminated = true;
8949
        }
8950
        case ast::NodeValue::Continue => {
8951
            assert checker.loopDepth > 0, "linear loop control outside loop";
8952
            let start = checker.loopMarks[checker.loopDepth - 1];
8953
            try finishLinearScope(checker, env, start);
8954
            try checkLinearLoopBackEdge(checker, env, node);
8955
            set env.terminated = true;
8956
        }
8957
        case ast::NodeValue::Return { value } => {
8958
            if let expr = value {
8959
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8960
            }
8961
            try finishLinearExit(checker, env);
8962
        }
8963
        case ast::NodeValue::Throw { expr } => {
8964
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8965
            try finishLinearExit(checker, env);
8966
        }
8967
        case ast::NodeValue::Panic { message } => {
8968
            if let expr = message {
8969
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8970
            }
8971
            set env.terminated = true;
8972
        }
8973
        case ast::NodeValue::Assert { condition, message } => {
8974
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
8975
            if let expr = message {
8976
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8977
            }
8978
        }
8979
        else => {}
8980
    }
8981
}
8982
8983
/// Check exact-use ownership for one resolved function.
8984
unsafe fn checkLinearFn(
8985
    self: &mut Resolver,
8986
    receiver: ?*ast::Node,
8987
    params: *[*ast::Node],
8988
    body: *ast::Node,
8989
) throws (ResolveError) {
8990
    let mut checker = LinearChecker {
8991
        resolver: self as *unsafe mut Resolver,
8992
        loans: undefined,
8993
        loanLen: 0,
8994
        locals: undefined,
8995
        localLen: 0,
8996
        loopMarks: undefined,
8997
        loopAvailable: undefined,
8998
        loopExitAvailable: undefined,
8999
        loopHasNaturalExit: undefined,
9000
        loopBreakSeen: undefined,
9001
        loopDepth: 0,
9002
    };
9003
    let mut env = LinearEnv {
9004
        symbols: undefined,
9005
        available: 0,
9006
        len: 0,
9007
        terminated: false,
9008
    };
9009
    if let receiverNode = receiver {
9010
        try addLinearBinding(&mut checker, &mut env, receiverNode);
9011
    }
9012
    for paramNode in params {
9013
        let case ast::NodeValue::FnParam(_) = paramNode.value
9014
            else panic "checkLinearFn: expected parameter";
9015
        try addLinearBinding(&mut checker, &mut env, paramNode);
9016
    }
9017
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
9018
    try finishLinearScope(&mut checker, &mut env, 0);
9019
}
9020
9021
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
9022
unsafe fn resolveModuleDefs(self: &mut Resolver, block: &ast::Block) throws (ResolveError) {
9023
    for stmt in block.statements {
9024
        try visitDef(self, stmt);
9025
    }
9026
}
9027
9028
/// Resolve all packages.
9029
/// The graph must outlive later uses of the resolver.
9030
export unsafe fn resolve(self: &mut Resolver, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
9031
    set self.moduleGraph = graph as *unsafe module::ModuleGraph;
9032
9033
    // 1. Bind all package roots to enable cross-package references.
9034
    for i in 0..packages.len {
9035
        let pkg = packages[i];
9036
        // Enter a new scope for the module.
9037
        let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry);
9038
        // Bind the package root module name in the global package scope.
9039
        let scope = self.pkgScope;
9040
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope);
9041
9042
        exitModuleScope(self, enter);
9043
    }
9044
    // 2. Resolve each package's contents.
9045
    for i in 0..packages.len {
9046
        let pkg = packages[i];
9047
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
9048
        if not success(&diags) {
9049
            return diags;
9050
        }
9051
    }
9052
    return diagnostics(self);
9053
}
9054
9055
/// Resolve a package.
9056
unsafe fn resolvePackage(self: &mut Resolver, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
9057
    let rootId = rootEntry.id;
9058
    let scope = self.moduleScopes[rootId as u32]
9059
        else panic "resolvePackage: module scope not found";
9060
9061
    // Set up the module scope for this package.
9062
    set self.scope = scope;
9063
    set self.currentMod = rootId;
9064
9065
    let case ast::NodeValue::Block(block) = node.value
9066
        else panic "resolvePackage: expected block for module root";
9067
9068
    // Module graph analysis phase: bind all module name symbols and scopes.
9069
    try resolveModuleGraph(self, &block) catch {
9070
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
9071
        return diagnostics(self);
9072
    };
9073
9074
    // Declaration phase: bind all names and analyze top-level declarations.
9075
    try resolveModuleDecls(self, &block) catch {
9076
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
9077
    };
9078
    if self.errors.len > 0 {
9079
        return diagnostics(self);
9080
    }
9081
9082
    // Definition phase: analyze function bodies and sub-module definitions.
9083
    try resolveModuleDefs(self, &block) catch {
9084
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
9085
    };
9086
    setNodeType(self, node, Type::Void);
9087
9088
    return diagnostics(self);
9089
}