lib/std/lang/resolver.rad 452.9 KiB raw
1
//! Radiance semantic analyzer and type resolver.
2
//!
3
//! This module performs scope construction, symbol binding, and identifier
4
//! resolution on top of the AST produced by the parser.
5
6
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
/// Power-of-two bucket count for interned type lookup chains.
28
constant TYPE_BUCKETS: u32 = 1024;
29
/// Power-of-two bucket count for exact nominal application lookup chains.
30
constant APPLICATION_BUCKETS: u32 = 1024;
31
/// Odd multiplier that combines cache key components.
32
constant CACHE_HASH_PRIME: u32 = 16777619;
33
34
/// Synthetic function name used when wrapping a bare expression for analysis.
35
export constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__";
36
/// Synthetic function name used when wrapping a block for analysis.
37
export constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__";
38
39
/// Maximum number of symbols stored within a module scope.
40
export constant MAX_MODULE_SYMBOLS: u32 = 512;
41
/// Maximum number of symbols stored within a local scope.
42
export constant MAX_LOCAL_SYMBOLS: u32 = 32;
43
/// Maximum function parameters.
44
export constant MAX_FN_PARAMS: u32 = 8;
45
/// Maximum function thrown types.
46
export constant MAX_FN_THROWS: u32 = 8;
47
/// Maximum number of variants in a union.
48
/// Nb. This should not be raised above `255`,
49
/// as tags are stored using 8-bits only.
50
export constant MAX_UNION_VARIANTS: u32 = 128;
51
/// Maximum nesting of loops.
52
export constant MAX_LOOP_DEPTH: u32 = 16;
53
/// Maximum trait instances.
54
export constant MAX_INSTANCES: u32 = 128;
55
/// Maximum standalone methods (across all types).
56
export constant MAX_METHODS: u32 = 256;
57
/// Maximum number of linear bindings active in one function.
58
constant MAX_LINEAR_BINDINGS: u32 = 32;
59
/// Maximum full-region projections active in nested lexical regions.
60
constant MAX_REGIONAL_LOANS: u32 = 32;
61
/// Maximum inline field depth used to prove borrow separation.
62
constant MAX_BORROW_FIELDS: u32 = 16;
63
/// Maximum nesting depth tracked for loops.
64
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
65
66
/// Trait definition stored in the resolver.
67
export record TraitType: Copy {
68
    /// Trait name.
69
    name: *[u8],
70
    /// Module that declares the trait.
71
    moduleId: u16,
72
    /// Method signatures, including from supertraits.
73
    methods: *unsafe mut [TraitMethod],
74
    /// Supertraits that must also be implemented.
75
    supertraits: *unsafe mut [*unsafe TraitType],
76
}
77
78
/// A single method signature within a trait.
79
export record TraitMethod: Copy {
80
    /// Method name.
81
    name: *[u8],
82
    /// Function type for the method, excluding the receiver.
83
    fnType: *FnType,
84
    /// Whether the receiver is mutable.
85
    mutable: bool,
86
    /// Pointer-like class used by the receiver.
87
    receiverClass: types::PointerClass,
88
    /// V-table slot index.
89
    index: u32,
90
}
91
92
/// An entry in the trait instance registry.
93
export record InstanceEntry: Copy {
94
    /// Trait type descriptor.
95
    traitType: *unsafe TraitType,
96
    /// Concrete type that implements the trait.
97
    concreteType: Type,
98
    /// Name of the concrete type.
99
    concreteTypeName: *[u8],
100
    /// Module where this instance was declared.
101
    moduleId: u16,
102
    /// Method symbols for each trait method, in declaration order.
103
    methods: *unsafe mut [*unsafe mut Symbol],
104
}
105
106
/// An entry in the method registry.
107
export record MethodEntry: Copy {
108
    /// Concrete type that owns the method.
109
    concreteType: Type,
110
    /// Name of the concrete type.
111
    concreteTypeName: *[u8],
112
    /// Method name.
113
    name: *[u8],
114
    /// Function type excluding the receiver.
115
    fnType: *FnType,
116
    /// Whether the receiver is mutable.
117
    mutable: bool,
118
    /// Pointer-like class used by the receiver.
119
    receiverClass: types::PointerClass,
120
    /// Resolver-local identity of the method symbol.
121
    symbolId: u32,
122
    /// Function type including the receiver, used for emitted calls.
123
    fullFnType: *FnType,
124
}
125
126
/// Identifier for the synthetic `len` field.
127
export constant LEN_FIELD: *[u8] = "len";
128
/// Identifier for the synthetic `ptr` field.
129
export constant PTR_FIELD: *[u8] = "ptr";
130
/// Identifier for the synthetic `cap` field.
131
export constant CAP_FIELD: *[u8] = "cap";
132
133
/// Maximum `u16` value.
134
constant U16_MAX: u16 = 0xFFFF;
135
/// Maximum `u8` value.
136
constant U8_MAX: u16 = 0xFF;
137
138
/// Minimum `i8` value.
139
constant I8_MIN: i32 = -128;
140
/// Maximum `i8` value.
141
constant I8_MAX: i32 = 127;
142
/// Minimum `i16` value.
143
constant I16_MIN: i32 = -32768;
144
/// Maximum `i16` value.
145
constant I16_MAX: i32 = 32767;
146
147
/// Minimum `i32` value.
148
constant I32_MIN: i32 = -2147483648;
149
/// Maximum `i32` value.
150
constant I32_MAX: i32 = 2147483647;
151
/// Minimum `i64` value: -(2^63).
152
constant I64_MIN: i64 = -9223372036854775808;
153
/// Maximum `i64` value: 2^63 - 1.
154
constant I64_MAX: i64 = 9223372036854775807;
155
156
/// Size of a pointer in bytes.
157
export constant PTR_SIZE: u32 = 8;
158
159
/// Information about a record or tuple field.
160
export record RecordField: Copy {
161
    /// Field name, `nil` for positional fields.
162
    name: ?*[u8],
163
    /// Field type.
164
    fieldType: Type,
165
    /// Byte offset from the start of the record.
166
    offset: i32,
167
}
168
169
/// Information about a union variant.
170
record UnionVariant: Copy {
171
    name: *[u8],
172
    valueType: Type,
173
    symbol: *unsafe mut Symbol,
174
}
175
176
/// Array type payload.
177
export record ArrayType: Copy {
178
    item: *Type,
179
    length: u32,
180
}
181
182
/// Record nominal type.
183
export record RecordType: Copy {
184
    /// Region parameters of the source declaration.
185
    regions: ?*RegionScope,
186
    /// Exact region arguments, if this is an applied type.
187
    application: ?*unsafe NominalApplication,
188
    fields: *unsafe [RecordField],
189
    labeled: bool,
190
    /// Shared layout of the source declaration.
191
    layout: *Layout,
192
    /// Whether the declaration explicitly carries the `Once` marker.
193
    declaredLinear: bool,
194
    /// Whether the declaration explicitly carries the `Copy` marker.
195
    declaredCopy: bool,
196
}
197
198
/// Union nominal type.
199
export record UnionType: Copy {
200
    /// Region parameters of the source declaration.
201
    regions: ?*RegionScope,
202
    /// Exact region arguments, if this is an applied type.
203
    application: ?*unsafe NominalApplication,
204
    variants: *unsafe [UnionVariant],
205
    /// Shared layout of the source declaration.
206
    layout: *Layout,
207
    /// Cached payload offset within the union aggregate.
208
    valOffset: u32,
209
    /// If all variants have void payloads.
210
    isAllVoid: bool,
211
    /// Whether the declaration explicitly carries the `Once` marker.
212
    declaredLinear: bool,
213
    /// Whether the declaration explicitly carries the `Copy` marker.
214
    declaredCopy: bool,
215
}
216
217
/// Metadata for user-defined types.
218
export union NominalType: Copy {
219
    /// Placeholder for a type that hasn't been fully resolved yet.
220
    /// Stores the declaration node for lazy resolution.
221
    Placeholder(*ast::Node),
222
    /// Declaration whose value layout is under analysis.
223
    Resolving(*ast::Node),
224
    /// Applied type whose field or variant view is not yet resolved.
225
    Application(*unsafe NominalApplication),
226
    Record(RecordType),
227
    Union(UnionType),
228
}
229
230
/// Coercion plan, when coercion from one type to another.
231
export union Coercion: Copy {
232
    /// No coercion, eg. `T -> T`.
233
    Identity,
234
    /// Eg. `u8 -> i32`. Stores both source and target types for lowering.
235
    NumericCast { from: Type, to: Type },
236
    /// Eg. `T -> ?T`. Stores the inner value type.
237
    OptionalLift(Type),
238
    /// Wrap return value in success variant of result type.
239
    ResultWrap,
240
    /// Coerce a concrete pointer to a trait object.
241
    TraitObject {
242
        /// Trait type information.
243
        traitInfo: *unsafe TraitType,
244
        /// Instance entry for v-table lookup.
245
        inst: *unsafe InstanceEntry,
246
    },
247
}
248
249
/// Result of resolving a module path.
250
record ResolvedModule: Copy {
251
    /// Module entry in the graph.
252
    entry: *module::ModuleEntry,
253
    /// Scope containing the module's declarations.
254
    scope: *unsafe mut Scope,
255
}
256
257
/// Type layout.
258
export record Layout: Copy {
259
    /// Size in bytes.
260
    size: u32,
261
    /// Alignment in bytes.
262
    alignment: u32,
263
}
264
265
/// Computed union layout parameters.
266
record UnionLayoutInfo: Copy {
267
    layout: Layout,
268
    valOffset: u32,
269
    isAllVoid: bool,
270
}
271
272
/// Pre-computed metadata for slice range expressions.
273
/// Used by the lowerer.
274
export record SliceRangeInfo: Copy {
275
    /// Element type of the resulting slice.
276
    itemType: *Type,
277
    /// Whether the resulting slice is mutable.
278
    mutable: bool,
279
    /// Static capacity if container is an array.
280
    capacity: ?u32,
281
}
282
283
/// Pre-computed metadata for `for` loop iteration.
284
/// Used by the lowerer to avoid re-analyzing the iterable type.
285
export union ForLoopInfo: Copy {
286
    /// Iterating over a range expression (e.g., `for i in 0..n`).
287
    Range {
288
        valType: *Type,
289
        range: ast::Range,
290
        bindingName: ?*[u8],
291
        indexName: ?*[u8]
292
    },
293
    /// Iterating over an array or slice. For arrays, the length field is set.
294
    Collection {
295
        elemType: *Type,
296
        length: ?u32,
297
        bindingName: ?*[u8],
298
        indexName: ?*[u8]
299
    },
300
}
301
302
/// Resolved function signature details.
303
export record FnType: Copy {
304
    /// Symbolic regions declared by the source function.
305
    regions: ?*RegionScope,
306
    /// Parameter types in call order.
307
    paramTypes: *[*Type],
308
    /// Return value type.
309
    returnType: *Type,
310
    /// Error types that the function can throw.
311
    throwList: *[*Type],
312
    /// Whether calling this function requires an unsafe context.
313
    isUnsafe: bool,
314
}
315
316
/// Describes a type computed during semantic analysis.
317
export union Type: Copy {
318
    /// A type that couldn't be decided.
319
    Unknown,
320
    /// Types only used during inference.
321
    Nil, Undefined, Int,
322
    /// Primitive types.
323
    Void, Opaque, Never, Bool,
324
    /// Integer types.
325
    U8, U16, U32, U64, I8, I16, I32, I64,
326
    /// Shared cell pointer with value access to a Copy payload.
327
    Cell {
328
        /// Storage lifetime and ownership class.
329
        class: types::PointerClass,
330
        /// Payload type, preserved by all writes.
331
        payload: *Type,
332
    },
333
    /// Affine allocation interface retained by a lexical region.
334
    Session(*unsafe types::Region),
335
    /// Range types, eg. `start..end`.
336
    Range {
337
        start: ?*Type,
338
        end: ?*Type,
339
    },
340
    /// Owning pointer-like address.
341
    Pointer {
342
        class: types::PointerClass,
343
        target: *Type,
344
        mutable: bool,
345
    },
346
    /// Owning slice.
347
    Slice {
348
        class: types::PointerClass,
349
        item: *Type,
350
        mutable: bool,
351
    },
352
    /// Eg. `[i32; 32]`.
353
    Array(ArrayType),
354
    /// Eg. `?T`.
355
    Optional(*Type),
356
    /// Eg. `fn id(i32) -> i32`.
357
    Fn(*FnType),
358
    /// Named, ie. user-defined types, includes union variants.
359
    Nominal(*unsafe NominalType),
360
    /// Owning trait object. An erased type with v-table.
361
    TraitObject {
362
        /// Ownership and safety class.
363
        class: types::PointerClass,
364
        /// Trait definition.
365
        traitInfo: *unsafe TraitType,
366
        /// Whether the pointer is mutable.
367
        mutable: bool,
368
    },
369
}
370
371
/// Structured diagnostic payload for type mismatches.
372
export record TypeMismatch: Copy {
373
    expected: Type,
374
    actual: Type,
375
}
376
377
/// Structured diagnostic payload for invalid `as` casts.
378
export record InvalidAsCast: Copy {
379
    from: Type,
380
    to: Type,
381
}
382
383
/// Diagnostic payload for argument count mismatches.
384
export record CountMismatch: Copy {
385
    expected: u32,
386
    actual: u32,
387
}
388
389
/// Detailed payload attached to a symbol, specialized per symbol kind.
390
export union SymbolData: Copy {
391
    /// Payload describing mutable bindings like variables or functions.
392
    Value {
393
        /// Whether the binding permits mutation.
394
        mutable: bool,
395
        /// Custom alignment requirement, or 0 for default.
396
        alignment: u32,
397
        /// Resolved type associated with the value.
398
        type: Type,
399
        /// Whether the variable's address is taken anywhere (via `&` or `&mut`).
400
        /// Used by the lowerer to allocate a stack slot eagerly.
401
        addressTaken: bool,
402
    },
403
    /// Payload describing constants.
404
    Constant {
405
        /// Resolved type associated with the value.
406
        type: Type,
407
        /// Constant value, if any.
408
        value: ?ConstValue,
409
    },
410
    /// Payload describing union variants and the union type they instantiate.
411
    Variant {
412
        /// Variant payload type.
413
        type: Type,
414
        /// Union declaration.
415
        decl: *ast::Node,
416
        /// Variant ordinal in declaration order.
417
        ordinal: u32,
418
        /// Variant index within the union.
419
        index: u32,
420
    },
421
    /// Module reference.
422
    Module {
423
        /// Module entry in the graph.
424
        entry: *module::ModuleEntry,
425
        /// Module scope.
426
        scope: *unsafe mut Scope,
427
    },
428
    /// Payload describing type symbols with their resolved type.
429
    Type(*unsafe mut NominalType),
430
    /// Trait symbol.
431
    Trait(*unsafe mut TraitType),
432
}
433
434
/// Resolved symbol allocated during semantic analysis.
435
export record Symbol: Copy {
436
    /// Unique identity within the resolver that created this symbol.
437
    id: u32,
438
    /// Symbol name in source code.
439
    name: *[u8],
440
    /// Data associated with the symbol.
441
    data: SymbolData,
442
    /// Bitset of attributes applied to the declaration.
443
    attrs: u32,
444
    /// AST node that introduced the symbol.
445
    node: *ast::Node,
446
    /// Module ID this symbol belongs to. Only for module-level symbols.
447
    moduleId: ?u16,
448
}
449
450
/// Integer constant payload.
451
export record ConstInt: Copy {
452
    /// Absolute magnitude of the value.
453
    magnitude: u64,
454
    /// Bit width of the integer.
455
    bits: u8,
456
    /// Whether the integer is signed.
457
    signed: bool,
458
    /// Whether the value is negative (only valid when `signed` is true).
459
    negative: bool,
460
}
461
462
/// Constant value recorded for literal nodes.
463
export union ConstValue: Copy {
464
    Bool(bool),
465
    Char(u8),
466
    String(*[u8]),
467
    Int(ConstInt),
468
}
469
470
/// Integer range metadata for primitive integer types.
471
union IntegerRange: Copy {
472
    Signed {
473
        bits: u8,
474
        min: i64,
475
        max: i64,
476
        lim: u64,
477
    },
478
    Unsigned {
479
        bits: u8,
480
        max: u64,
481
    },
482
}
483
484
/// Diagnostic emitted by the analyzer.
485
export record Error: Copy {
486
    /// Error category.
487
    kind: ErrorKind,
488
    /// Node associated with the error, if known.
489
    node: ?*ast::Node,
490
    /// Module ID where this error occurred.
491
    moduleId: u16,
492
}
493
494
/// High-level classification for semantic diagnostics.
495
export union ErrorKind: Copy {
496
    /// Identifier declared more than once in the same scope.
497
    DuplicateBinding(*[u8]),
498
    /// Identifier referenced before it was declared.
499
    UnresolvedSymbol(*[u8]),
500
    /// Attempted to assign to an immutable binding.
501
    ImmutableBinding,
502
    /// Slice append requires a valid allocator record and callback.
503
    InvalidSliceAllocator,
504
    /// Expected a compile-time constant expression.
505
    ConstExprRequired,
506
    /// Symbol arena exhausted while binding identifiers.
507
    SymbolOverflow,
508
    /// Expression has the wrong type.
509
    TypeMismatch(TypeMismatch),
510
    /// Numeric literal does not fit within the required range.
511
    NumericLiteralOverflow,
512
    /// Record literal omitted a required field.
513
    RecordFieldMissing(*[u8]),
514
    /// Record literal referenced a field that does not exist.
515
    RecordFieldUnknown(*[u8]),
516
    /// Brace syntax used on unlabeled record.
517
    RecordFieldStyleMismatch,
518
    /// Record literal supplied the wrong number of fields.
519
    RecordFieldCountMismatch(CountMismatch),
520
    /// Record literal fields not in declaration order.
521
    RecordFieldOutOfOrder { field: *[u8], prev: *[u8] },
522
    /// Function call supplied the wrong number of arguments.
523
    FnArgCountMismatch(CountMismatch),
524
    /// Function throws list has the wrong number of types.
525
    FnThrowCountMismatch(CountMismatch),
526
    /// Expected an identifier node.
527
    ExpectedIdentifier,
528
    /// Expected any optional type.
529
    ExpectedOptional,
530
    /// Expected a numeric type.
531
    ExpectedNumeric,
532
    /// Expected a pointer type.
533
    ExpectedPointer,
534
    /// Expected a record type.
535
    ExpectedRecord,
536
    /// Expected an array or slice value.
537
    ExpectedIndexable,
538
    /// Expected an iterable (array, slice, or range) for a `for` loop.
539
    ExpectedIterable,
540
    /// Invalid `as` cast between the provided types.
541
    InvalidAsCast(InvalidAsCast),
542
    /// Invalid alignment value specified.
543
    InvalidAlignmentValue(u32),
544
    /// Invalid module path.
545
    InvalidModulePath,
546
    /// Invalid identifier.
547
    InvalidIdentifier(*ast::Node),
548
    /// Invalid scope access.
549
    InvalidScopeAccess,
550
    /// Referenced an unknown array field.
551
    ArrayFieldUnknown(*[u8]),
552
    /// Referenced an unknown slice field.
553
    SliceFieldUnknown(*[u8]),
554
    /// Array slicing without taking an address.
555
    SliceRequiresAddress,
556
    /// Slice bounds exceed array length.
557
    SliceRangeOutOfBounds,
558
    /// Unexpected `return` statement.
559
    UnexpectedReturn,
560
    /// Unexpected module name.
561
    UnexpectedModuleName,
562
    /// Unexpected node.
563
    UnexpectedNode(*ast::Node),
564
    /// Function with non-void return type falls through without returning.
565
    FnMissingReturn,
566
    /// Function is missing a body.
567
    FnMissingBody,
568
    /// Function body is not expected.
569
    FnUnexpectedBody,
570
    /// Intrinsic function must not have a body.
571
    IntrinsicUnexpectedBody,
572
    /// Encountered loop control outside of a loop construct.
573
    InvalidLoopControl,
574
    /// `try` used when the enclosing function does not declare throws.
575
    TryRequiresThrows,
576
    /// `try` used to propagate an error not declared by the enclosing function.
577
    TryIncompatibleError,
578
    /// `throw` used when the enclosing function does not declare throws.
579
    ThrowRequiresThrows,
580
    /// `throw` used with an error type not declared by the enclosing function.
581
    ThrowIncompatibleError,
582
    /// `try` applied to an expression that cannot throw.
583
    TryNonThrowing,
584
    /// Inferred catch binding used with multi-error callee.
585
    TryCatchMultiError,
586
    /// Duplicate error type in typed catch clauses.
587
    TryCatchDuplicateType,
588
    /// Distinct error types have the same tag after region erasure.
589
    AmbiguousRegionalError,
590
    /// Typed catch clauses do not cover all error types.
591
    TryCatchNonExhaustive,
592
    /// Called a fallible function without using `try`.
593
    MissingTry,
594
    /// Cannot use opaque type in this context.
595
    OpaqueTypeNotAllowed,
596
    /// Cannot dereference pointer to opaque type.
597
    OpaqueTypeDeref,
598
    /// Cannot perform pointer arithmetic on opaque pointer.
599
    OpaquePointerArithmetic,
600
    /// Cannot infer type from context.
601
    CannotInferType,
602
    /// Cannot assign a void value to a variable.
603
    CannotAssignVoid,
604
    /// `default` attribute used on a non-function declaration.
605
    DefaultAttrOnlyOnFn,
606
    /// Union variant requires a payload but none was provided.
607
    UnionVariantPayloadMissing(*[u8]),
608
    /// Union variant does not expect a payload but one was provided.
609
    UnionVariantPayloadUnexpected(*[u8]),
610
    /// `match` on a union omits a variant without a `default` case.
611
    UnionMatchNonExhaustive(*[u8]),
612
    /// `match` on an optional is missing a value case.
613
    OptionalMatchMissingValue,
614
    /// `match` on an optional is missing a nil case.
615
    OptionalMatchMissingNil,
616
    /// `match` on a bool is missing a case (true or false).
617
    BoolMatchMissing(bool),
618
    /// `match` on a non-union type is missing a catch-all.
619
    MatchNonExhaustive,
620
    /// `match` has more than one catch-all prongs.
621
    DuplicateCatchAll,
622
    /// `match` has a duplicate case pattern.
623
    DuplicateMatchPattern,
624
    /// `match` has an unreachable `else`: all cases are already handled.
625
    UnreachableElse,
626
    /// Builtin called with wrong number of arguments.
627
    BuiltinArgCountMismatch(CountMismatch),
628
    /// Instance method receiver mutability does not match the trait declaration.
629
    ReceiverMutabilityMismatch,
630
    /// Duplicate instance declaration for the same (trait, type) pair.
631
    DuplicateInstance,
632
    /// Instance declaration is missing a required trait method.
633
    MissingTraitMethod(*[u8]),
634
    /// Trait name used as a value expression.
635
    UnexpectedTraitName,
636
    /// Trait method receiver does not point to the declaring trait.
637
    TraitReceiverMismatch,
638
    /// Trait declaration and instance disagree about unsafe call requirements.
639
    TraitMethodSafetyMismatch,
640
    /// Function declaration has too many parameters.
641
    FnParamOverflow(CountMismatch),
642
    /// Function declaration has too many throws.
643
    FnThrowOverflow(CountMismatch),
644
    /// Trait declaration has too many methods.
645
    TraitMethodOverflow(CountMismatch),
646
    /// Instance declaration is missing a required supertrait instance.
647
    MissingSupertraitInstance(*[u8]),
648
    /// An affine binding was used after it moved.
649
    AffineUseAfterMove(*[u8]),
650
    /// Linear binding was consumed more than once.
651
    LinearUseAfterConsume(*[u8]),
652
    /// Linear binding remains available at an exit.
653
    LinearNotConsumed(*[u8]),
654
    /// A case-pattern `let-else` fallback must terminate control flow.
655
    LinearLetElseMustTerminate,
656
    /// Branches disagree about a linear binding's state.
657
    LinearBranchMismatch(*[u8]),
658
    /// A linear field cannot be moved independently.
659
    LinearPartialMove,
660
    /// A linear value cannot be discarded.
661
    LinearDiscard,
662
    /// Assignment would overwrite a live linear value.
663
    LinearOverwrite,
664
    /// `undefined` cannot initialize a linear type.
665
    LinearUndefined,
666
    /// A `Copy` declaration contains a non-copy field or variant.
667
    CopyContainsNonCopy,
668
    /// A declaration carries both `Copy` and `Once`.
669
    ConflictingOwnershipMarkers,
670
    /// A region name is not visible in this declaration or block.
671
    UnknownRegion(*[u8]),
672
    /// A region application has the wrong argument count.
673
    RegionArgumentCount(CountMismatch),
674
    /// A region parameter has no consistent argument from checked references.
675
    RegionInference(*[u8]),
676
    /// A region argument does not satisfy its declared parent relation.
677
    RegionParent(*[u8]),
678
    /// A region parent relation contains a cycle.
679
    RegionCycle(*[u8]),
680
    /// A value retains a region that has left lexical scope.
681
    RegionEscape(*[u8]),
682
    /// A session requires one exclusive borrow of an allocation trait implementer.
683
    InvalidSessionSource,
684
    /// Allocation requires a value that can be discarded without destruction.
685
    InvalidAllocationValue,
686
    /// Cell payload is not a storable plain Copy value.
687
    InvalidCellPayload,
688
    /// The allocated value has an invalid or overflowing layout.
689
    InvalidAllocationLayout,
690
    /// A compiler-known allocation trait method has an invalid signature.
691
    InvalidAllocationRuntime,
692
    /// The function has too many distinct full-region projections.
693
    RegionalLoanOverflow,
694
    /// A nominal value layout contains itself.
695
    RecursiveType,
696
    /// A reference appears in a storable or escaping position.
697
    InvalidRefPosition,
698
    /// A reference local requires a fixed binding to existing storage.
699
    RefBinding,
700
    /// Call arguments contain overlapping incompatible loans.
701
    BorrowConflict(*[u8]),
702
    /// Unsafe operation outside an unsafe context.
703
    UnsafeOperation,
704
    /// An unsafe call requires an unsafe context.
705
    UnsafeCall,
706
    /// Internal error.
707
    Internal,
708
}
709
710
/// Diagnostics returned by the analyzer.
711
export record Diagnostics: Copy {
712
    /// Immutable errors captured at the end of an analysis operation.
713
    errors: *[Error],
714
}
715
716
/// Mutable diagnostic storage owned by a resolver.
717
record DiagnosticBuffer {
718
    /// Backing entries. Only the prefix below `len` is initialized.
719
    entries: *mut [Error],
720
    /// Number of recorded errors.
721
    len: u32,
722
}
723
724
/// Call context.
725
union CallCtx: Copy {
726
    /// Normal function call.
727
    Normal,
728
    /// Fallible function call, ie. `try f()`.
729
    Try,
730
}
731
732
/// Result of resolving a record literal's type name.
733
record ResolvedRecordLitType: Copy {
734
    /// The record nominal type to use for field checking.
735
    recordType: *unsafe NominalType,
736
    /// The result type of the literal (record type or union type for variants).
737
    resultType: Type,
738
}
739
740
/// Result of checking for a `super` path prefix.
741
record SuperAccessResult: Copy {
742
    scope: *unsafe mut Scope,
743
    child: *ast::Node,
744
}
745
746
/// Initialization operation performed after session storage reservation.
747
export union SessionAllocationKind: Copy {
748
    /// Initialize one object from a value.
749
    New,
750
    /// Copy plain Copy elements from a slice.
751
    Copy,
752
    /// Fill a slice with a plain Copy value.
753
    Fill,
754
}
755
756
/// Typed session allocation and its checked runtime reservation function.
757
export record SessionAllocation: Copy {
758
    /// Initialization operation.
759
    kind: SessionAllocationKind,
760
    /// Initialized element type.
761
    item: *Type,
762
    /// Allocation trait used by the session source.
763
    traitInfo: *unsafe TraitType,
764
    /// Reservation method slot in the allocation trait.
765
    methodIndex: u32,
766
}
767
768
/// Node-specific resolver metadata.
769
export union NodeExtra: Copy {
770
    /// No extra data for this node.
771
    None,
772
    /// Region identities owned by a source declaration.
773
    Regions(*RegionScope),
774
    /// Resolved field index for record literal fields.
775
    RecordField { index: u32 },
776
    /// Slice range metadata for subscript expressions with ranges.
777
    SliceRange(SliceRangeInfo),
778
    /// Cached union variant metadata for patterns/constructors.
779
    UnionVariant { ordinal: u32, tag: u32 },
780
    /// Match prong metadata.
781
    MatchProng { catchAll: bool },
782
    /// Match expression metadata.
783
    Match { isConst: bool },
784
    /// For-loop iteration metadata.
785
    ForLoop(ForLoopInfo),
786
    /// Trait method call metadata.
787
    TraitMethodCall {
788
        /// Trait definition.
789
        traitInfo: *unsafe TraitType,
790
        /// Method index in the v-table.
791
        methodIndex: u32,
792
    },
793
    /// Standalone method call metadata.
794
    MethodCall { method: *unsafe MethodEntry },
795
    /// Typed allocation through a session interface.
796
    SessionAllocation(SessionAllocation),
797
    /// Slice `.append(val, allocator)` method call.
798
    SliceAppend { elemType: *Type },
799
    /// Slice `.delete(index)` method call.
800
    SliceDelete { elemType: *Type },
801
}
802
803
/// Symbol identity and storage associated with a resolved AST node.
804
export record ResolvedSymbol: Copy {
805
    /// Identity within the resolver that owns the node metadata.
806
    id: u32,
807
    /// Symbol storage used by type resolution and lowering.
808
    symbol: *unsafe mut Symbol,
809
}
810
811
/// Combined resolver metadata for a single AST node.
812
export record NodeData: Copy {
813
    /// Number of local bindings and internal iteration variables in this function.
814
    localCount: u32,
815
    /// Resolved type for this node.
816
    ty: Type,
817
    /// Coercion plan applied to this node.
818
    coercion: Coercion,
819
    /// Symbol identity and storage associated with this node.
820
    binding: ?ResolvedSymbol,
821
    /// Constant value for literal nodes.
822
    constValue: ?ConstValue,
823
    /// Lexical scope owned by this node.
824
    scope: ?*unsafe mut Scope,
825
    /// Node-specific extra data.
826
    extra: NodeExtra,
827
}
828
829
/// Table storing all resolver metadata indexed by node ID.
830
record NodeDataTable {
831
    /// Semantic data indexed by AST node ID.
832
    entries: *mut [NodeData],
833
}
834
835
/// Lexical scope.
836
export record Scope: Copy {
837
    /// Owning AST node, or `nil` for the root scope.
838
    owner: ?*ast::Node,
839
    /// Parent/enclosing scope.
840
    parent: ?*unsafe mut Scope,
841
    /// Module ID if this is a module scope.
842
    moduleId: ?u16,
843
    /// Symbols introduced inside the scope, allocated from the arena.
844
    symbols: *unsafe mut [*unsafe mut Symbol],
845
    /// Number of live symbols.
846
    symbolsLen: u32,
847
}
848
849
/// An object used by the enter and exit functions for module scopes.
850
record ModuleScope: Copy {
851
    /// Module root node.
852
    root: *ast::Node,
853
    /// Module entry in graph.
854
    entry: *module::ModuleEntry,
855
    /// The newly entered scope.
856
    newScope: *unsafe mut Scope,
857
    /// The previous scope.
858
    prevScope: *unsafe mut Scope,
859
    /// The previous module.
860
    prevMod: u16,
861
}
862
863
/// Loop context for tracking control flow within loops.
864
record LoopCtx: Copy {
865
    /// Whether a reachable break was encountered in this loop.
866
    /// This is used to determine whether a loop diverges.
867
    hasBreak: bool,
868
}
869
870
/// Configuration for semantic analysis.
871
export record Config: Copy {
872
    /// Whether we're building in test mode.
873
    buildTest: bool,
874
}
875
876
/// How pattern bindings are created during match.
877
export union MatchBy: Copy {
878
    /// Match by value.
879
    Value,
880
    /// Match by immutable reference.
881
    Ref,
882
    /// Match by mutable reference.
883
    MutRef,
884
}
885
886
/// State of a match statement being resolved.
887
// TODO: This is only used because of the maximum function param limitation.
888
record MatchState: Copy {
889
    /// Is the match catch-all?
890
    catchAll: bool,
891
    /// Is the match constant?
892
    isConst: bool
893
}
894
895
/// Result of unwrapping a type for pattern matching.
896
export record MatchSubject: Copy {
897
    /// The effective type to match against.
898
    effectiveTy: Type,
899
    /// How bindings should be created.
900
    by: MatchBy,
901
}
902
903
/// How an expression uses a linear result.
904
union LinearUse: Copy {
905
    /// Consume the value and end its availability.
906
    Consume,
907
    /// Read the value without consuming it.
908
    Observe,
909
    /// Borrow the value through a reference.
910
    Borrow,
911
    /// Discard an unused expression result.
912
    Discard,
913
    /// Use the value as an assignment target.
914
    Place,
915
    /// Evaluate a place prefix after checking the complete place.
916
    Locate,
917
}
918
919
/// Consumption rule for a tracked move-only binding.
920
union BindingUse: Copy {
921
    /// The binding can be consumed at most once.
922
    Affine,
923
    /// The binding must be consumed exactly once.
924
    Linear,
925
}
926
927
/// Resolved binding metadata retained for ownership checks and diagnostics.
928
record TrackedSymbol: Copy {
929
    /// Resolver-local symbol identity.
930
    id: u32,
931
    /// Source name used in ownership diagnostics.
932
    name: *[u8],
933
    /// Declaration used to locate an unconsumed binding.
934
    node: *ast::Node,
935
    /// Consumption rule fixed before ownership analysis.
936
    usage: BindingUse,
937
}
938
939
/// Per-control-flow-path ownership state.
940
/// Active binding slots below `len` must contain metadata.
941
record LinearEnv: Copy {
942
    /// Active full-region loans, indexed by the checker's regional loan table.
943
    regionalLoans: u64,
944
    /// Initialized slots for resolved binding metadata.
945
    symbols: [?TrackedSymbol; MAX_LINEAR_BINDINGS],
946
    /// Bit set for each binding that remains available.
947
    available: u64,
948
    /// Number of active binding slots in `symbols`.
949
    len: u32,
950
    /// Whether this control-flow path has terminated.
951
    terminated: bool,
952
}
953
954
/// A storage root and its statically distinct record fields.
955
record BorrowPlace: Copy {
956
    /// Symbol that owns or supplies the storage.
957
    root: ?*unsafe mut Symbol,
958
    /// Field indices before the first uncertain projection.
959
    fields: [u32; MAX_BORROW_FIELDS],
960
    /// Number of initialized field indices.
961
    len: u32,
962
    /// Whether further projections can identify distinct storage.
963
    precise: bool,
964
}
965
966
/// A reference binding that protects its source for one lexical scope.
967
record LocalLoan: Copy {
968
    /// Local symbol that provides access, or nil for a pending call argument.
969
    binding: ?*unsafe mut Symbol,
970
    /// Storage retained by the reference.
971
    place: BorrowPlace,
972
    /// Whether other reads of the source are excluded.
973
    exclusive: bool,
974
}
975
976
/// Argument metadata retained during call-scoped conflict checks.
977
record CallArgument: Copy {
978
    /// Receiver or explicit argument expression.
979
    node: *ast::Node,
980
    /// Whether overlapping argument access is excluded.
981
    exclusive: bool,
982
}
983
984
/// Function-local exact-use checker state.
985
/// Read loop arrays only at indices below `loopDepth`.
986
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
987
record LinearChecker: 'arena + 'checking where 'arena: 'checking {
988
    /// Resolver that owns the symbols and diagnostics.
989
    resolver: &'checking mut Resolver 'arena,
990
    /// Regional projections discovered in this function.
991
    regional: [?RegionalLoan; MAX_REGIONAL_LOANS],
992
    /// Number of active regional loan entries.
993
    regionalLen: u32,
994
    /// Named regions active at the current source location.
995
    regions: ?*RegionScope,
996
    /// Regional loans carried to each loop's next iteration.
997
    loopBackLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
998
    /// Regional loans carried to each loop's exits.
999
    loopExitLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
1000
    /// Regions active at each loop's entry and exit.
1001
    loopRegions: [?*RegionScope; MAX_LINEAR_LOOP_DEPTH],
1002
    /// Source places protected by active pattern references.
1003
    loans: [BorrowPlace; MAX_LINEAR_BINDINGS],
1004
    /// Number of active entries in `loans`.
1005
    loanLen: u32,
1006
    /// Reference locals in active lexical scopes.
1007
    locals: [LocalLoan; MAX_LINEAR_BINDINGS],
1008
    /// Number of active local loans.
1009
    localLen: u32,
1010
    /// Binding count at entry to each active loop.
1011
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
1012
    /// Available bindings at entry to each active loop.
1013
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
1014
    /// Available bindings shared by the exits from each active loop.
1015
    loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
1016
    /// Whether each active loop can exit without `break`.
1017
    loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH],
1018
    /// Whether each active loop contains a reachable `break`.
1019
    loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH],
1020
    /// Number of active loops.
1021
    loopDepth: u32,
1022
}
1023
1024
/// Unwrap a pointer type for pattern matching.
1025
export fn unwrapMatchSubject(ty: Type) -> MatchSubject {
1026
    if let case Type::Pointer { target, mutable, .. } = ty {
1027
        let by = MatchBy::MutRef if mutable else MatchBy::Ref;
1028
        return MatchSubject { effectiveTy: *target, by };
1029
    }
1030
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
1031
}
1032
1033
/// Source nodes that define the identities in one region environment.
1034
export union RegionDeclarations: Copy {
1035
    /// Declaration parameters, including any non-region constraints.
1036
    Parameters(*[*ast::Node]),
1037
    /// Single region introduced by a lexical block.
1038
    Block(*ast::Node),
1039
}
1040
1041
/// Region names introduced by a declaration or lexical block.
1042
export record RegionScope: Copy {
1043
    /// Immutable source declarations that supply region identities.
1044
    declarations: RegionDeclarations,
1045
    /// Entries in declaration order.
1046
    entries: *unsafe [*unsafe mut types::Region],
1047
    /// Enclosing lexical region environment.
1048
    parent: ?*RegionScope,
1049
}
1050
1051
/// Region arguments for one source declaration.
1052
record RegionSubstitution: Copy {
1053
    /// Declared parameters in source order.
1054
    parameters: *RegionScope,
1055
    /// Inferred or explicit arguments. Every entry must be set before substitution.
1056
    arguments: *unsafe mut [?*unsafe types::Region],
1057
}
1058
1059
/// One interned application of a nominal declaration to exact region arguments.
1060
export record NominalApplication: Copy {
1061
    /// Canonical source declaration identity.
1062
    base: *unsafe NominalType,
1063
    /// Source parameters in declaration order.
1064
    parameters: *RegionScope,
1065
    /// Region arguments in parameter order.
1066
    arguments: *unsafe [*unsafe types::Region],
1067
    /// Stable descriptor for the substituted field or variant view.
1068
    view: *unsafe mut NominalType,
1069
    /// Next application in the resolver cache.
1070
    next: ?*unsafe NominalApplication,
1071
    /// Next application in the same lookup bucket.
1072
    bucketNext: ?*unsafe NominalApplication,
1073
}
1074
1075
/// Global resolver state.
1076
export record Resolver: 'arena {
1077
    /// Number of symbol identities allocated by this resolver.
1078
    symbolCount: u32,
1079
    /// Active region names for source type checking.
1080
    regionScope: ?*RegionScope,
1081
    /// Interned applications of nominal region parameters.
1082
    applications: ?*unsafe NominalApplication,
1083
    /// First entry in the fully resolved suffix of the application list.
1084
    completedApplications: ?*unsafe NominalApplication,
1085
    /// Exact application lookup chains, backed by the resolver arena.
1086
    applicationBuckets: *unsafe mut [?*unsafe NominalApplication],
1087
    /// Current scope.
1088
    scope: *unsafe mut Scope,
1089
    /// Package scope containing package roots and top-level symbols.
1090
    pkgScope: *unsafe mut Scope,
1091
    /// Stack of loop contexts for nested loops.
1092
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
1093
    /// Current loop depth, indexes into loop stack.
1094
    loopDepth: u32,
1095
    /// Signature of the function currently being analyzed.
1096
    currentFn: ?FnType,
1097
    /// Declaration that owns the active function body and its local bindings.
1098
    currentFnNode: ?*ast::Node,
1099
    /// Current module being analyzed.
1100
    currentMod: u16,
1101
    /// Whether the current lexical context permits unsafe operations.
1102
    inUnsafeContext: bool,
1103
    /// Configuration for semantic analysis.
1104
    config: Config,
1105
    /// Caller-owned arena, valid for this resolver and all emitted metadata.
1106
    arena: &'arena mut alloc::Arena,
1107
    /// Combined semantic metadata table indexed by node ID.
1108
    nodeData: NodeDataTable,
1109
    /// Lookup chains for interned types.
1110
    types: *unsafe mut [?*TypeNode],
1111
    /// Diagnostics recorded so far.
1112
    errors: DiagnosticBuffer,
1113
    /// Stable module identities indexed by module ID.
1114
    moduleEntries: [?*module::ModuleEntry; module::MAX_MODULES],
1115
    /// Cache of module scopes indexed by module ID.
1116
    moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES],
1117
    /// Trait instance registry.
1118
    instances: [InstanceEntry; MAX_INSTANCES],
1119
    /// Number of registered instances.
1120
    instancesLen: u32,
1121
    /// Standalone method registry.
1122
    methods: [MethodEntry; MAX_METHODS],
1123
    /// Number of registered standalone methods.
1124
    methodsLen: u32,
1125
}
1126
1127
/// Internal error sentinel thrown when analysis cannot proceed.
1128
export union ResolveError: Copy {
1129
    Failure,
1130
}
1131
1132
/// Node in a type interning lookup chain.
1133
record TypeNode: Copy {
1134
    /// Exact interned type value.
1135
    ty: Type,
1136
    /// Next type in the same lookup bucket.
1137
    next: ?*TypeNode,
1138
}
1139
1140
/// Look up a region name in a lexical environment.
1141
unsafe fn findRegion(scope: ?*RegionScope, name: *[u8]) -> ?*unsafe mut types::Region {
1142
    let mut current = scope;
1143
    while let env = current {
1144
        for region in env.entries {
1145
            if mem::eq(region.name, name) {
1146
                return region;
1147
            }
1148
        }
1149
        set current = env.parent;
1150
    }
1151
    return nil;
1152
}
1153
1154
/// Resolve a source region name without using its spelling as an identity.
1155
unsafe fn resolveRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe types::Region
1156
    throws (ResolveError)
1157
{
1158
    let case ast::NodeValue::Region { name, .. } = node.value
1159
        else panic "resolveRegion: invalid region node";
1160
    let region = findRegion(self.regionScope, name)
1161
        else throw emitError(self, node, ErrorKind::UnknownRegion(name));
1162
    return region;
1163
}
1164
1165
/// Bind all region parameters before resolving their parent relations.
1166
unsafe fn bindRegions 'arena (self: &mut Resolver 'arena, owner: *ast::Node, nodes: *[*ast::Node]) -> ?*RegionScope
1167
    throws (ResolveError)
1168
{
1169
    if let case NodeExtra::Regions(scope) = self.nodeData.entries[owner.id].extra {
1170
        return scope;
1171
    }
1172
    let mut count: u32 = 0;
1173
    for node in nodes {
1174
        if let case ast::NodeValue::Region { .. } = node.value {
1175
            set count += 1;
1176
        }
1177
    }
1178
    if count == 0 {
1179
        return nil;
1180
    }
1181
    let entries = try! alloc::allocRawSlice(
1182
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), count
1183
    ) as *unsafe mut [*unsafe mut types::Region];
1184
    let mut index: u32 = 0;
1185
    for node in nodes {
1186
        let case ast::NodeValue::Region { name, .. } = node.value else continue;
1187
        for i in 0..index {
1188
            if mem::eq(entries[i].name, name) {
1189
                throw emitError(self, node, ErrorKind::DuplicateBinding(name));
1190
            }
1191
        }
1192
        let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
1193
            as *unsafe mut types::Region;
1194
        set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil };
1195
        set entries[index] = region;
1196
        set index += 1;
1197
    }
1198
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
1199
    set *scope = RegionScope { declarations: RegionDeclarations::Parameters(nodes), entries, parent: nil };
1200
    let frozen: *RegionScope = scope;
1201
    set index = 0;
1202
    for node in nodes {
1203
        let case ast::NodeValue::Region { parent, .. } = node.value else continue;
1204
        if let parentNode = parent {
1205
            let case ast::NodeValue::Region { name, .. } = parentNode.value
1206
                else panic "bindRegions: invalid parent node";
1207
            let target = findRegion(frozen, name)
1208
                else throw emitError(self, parentNode, ErrorKind::UnknownRegion(name));
1209
            if types::regionContains(entries[index], target) {
1210
                throw emitError(self, parentNode, ErrorKind::RegionCycle(entries[index].name));
1211
            }
1212
            set entries[index].parent = target;
1213
        }
1214
        set index += 1;
1215
    }
1216
    set self.nodeData.entries[owner.id].extra = NodeExtra::Regions(frozen);
1217
    return frozen;
1218
}
1219
1220
/// Mix aligned metadata addresses into a lookup key.
1221
fn addressHash(address: u64) -> u32 {
1222
    return ((address >> 3) as u32) ^ ((address >> 35) as u32);
1223
}
1224
1225
/// Hash the active fields of a pointer class.
1226
unsafe fn classHash(class: types::PointerClass) -> u32 {
1227
    match class {
1228
        case types::PointerClass::Owned => return 1,
1229
        case types::PointerClass::Ref => return 2,
1230
        case types::PointerClass::Unsafe => return 3,
1231
        case types::PointerClass::Region(region) => return addressHash(region as u64),
1232
    }
1233
}
1234
1235
/// Hash active type fields so equal values select the same lookup chain.
1236
unsafe fn typeHash(ty: Type) -> u32 {
1237
    match ty {
1238
        case Type::Cell { class, payload } =>
1239
            return addressHash(payload as u64) ^ (classHash(class) * CACHE_HASH_PRIME),
1240
        case Type::Pointer { class, target, mutable } =>
1241
            return addressHash(target as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1242
        case Type::Slice { class, item, mutable } =>
1243
            return addressHash(item as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1244
        case Type::TraitObject { class, traitInfo, mutable } =>
1245
            return addressHash(traitInfo as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1246
        case Type::Session(region) => return addressHash(region as u64),
1247
        case Type::Optional(inner) => return addressHash(inner as u64),
1248
        case Type::Fn(info) => return addressHash(info as u64),
1249
        case Type::Nominal(info) => return addressHash(info as u64),
1250
        case Type::Array(array) => return addressHash(array.item as u64) ^ (array.length * CACHE_HASH_PRIME),
1251
        case Type::Range { start, end } => {
1252
            let mut hash: u32 = 0;
1253
            if let item = start {
1254
                set hash = addressHash(item as u64);
1255
            }
1256
            if let item = end {
1257
                set hash = hash ^ (addressHash(item as u64) * CACHE_HASH_PRIME);
1258
            }
1259
            return hash;
1260
        }
1261
        else => return 0,
1262
    }
1263
}
1264
1265
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
1266
export unsafe fn allocType 'arena (self: &mut Resolver 'arena, ty: Type) -> *Type {
1267
    // Search existing types for a match.
1268
    let bucket = typeHash(ty) & (TYPE_BUCKETS - 1);
1269
    let mut cursor = self.types[bucket];
1270
    while let node = cursor {
1271
        if node.ty == ty {
1272
            return &node.ty;
1273
        }
1274
        set cursor = node.next;
1275
    }
1276
    // Allocate a new type node from the arena.
1277
    let node = try! alloc::alloc(
1278
        &mut *self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
1279
    ) as *mut TypeNode;
1280
1281
    set *node = TypeNode { ty, next: self.types[bucket] };
1282
    let frozen: *TypeNode = node;
1283
    set self.types[bucket] = frozen;
1284
1285
    return &frozen.ty;
1286
}
1287
1288
/// Allocate a nominal type descriptor and return a pointer to it.
1289
unsafe fn allocNominalType 'arena (self: &mut Resolver 'arena, info: NominalType) -> *unsafe mut NominalType {
1290
    // Nb. We don't attempt to de-duplicate nominal type entries,
1291
    // since they don't carry node information and we create
1292
    // placeholder entries when binding symbols.
1293
    let entry = try! alloc::allocRaw(
1294
        self.arena, @sizeOf(NominalType), @alignOf(NominalType)
1295
    ) as *unsafe mut NominalType;
1296
1297
    set *entry = info;
1298
1299
    return entry;
1300
}
1301
1302
/// Allocate the single runtime layout for a nominal declaration.
1303
unsafe fn allocLayout 'arena (self: &mut Resolver 'arena, value: Layout) -> *Layout {
1304
    let layout = try! alloc::alloc(&mut *self.arena, @sizeOf(Layout), @alignOf(Layout)) as *mut Layout;
1305
    set *layout = value;
1306
    return layout;
1307
}
1308
1309
/// Get the exact arguments of an applied nominal descriptor.
1310
export fn nominalApplication(info: &NominalType) -> ?*unsafe NominalApplication {
1311
    match *info {
1312
        case NominalType::Application(applied) => return applied,
1313
        case NominalType::Record(body) => return body.application,
1314
        case NominalType::Union(body) => return body.application,
1315
        else => return nil,
1316
    }
1317
}
1318
1319
/// Get source region parameters without forcing a recursive type's layout.
1320
unsafe fn nominalParameters 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType) -> ?*RegionScope
1321
    throws (ResolveError)
1322
{
1323
    match *info {
1324
        case NominalType::Placeholder(node) => return try declarationRegions(self, node),
1325
        case NominalType::Resolving(node) => return try declarationRegions(self, node),
1326
        case NominalType::Application(applied) => return applied.parameters,
1327
        case NominalType::Record(body) => return body.regions,
1328
        case NominalType::Union(body) => return body.regions,
1329
    }
1330
}
1331
1332
/// Bind the regions declared by a nominal source node.
1333
unsafe fn declarationRegions 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*RegionScope
1334
    throws (ResolveError)
1335
{
1336
    match node.value {
1337
        case ast::NodeValue::RecordDecl(decl) => return try bindRegions(self, node, decl.regions),
1338
        case ast::NodeValue::UnionDecl(decl) => return try bindRegions(self, node, decl.regions),
1339
        else => panic "declarationRegions: expected nominal declaration",
1340
    }
1341
}
1342
1343
/// Use a hinted application only for the same unapplied nominal declaration.
1344
unsafe fn hintedNominal(info: *unsafe NominalType, hint: Type) -> *unsafe NominalType {
1345
    if nominalApplication(info) <> nil {
1346
        return info;
1347
    }
1348
    let mut target = hint;
1349
    if let case Type::Optional(inner) = target {
1350
        set target = *inner;
1351
    }
1352
    if let case Type::Nominal(other) = target {
1353
        if let applied = nominalApplication(other); applied.base == info {
1354
            return other;
1355
        }
1356
    }
1357
    return info;
1358
}
1359
1360
/// Require explicit arguments for a parameterized nominal type.
1361
unsafe fn requireNominalArguments 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType, site: *ast::Node)
1362
    throws (ResolveError)
1363
{
1364
    if nominalApplication(info) <> nil {
1365
        return;
1366
    }
1367
    if let parameters = try nominalParameters(self, info) {
1368
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
1369
            expected: parameters.entries.len, actual: 0,
1370
        }));
1371
    }
1372
}
1373
1374
/// Intern an exact nominal application before resolving its recursive members.
1375
unsafe fn internNominalApplication 'arena (
1376
    self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution
1377
) -> *unsafe mut NominalType {
1378
    let mut hash = addressHash(base as u64);
1379
    for argument in map.arguments {
1380
        let region = argument else panic "internNominalApplication: incomplete map";
1381
        set hash = (hash ^ region.id) * CACHE_HASH_PRIME;
1382
    }
1383
    let bucket = hash & (APPLICATION_BUCKETS - 1);
1384
    let mut cursor = self.applicationBuckets[bucket];
1385
    while let applied = cursor {
1386
        if applied.base == base {
1387
            let mut same = true;
1388
            for argument, i in applied.arguments {
1389
                let other = map.arguments[i] else panic "internNominalApplication: missing argument";
1390
                if argument.id <> other.id {
1391
                    set same = false;
1392
                    break;
1393
                }
1394
            }
1395
            if same {
1396
                return applied.view;
1397
            }
1398
        }
1399
        set cursor = applied.bucketNext;
1400
    }
1401
    let arguments = try! alloc::allocRawSlice(
1402
        self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len
1403
    ) as *unsafe mut [*unsafe types::Region];
1404
    for argument, i in map.arguments {
1405
        let region = argument else panic "internNominalApplication: incomplete map";
1406
        set arguments[i] = region;
1407
    }
1408
    let entry = try! alloc::allocRaw(
1409
        self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication)
1410
    ) as *unsafe mut NominalApplication;
1411
    let view = allocNominalType(self, NominalType::Application(entry));
1412
    set *entry = NominalApplication {
1413
        base, parameters: map.parameters, arguments, view,
1414
        next: self.applications, bucketNext: self.applicationBuckets[bucket],
1415
    };
1416
    set self.applications = entry;
1417
    set self.applicationBuckets[bucket] = entry;
1418
    return view;
1419
}
1420
1421
/// Check explicit region arguments and intern the applied nominal type.
1422
unsafe fn applyNominalRegions 'arena (
1423
    self: &mut Resolver 'arena, base: *unsafe NominalType, regions: *[*ast::Node], site: *ast::Node
1424
) -> *unsafe mut NominalType throws (ResolveError) {
1425
    if nominalApplication(base) <> nil {
1426
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: 0, actual: regions.len }));
1427
    }
1428
    let parameters = try nominalParameters(self, base);
1429
    let mut count: u32 = 0;
1430
    if let scope = parameters {
1431
        set count = scope.entries.len;
1432
    }
1433
    if count <> regions.len {
1434
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
1435
    }
1436
    let scope = parameters else panic "applyNominalRegions: empty application";
1437
    let map = regionSubstitution(self, scope);
1438
    for region, i in regions {
1439
        set map.arguments[i] = try resolveRegion(self, region);
1440
    }
1441
    try validateRegionArguments(self, &map, site);
1442
    return internNominalApplication(self, base, &map);
1443
}
1444
1445
/// Complete nominal views stored inline within an applied type.
1446
/// Pointer, slice, and cell targets have independent storage layouts.
1447
unsafe fn resolveInlineTypeViews 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node)
1448
    throws (ResolveError)
1449
{
1450
    match ty {
1451
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1452
        case Type::Array(array) => try resolveInlineTypeViews(self, *array.item, site),
1453
        case Type::Optional(inner) => try resolveInlineTypeViews(self, *inner, site),
1454
        else => {
1455
        },
1456
    }
1457
}
1458
1459
/// Resolve a substituted member view with the source declaration's shared layout.
1460
unsafe fn resolveNominalApplication 'arena (self: &mut Resolver 'arena, applied: *unsafe NominalApplication, site: *ast::Node)
1461
    throws (ResolveError)
1462
{
1463
    try ensureNominalResolved(self, applied.base, site);
1464
    let map = regionSubstitution(self, applied.parameters);
1465
    for argument, i in applied.arguments {
1466
        set map.arguments[i] = argument;
1467
    }
1468
    let allocator = alloc::arenaAllocator(self.arena);
1469
    match *applied.base {
1470
        case NominalType::Record(body) => {
1471
            let mut fields: *mut [RecordField] = &mut [];
1472
            for field in body.fields {
1473
                let fieldType = substituteRegions(self, &map, field.fieldType);
1474
                try resolveInlineTypeViews(self, fieldType, site);
1475
                fields.append(RecordField {
1476
                    name: field.name,
1477
                    fieldType,
1478
                    offset: field.offset,
1479
                }, allocator);
1480
            }
1481
            set *applied.view = NominalType::Record(RecordType {
1482
                regions: body.regions,
1483
                application: applied,
1484
                fields: (&fields[..]) as *unsafe [RecordField],
1485
                labeled: body.labeled,
1486
                layout: body.layout,
1487
                declaredLinear: body.declaredLinear,
1488
                declaredCopy: body.declaredCopy,
1489
            });
1490
        }
1491
        case NominalType::Union(body) => {
1492
            let mut variants: *mut [UnionVariant] = &mut [];
1493
            for variant in body.variants {
1494
                let valueType = substituteRegions(self, &map, variant.valueType);
1495
                try resolveInlineTypeViews(self, valueType, site);
1496
                variants.append(UnionVariant {
1497
                    name: variant.name,
1498
                    valueType,
1499
                    symbol: variant.symbol,
1500
                }, allocator);
1501
            }
1502
            set *applied.view = NominalType::Union(UnionType {
1503
                regions: body.regions,
1504
                application: applied,
1505
                variants: (&variants[..]) as *unsafe [UnionVariant],
1506
                layout: body.layout,
1507
                valOffset: body.valOffset,
1508
                isAllVoid: body.isAllVoid,
1509
                declaredLinear: body.declaredLinear,
1510
                declaredCopy: body.declaredCopy,
1511
            });
1512
        }
1513
        else => panic "resolveNominalApplication: unresolved base",
1514
    }
1515
}
1516
1517
/// Complete all applied member views before semantic metadata reaches lowering.
1518
unsafe fn resolveNominalApplications 'arena (self: &mut Resolver 'arena, site: *ast::Node) throws (ResolveError) {
1519
    let mut end = self.completedApplications;
1520
    loop {
1521
        let first = self.applications;
1522
        let mut cursor = first;
1523
        while cursor <> end {
1524
            let applied = cursor else panic "resolveNominalApplications: invalid frontier";
1525
            try ensureNominalResolved(self, applied.view, site);
1526
            set cursor = applied.next;
1527
        }
1528
        if self.applications == first {
1529
            set self.completedApplications = first;
1530
            return;
1531
        }
1532
        set end = first;
1533
    }
1534
}
1535
1536
1537
/// Allocate a function type descriptor and return a pointer to it.
1538
unsafe fn allocFnType 'arena (self: &mut Resolver 'arena, info: FnType) -> *FnType {
1539
    let entry = try! alloc::alloc(
1540
        &mut *self.arena, @sizeOf(FnType), @alignOf(FnType)
1541
    ) as *mut FnType;
1542
1543
    set *entry = info;
1544
1545
    return entry;
1546
}
1547
1548
/// Returns an error, if any, associated with the given node.
1549
fn errorForNode 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Error {
1550
    for i in 0..self.errors.len {
1551
        let err = self.errors.entries[i];
1552
        if err.node == node {
1553
            return err;
1554
        }
1555
    }
1556
    return nil;
1557
}
1558
1559
/// Storage buffers used by the analyzer.
1560
export record ResolverStorage {
1561
    /// Node semantic metadata indexed by node ID.
1562
    nodeData: *mut [NodeData],
1563
    /// Package scope.
1564
    pkgScope: *unsafe mut Scope,
1565
    /// Error storage.
1566
    errors: *mut [Error],
1567
}
1568
1569
/// Input for resolving a single package.
1570
export record Pkg: Copy {
1571
    /// Root module entry.
1572
    rootEntry: *module::ModuleEntry,
1573
    /// Root AST node.
1574
    rootAst: *ast::Node,
1575
}
1576
1577
/// Construct a resolver with module context and backing storage.
1578
/// The arena owner and backing bytes must retain stable addresses during use.
1579
/// Arena reclamation can occur only after all metadata uses end.
1580
export unsafe fn resolver 'arena (
1581
    arena: &'arena mut alloc::Arena,
1582
    storage: ResolverStorage,
1583
    config: Config
1584
) -> Resolver 'arena {
1585
    let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
1586
    let applicationBuckets = try! alloc::allocRawSlice(
1587
        arena, @sizeOf(?*unsafe NominalApplication), @alignOf(?*unsafe NominalApplication), APPLICATION_BUCKETS
1588
    ) as *unsafe mut [?*unsafe NominalApplication];
1589
    for i in 0..applicationBuckets.len {
1590
        set applicationBuckets[i] = nil;
1591
    }
1592
    let types = try! alloc::allocRawSlice(
1593
        arena, @sizeOf(?*TypeNode), @alignOf(?*TypeNode), TYPE_BUCKETS
1594
    ) as *unsafe mut [?*TypeNode];
1595
    for i in 0..types.len {
1596
        set types[i] = nil;
1597
    }
1598
    let symbols = try! alloc::allocRawSlice(
1599
        arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS
1600
    ) as *unsafe mut [*unsafe mut Symbol];
1601
1602
    // Initialize the root scope.
1603
    // TODO: Set this up when declaring `PKG_SCOPE`, not here.
1604
    set *pkgScope = Scope {
1605
        owner: nil,
1606
        parent: nil,
1607
        moduleId: nil,
1608
        symbols,
1609
        symbolsLen: 0,
1610
    };
1611
1612
    // Clear all node semantic metadata to sentinel values.
1613
    // TODO: Use array repeat literal?
1614
    for i in 0..nodeData.len {
1615
        set nodeData[i] = NodeData {
1616
            localCount: 0,
1617
            ty: Type::Unknown,
1618
            coercion: Coercion::Identity,
1619
            binding: nil,
1620
            constValue: nil,
1621
            scope: nil,
1622
            extra: NodeExtra::None,
1623
        };
1624
    }
1625
1626
    let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined;
1627
    // TODO: Simplify.
1628
    for i in 0..moduleScopes.len {
1629
        set moduleScopes[i] = nil;
1630
    }
1631
    return Resolver 'arena {
1632
        symbolCount: 0,
1633
        regionScope: nil,
1634
        applications: nil,
1635
        completedApplications: nil,
1636
        applicationBuckets,
1637
        scope: pkgScope,
1638
        pkgScope: pkgScope,
1639
        loopStack: [LoopCtx { hasBreak: false }; MAX_LOOP_DEPTH],
1640
        loopDepth: 0,
1641
        currentFn: nil,
1642
        currentFnNode: nil,
1643
        currentMod: 0,
1644
        inUnsafeContext: false,
1645
        config,
1646
        arena,
1647
        nodeData: NodeDataTable { entries: nodeData },
1648
        types,
1649
        errors: DiagnosticBuffer { entries: errors, len: 0 },
1650
        moduleEntries: [nil; module::MAX_MODULES],
1651
        moduleScopes,
1652
        instances: undefined,
1653
        instancesLen: 0,
1654
        methods: undefined,
1655
        methodsLen: 0,
1656
    };
1657
}
1658
1659
/// Capture the current errors in an immutable arena allocation.
1660
/// The allocation must remain valid while the diagnostics are used.
1661
export unsafe fn diagnostics 'arena (self: &mut Resolver 'arena) -> Diagnostics {
1662
    let count = self.errors.len;
1663
    let entries = try! alloc::allocSlice(
1664
        self.arena, @sizeOf(Error), @alignOf(Error), count
1665
    ) as *mut [Error];
1666
    for i in 0..self.errors.len {
1667
        set entries[i] = self.errors.entries[i];
1668
    }
1669
    return Diagnostics { errors: entries };
1670
}
1671
1672
/// Return `true` if there are no errors in the diagnostics.
1673
export fn success(diag: &Diagnostics) -> bool {
1674
    return diag.errors.len == 0;
1675
}
1676
1677
/// Retrieve an error diagnostic by index, if present.
1678
export fn errorAt(errs: &[Error], index: u32) -> ?Error {
1679
    if index >= errs.len {
1680
        return nil;
1681
    }
1682
    return errs[index];
1683
}
1684
1685
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1686
fn emitError 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1687
    // If our error list is full, just return an error without recording it.
1688
    if self.errors.len >= self.errors.entries.len {
1689
        return ResolveError::Failure;
1690
    }
1691
    // Don't record more than one error per node.
1692
    if let n = node; errorForNode(self, n) <> nil {
1693
        return ResolveError::Failure;
1694
    }
1695
    let idx = self.errors.len;
1696
    set self.errors.entries[idx] = Error { kind, node, moduleId: self.currentMod };
1697
    set self.errors.len = idx + 1;
1698
1699
    return ResolveError::Failure;
1700
}
1701
1702
/// Like [`emitError`], but for type mismatches specifically.
1703
fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1704
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1705
}
1706
1707
/// Allocate a scope object with the given symbol capacity.
1708
unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1709
    // Check for an existing scope for this node, and don't allocate a new
1710
    // one in that case.
1711
    if let scope = scopeFor(self, owner) {
1712
        return scope;
1713
    }
1714
    assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds";
1715
    let p = try! alloc::allocRaw(self.arena, @sizeOf(Scope), @alignOf(Scope));
1716
    let entry = p as *unsafe mut Scope;
1717
1718
    // Allocate symbols from the arena.
1719
    let symbols = try! alloc::allocRawSlice(
1720
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity
1721
    ) as *unsafe mut [*unsafe mut Symbol];
1722
1723
    set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 };
1724
    set self.nodeData.entries[owner.id].scope = entry;
1725
1726
    return entry;
1727
}
1728
1729
/// Enter a new local scope that is the child of the current scope.
1730
/// This creates a parent/child relationship that means that lookups in the
1731
/// child scope can recurse upwards.
1732
export unsafe fn enterScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node) -> *unsafe Scope {
1733
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1734
    set scope.parent = self.scope;
1735
    set self.scope = scope;
1736
    return scope;
1737
}
1738
1739
/// Enter a module scope. Returns an object that can be used to exit the scope.
1740
export unsafe fn enterModuleScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1741
    let prevScope = self.scope;
1742
    let prevMod = self.currentMod;
1743
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1744
1745
    set self.scope = scope;
1746
    set self.scope.moduleId = module.id;
1747
    set self.currentMod = module.id;
1748
    // TODO: Allow any unsigned integer to index an array.
1749
    set self.moduleScopes[module.id as u32] = scope;
1750
1751
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1752
}
1753
1754
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1755
unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1756
    let modEntry = findChildModule(self, name, self.currentMod)
1757
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1758
    let modRoot = module::astFor(modEntry)
1759
        else panic "enterSubModule: analyzing module that wasn't parsed";
1760
1761
    return enterModuleScope(self, modRoot, modEntry);
1762
}
1763
1764
/// Exit a module scope, given the object returned by `enterModuleScope`.
1765
export fn exitModuleScope 'arena (self: &mut Resolver 'arena, entry: ModuleScope) {
1766
    set self.scope = entry.prevScope;
1767
    set self.currentMod = entry.prevMod;
1768
}
1769
1770
/// Exit the most recent scope.
1771
export unsafe fn exitScope 'arena (self: &mut Resolver 'arena) {
1772
    let parent = self.scope.parent else {
1773
        // TODO: This should be a panic, but one of the tests hits this
1774
        // clause, which might be a bug in the generator.
1775
        return;
1776
    };
1777
    set self.scope = parent;
1778
}
1779
1780
/// Initialize a loop context before making it active.
1781
fn enterLoop 'arena (self: &mut Resolver 'arena) {
1782
    assert self.loopDepth < MAX_LOOP_DEPTH, "enterLoop: loop nesting depth exceeded";
1783
    set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false };
1784
    set self.loopDepth += 1;
1785
}
1786
1787
/// End the active loop context and return its control-flow type.
1788
fn exitLoop 'arena (self: &mut Resolver 'arena) -> Type {
1789
    assert self.loopDepth > 0, "exitLoop: loop depth underflow";
1790
    // Pop and check if break was encountered.
1791
    set self.loopDepth -= 1;
1792
    if self.loopStack[self.loopDepth].hasBreak {
1793
        return Type::Void;
1794
    }
1795
    return Type::Never;
1796
}
1797
1798
/// Visit the body of a loop while tracking nesting depth.
1799
unsafe fn visitLoop 'arena (self: &mut Resolver 'arena, body: *ast::Node) -> Type
1800
    throws (ResolveError)
1801
{
1802
    enterLoop(self);
1803
    try infer(self, body) catch {
1804
        exitLoop(self);
1805
        throw ResolveError::Failure;
1806
    };
1807
    return exitLoop(self);
1808
}
1809
1810
/// Require that loop control statements appear inside a loop.
1811
/// Record breaks and assign the control statement's diverging type.
1812
fn resolveLoopControl 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
1813
    if self.loopDepth == 0 {
1814
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1815
    }
1816
    match node.value {
1817
        case ast::NodeValue::Break => {
1818
            // Mark that the current loop has a reachable break.
1819
            set self.loopStack[self.loopDepth - 1].hasBreak = true;
1820
        }
1821
        case ast::NodeValue::Continue => {}
1822
        else => panic "resolveLoopControl: expected loop control statement",
1823
    }
1824
    return setNodeType(self, node, Type::Never);
1825
}
1826
1827
/// Bind a loop pattern to the provided type.
1828
unsafe fn bindForLoopPattern 'arena (self: &mut Resolver 'arena, pattern: *ast::Node, ty: Type, mutable: bool)
1829
    throws (ResolveError)
1830
{
1831
    match pattern.value {
1832
        case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => {
1833
            let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0);
1834
        }
1835
        else => {
1836
            let actualTy = try checkAssignable(self, pattern, ty);
1837
            setNodeType(self, pattern, actualTy);
1838
        }
1839
    }
1840
}
1841
1842
/// Set the expected return type for a new function body.
1843
unsafe fn enterFn 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: &FnType) {
1844
    assert self.currentFn == nil, "enterFn: already in a function";
1845
    set self.currentFn = *ty;
1846
    set self.currentFnNode = node;
1847
    enterScope(self, node);
1848
}
1849
1850
/// Clear the expected return type when leaving a function body.
1851
unsafe fn exitFn 'arena (self: &mut Resolver 'arena) {
1852
    if self.currentFn == nil {
1853
        // TODO: This should be a panic, but one of the tests hits this
1854
        // clause, which might be a bug in the generator.
1855
        return;
1856
    }
1857
    set self.currentFn = nil;
1858
    set self.currentFnNode = nil;
1859
    exitScope(self);
1860
}
1861
1862
/// Extract the identifier text from a node.
1863
fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8]
1864
    throws (ResolveError)
1865
{
1866
    let case ast::NodeValue::Ident(name) = node.value
1867
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1868
    return name;
1869
}
1870
1871
/// Associate a resolved symbol with an AST node.
1872
unsafe fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) {
1873
    if let existingSym = self.nodeData.entries[node.id].binding {
1874
        panic "setNodeSymbol: a symbol is already associated with this node";
1875
    }
1876
    set self.nodeData.entries[node.id].binding = ResolvedSymbol { id: symbol.id, symbol };
1877
}
1878
1879
/// Associate a resolved type with an AST node and return it.
1880
fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type {
1881
    if ty == Type::Unknown {
1882
        // In this case, we simply don't associate a type.
1883
        return ty;
1884
    }
1885
    set self.nodeData.entries[node.id].ty = ty;
1886
1887
    return ty;
1888
}
1889
1890
/// Unify the types of two branches for control flow. Returns `never` only if
1891
/// both branches diverge, otherwise returns `void`. If the else branch is
1892
/// absent, we assume it doesn't diverge.
1893
fn unifyBranches(left: Type, right: ?Type) -> Type {
1894
    if left == Type::Never {
1895
        if let ty = right; ty == Type::Never {
1896
            return Type::Never;
1897
        }
1898
    }
1899
    return Type::Void;
1900
}
1901
1902
/// Associate a coercion plan with an AST node.
1903
fn setNodeCoercion 'arena (self: &mut Resolver 'arena, node: *ast::Node, coercion: Coercion) -> Coercion {
1904
    if coercion == Coercion::Identity {
1905
        return coercion;
1906
    }
1907
    set self.nodeData.entries[node.id].coercion = coercion;
1908
1909
    return coercion;
1910
}
1911
1912
/// Associate a constant value with an AST node.
1913
fn setNodeConstValue 'arena (self: &mut Resolver 'arena, node: *ast::Node, value: ConstValue) {
1914
    set self.nodeData.entries[node.id].constValue = value;
1915
}
1916
1917
/// Associate a record field index with a record literal field node.
1918
fn setRecordFieldIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node, index: u32) {
1919
    set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index };
1920
}
1921
1922
/// Associate slice range metadata with a subscript expression.
1923
fn setSliceRangeInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: SliceRangeInfo) {
1924
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
1925
}
1926
1927
/// Associate union variant metadata with a pattern or constructor node.
1928
fn setVariantInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, ordinal: u32, tag: u32) {
1929
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
1930
}
1931
1932
/// Associate trait method call metadata with a call node.
1933
fn setTraitMethodCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) {
1934
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
1935
}
1936
1937
/// Associate for-loop metadata with a for-loop node.
1938
fn setForLoopInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: ForLoopInfo) {
1939
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
1940
}
1941
1942
/// Retrieve the constant value associated with a node, if any.
1943
export fn constValueEntry 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ConstValue {
1944
    return self.nodeData.entries[node.id].constValue;
1945
}
1946
1947
/// Get the resolved record field index for a record literal field node.
1948
export fn recordFieldIndexFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?u32 {
1949
    if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra {
1950
        return index;
1951
    }
1952
    return nil;
1953
}
1954
1955
/// Get the range metadata for a slice borrow or range assignment.
1956
export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo {
1957
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
1958
        return info;
1959
    }
1960
    return nil;
1961
}
1962
1963
/// Get the for-loop metadata for a for-loop node.
1964
export fn forLoopInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ForLoopInfo {
1965
    if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra {
1966
        return info;
1967
    }
1968
    return nil;
1969
}
1970
1971
/// Associate match prong metadata with a match prong node.
1972
fn setProngCatchAll 'arena (self: &mut Resolver 'arena, node: *ast::Node, catchAll: bool) {
1973
    set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll };
1974
}
1975
1976
/// Check if a prong is catch-all.
1977
export fn isProngCatchAll 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
1978
    if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra {
1979
        return catchAll;
1980
    }
1981
    return false;
1982
}
1983
1984
/// Set match metadata.
1985
fn setMatchConst 'arena (self: &mut Resolver 'arena, node: *ast::Node, isConst: bool) {
1986
    set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst };
1987
}
1988
1989
/// Check if a match has all constant patterns.
1990
export fn isMatchConst 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
1991
    if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra {
1992
        return isConst;
1993
    }
1994
    return false;
1995
}
1996
1997
/// Get the resolver metadata for a node.
1998
export fn nodeData 'arena (self: &Resolver 'arena, node: *ast::Node) -> NodeData {
1999
    return self.nodeData.entries[node.id];
2000
}
2001
2002
/// Get the type for a node, or `nil` if unknown.
2003
export fn typeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Type {
2004
    let ty = self.nodeData.entries[node.id].ty;
2005
    if ty == Type::Unknown {
2006
        return nil;
2007
    }
2008
    return ty;
2009
}
2010
2011
/// Get the scope associated with a node.
2012
export fn scopeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope {
2013
    return self.nodeData.entries[node.id].scope;
2014
}
2015
2016
/// Get the symbol bound to a node.
2017
export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
2018
    let binding = self.nodeData.entries[node.id].binding else return nil;
2019
    return binding.symbol;
2020
}
2021
2022
/// Get the coercion plan associated with a node, if any.
2023
export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion {
2024
    let c = self.nodeData.entries[node.id].coercion;
2025
    if c == Coercion::Identity {
2026
        return nil;
2027
    }
2028
    return c;
2029
}
2030
2031
/// Get the module ID for a symbol by walking up its scope chain.
2032
export unsafe fn moduleIdForSymbol 'arena (self: &Resolver 'arena, sym: *unsafe Symbol) -> ?u16 {
2033
    // For module-level symbols, return the cached module ID.
2034
    if let id = sym.moduleId {
2035
        return id;
2036
    }
2037
    // For module symbols, return the module ID directly.
2038
    if let case SymbolData::Module { entry, .. } = sym.data {
2039
        return entry.id;
2040
    }
2041
    // If this node has its own scope (functions, types, etc.), walk up from there.
2042
    if let scope = self.nodeData.entries[sym.node.id].scope {
2043
        return findModuleForScope(scope);
2044
    }
2045
    return nil;
2046
}
2047
2048
/// Get the binding node for a variant pattern.
2049
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
2050
export unsafe fn variantPatternBinding 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> ?*ast::Node {
2051
    let case ast::NodeValue::Call(call) = pattern.value
2052
        else return nil;
2053
    let sym = symbolFor(self, call.callee)
2054
        else return nil;
2055
    let case SymbolData::Variant { .. } = sym.data
2056
        else return nil;
2057
2058
    if call.args.len == 0 {
2059
        return nil;
2060
    }
2061
    let arg = call.args[0];
2062
2063
    if let case ast::NodeValue::Placeholder = arg.value {
2064
        return nil;
2065
    }
2066
    return arg;
2067
}
2068
2069
/// Allocate a new symbol, and return a reference to it.
2070
unsafe fn allocSymbol 'arena (self: &mut Resolver 'arena, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol {
2071
    let sym = try! alloc::allocRaw(self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol;
2072
    assert self.symbolCount < parser::U32_MAX, "allocSymbol: symbol identity overflow";
2073
    let id = self.symbolCount;
2074
    set self.symbolCount += 1;
2075
    set *sym = Symbol { id, name, data, attrs, node, moduleId: nil };
2076
2077
    return sym;
2078
}
2079
2080
/// Check that a type is boolean, otherwise throw an error.
2081
unsafe fn checkBoolean 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2082
    return try checkEqual(self, node, Type::Bool);
2083
}
2084
2085
/// Check that a type is numeric, otherwise throw an error.
2086
unsafe fn checkNumeric 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2087
    let ty = try infer(self, node);
2088
    if not isNumericType(ty) {
2089
        throw emitError(self, node, ErrorKind::ExpectedNumeric);
2090
    }
2091
    return ty;
2092
}
2093
2094
/// Check if a type is a numeric type.
2095
fn isNumericType(ty: Type) -> bool {
2096
    match ty {
2097
        case Type::U8, Type::U16, Type::U32, Type::U64,
2098
             Type::I8, Type::I16, Type::I32, Type::I64,
2099
             Type::Int => return true,
2100
        else => return false,
2101
    }
2102
}
2103
2104
/// Check if a type is an unsigned integer type.
2105
export fn isUnsignedIntegerType(ty: Type) -> bool {
2106
    match ty {
2107
        case Type::U8, Type::U16, Type::U32, Type::U64 => return true,
2108
        else => return false,
2109
    }
2110
}
2111
2112
/// Return the maximum of two u32 values.
2113
fn max(a: u32, b: u32) -> u32 {
2114
    if a > b {
2115
        return a;
2116
    }
2117
    return b;
2118
}
2119
2120
/// Get the layout of a type.
2121
export unsafe fn getTypeLayout(ty: Type) -> Layout {
2122
    return typeLayout(ty);
2123
}
2124
2125
/// Traverse owned type links and compute array, optional, or fixed layouts.
2126
fn typeLayout(ty: Type) -> Layout {
2127
    match ty {
2128
        case Type::Array(arr) => return getArrayLayout(typeLayout(*arr.item), arr.length),
2129
        case Type::Optional(inner) => {
2130
            // Nullable types use null pointer optimization -- no tag byte needed.
2131
            if isNullableType(*inner) {
2132
                return typeLayout(*inner);
2133
            }
2134
            return getOptionalAggregateLayout(typeLayout(*inner));
2135
        }
2136
        case Type::Nominal(info) => {
2137
            unsafe {
2138
                return getNominalLayout(*info);
2139
            }
2140
        },
2141
        else => return fixedTypeLayout(ty),
2142
    }
2143
}
2144
2145
/// Get a layout that does not depend on nested type or nominal metadata.
2146
fn fixedTypeLayout(ty: Type) -> Layout {
2147
    match ty {
2148
        case Type::Pointer { .. } => return Layout {
2149
            size: PTR_SIZE, alignment: PTR_SIZE
2150
        },
2151
        case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) =>
2152
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
2153
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
2154
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
2155
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
2156
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
2157
        case Type::Int => return Layout { size: 8, alignment: 8 },
2158
        case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 },
2159
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
2160
        case Type::Cell { .. } => return Layout {
2161
            size: PTR_SIZE, alignment: PTR_SIZE
2162
        },
2163
        else => {
2164
            panic "fixedTypeLayout: the given type has no fixed layout";
2165
        }
2166
    }
2167
}
2168
2169
/// Get the layout of a type or value.
2170
export unsafe fn getLayout 'arena (self: &Resolver 'arena, node: *ast::Node, ty: Type) -> Layout {
2171
    let mut layout = getTypeLayout(ty);
2172
    // Check for symbol-specific alignment override.
2173
    if let sym = symbolFor(self, node) {
2174
        if let case SymbolData::Value { alignment, .. } = sym.data {
2175
            if alignment > 0 {
2176
                set layout.alignment = alignment;
2177
            }
2178
        }
2179
    }
2180
    return layout;
2181
}
2182
2183
/// Get an array layout from its element layout and length.
2184
export fn getArrayLayout(item: Layout, length: u32) -> Layout {
2185
    return Layout {
2186
        size: item.size * length,
2187
        alignment: item.alignment,
2188
    };
2189
}
2190
2191
/// Get an optional aggregate layout from its payload layout.
2192
export fn getOptionalAggregateLayout(innerLayout: Layout) -> Layout {
2193
    let valOffset = getOptionalValOffset(innerLayout);
2194
    let alignment = max(innerLayout.alignment, 1);
2195
2196
    return Layout {
2197
        size: mem::alignUp(valOffset + innerLayout.size, alignment),
2198
        alignment,
2199
    };
2200
}
2201
2202
/// Get the payload offset within an optional aggregate.
2203
export fn getOptionalValOffset(inner: Layout) -> u32 {
2204
    return mem::alignUp(1, inner.alignment);
2205
}
2206
2207
/// Check if a type is optional.
2208
export fn isOptionalType(ty: Type) -> bool {
2209
    match ty {
2210
        case Type::Optional(_) => return true,
2211
        else => return false,
2212
    }
2213
}
2214
2215
/// Check if a type uses null pointer optimization.
2216
/// This applies to optional pointers `?*T` and optional slices `?*[T]`,
2217
/// where `nil` is represented as a null data pointer with no tag byte.
2218
export fn isOptionalPointer(ty: Type) -> bool {
2219
    if let case Type::Optional(inner) = ty {
2220
        return isNullableType(*inner);
2221
    }
2222
    return false;
2223
}
2224
2225
/// Check if a type uses the optional aggregate representation.
2226
export fn isOptionalAggregate(ty: Type) -> bool {
2227
    if let case Type::Optional(inner) = ty {
2228
        return not isNullableType(*inner);
2229
    }
2230
    return false;
2231
}
2232
2233
/// Check if a type can use null to represent `nil`.
2234
/// Pointers and slices have a data pointer that is never null when valid.
2235
export fn isNullableType(ty: Type) -> bool {
2236
    match ty {
2237
        case Type::Pointer { .. }, Type::Slice { .. } => return true,
2238
        else => return false,
2239
    }
2240
}
2241
2242
/// Get the layout of a nominal type.
2243
export fn getNominalLayout(info: NominalType) -> Layout {
2244
    match info {
2245
        case NominalType::Placeholder(_), NominalType::Resolving(_), NominalType::Application(_) => {
2246
            panic "getNominalLayout: unresolved type";
2247
        }
2248
        case NominalType::Record(recordType) => {
2249
            return *recordType.layout;
2250
        }
2251
        case NominalType::Union(unionType) => {
2252
            return *unionType.layout;
2253
        }
2254
    }
2255
}
2256
2257
/// Get the layout of a result aggregate with a tag and the larger payload.
2258
export unsafe fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout {
2259
    return resultLayout(payload, throwList);
2260
}
2261
2262
/// Compute tagged result storage while borrowing its error type table.
2263
fn resultLayout(payload: Type, throwList: &[*Type]) -> Layout {
2264
    let payloadLayout = typeLayout(payload);
2265
    let mut maxSize = payloadLayout.size;
2266
    let mut maxAlign = payloadLayout.alignment;
2267
2268
    for errType in throwList {
2269
        let errLayout = typeLayout(*errType);
2270
        set maxSize = max(maxSize, errLayout.size);
2271
        set maxAlign = max(maxAlign, errLayout.alignment);
2272
    }
2273
    return Layout {
2274
        size: PTR_SIZE + maxSize,
2275
        alignment: max(PTR_SIZE, maxAlign),
2276
    };
2277
}
2278
2279
/// Compute the layout for a union given its resolved variants.
2280
fn computeUnionLayout(variants: &[UnionVariant]) -> UnionLayoutInfo {
2281
    let tagSize: u32 = 1;
2282
    let mut maxVarSize: u32 = 0;
2283
    let mut maxVarAlign: u32 = 1;
2284
    let mut isAllVoid: bool = true;
2285
2286
    for variant in variants {
2287
        if variant.valueType <> Type::Void {
2288
            set isAllVoid = false;
2289
            let payloadLayout = typeLayout(variant.valueType);
2290
            set maxVarSize = max(maxVarSize, payloadLayout.size);
2291
            set maxVarAlign = max(maxVarAlign, payloadLayout.alignment);
2292
        }
2293
    }
2294
    let unionAlignment: u32 = max(1, maxVarAlign);
2295
    let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign);
2296
    let unionLayout = Layout {
2297
        size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment),
2298
        alignment: unionAlignment,
2299
    };
2300
    return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid };
2301
}
2302
2303
/// Compute the discriminant tag for a variant, advancing the iota counter.
2304
/// If the variant has an explicit `= N` value, uses that; otherwise uses iota.
2305
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: &mut u32) -> u32 {
2306
    let mut tag: u32 = *iota;
2307
    if let valueNode = variantDecl.value {
2308
        let case ast::NodeValue::Number(lit) = valueNode.value
2309
            else panic "variantTag: expected number literal";
2310
        set tag = lit.magnitude as u32;
2311
    }
2312
    set *iota = tag + 1;
2313
    return tag;
2314
}
2315
2316
/// Check if a type is a union without payloads.
2317
export unsafe fn isVoidUnion(ty: Type) -> bool {
2318
    let case Type::Nominal(NominalType::Union(unionType)) = ty
2319
        else return false;
2320
    return unionType.isAllVoid;
2321
}
2322
2323
/// Check if a type should be treated as an address-like value.
2324
fn isAddressType(ty: Type) -> bool {
2325
    if isNullableType(ty) {
2326
        return true;
2327
    }
2328
    match ty {
2329
        case Type::Fn(_) => return true,
2330
        else => return false,
2331
    }
2332
}
2333
2334
/// Return the representable range for an integer type.
2335
fn integerRange(ty: Type) -> ?IntegerRange {
2336
    match ty {
2337
        case Type::I8 => return IntegerRange::Signed {
2338
            bits: 8,
2339
            min: I8_MIN as i64,
2340
            max: I8_MAX as i64,
2341
            lim: (I8_MAX as u64) + 1,
2342
        },
2343
        case Type::I16 => return IntegerRange::Signed {
2344
            bits: 16,
2345
            min: I16_MIN as i64,
2346
            max: I16_MAX as i64,
2347
            lim: (I16_MAX as u64) + 1,
2348
        },
2349
        case Type::I32 => return IntegerRange::Signed {
2350
            bits: 32,
2351
            min: I32_MIN as i64,
2352
            max: I32_MAX as i64,
2353
            lim: (I32_MAX as u64) + 1,
2354
        },
2355
        case Type::I64, Type::Int => return IntegerRange::Signed {
2356
            bits: 64,
2357
            min: I64_MIN,
2358
            max: I64_MAX,
2359
            lim: (I64_MAX as u64) + 1,
2360
        },
2361
        case Type::U8 => return IntegerRange::Unsigned { bits: 8, max: U8_MAX as u64 },
2362
        case Type::U16 => return IntegerRange::Unsigned { bits: 16, max: U16_MAX as u64 },
2363
        case Type::U32 => return IntegerRange::Unsigned { bits: 32, max: parser::U32_MAX as u64 },
2364
        case Type::U64 => return IntegerRange::Unsigned { bits: 64, max: parser::U64_MAX },
2365
        else => return nil,
2366
    }
2367
}
2368
2369
/// Validate that an integer constant fits within the target type's range.
2370
fn validateConstIntRange(value: ConstValue, target: Type) -> bool {
2371
    let range = integerRange(target)
2372
        else panic "validateConstIntRange: expected integer type";
2373
    let case ConstValue::Int(int) = value
2374
        else panic "validateConstIntRange: expected integer constant";
2375
2376
    match range {
2377
        case IntegerRange::Signed { lim, .. } => {
2378
            if int.negative {
2379
                if int.magnitude > lim {
2380
                    return false;
2381
                }
2382
                return true;
2383
            }
2384
            if int.magnitude > lim - 1 {
2385
                return false;
2386
            }
2387
            return true;
2388
        }
2389
        case IntegerRange::Unsigned { max, .. } => {
2390
            if int.negative or int.magnitude > max {
2391
                return false;
2392
            }
2393
            return true;
2394
        }
2395
    }
2396
}
2397
2398
/// Ensure all nested nominal types in a type are resolved.
2399
unsafe fn ensureTypeResolved 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) throws (ResolveError) {
2400
    match ty {
2401
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
2402
        // Pointer and slice layouts do not depend on their element layout.
2403
        case Type::Pointer { .. }, Type::Slice { .. } => {
2404
        },
2405
        case Type::Cell { payload, .. } => try validateCellPayload(self, site, *payload),
2406
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
2407
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
2408
        else => {},
2409
    }
2410
}
2411
2412
/// Ensure a nominal type has its body resolved.
2413
unsafe fn ensureNominalResolved 'arena (self: &mut Resolver 'arena, tyInfo: *unsafe NominalType, site: *ast::Node)
2414
    throws (ResolveError)
2415
{
2416
    if let case NominalType::Application(applied) = *tyInfo {
2417
        try resolveNominalApplication(self, applied, site);
2418
        return;
2419
    }
2420
    if let case NominalType::Resolving(_) = *tyInfo {
2421
        throw emitError(self, site, ErrorKind::RecursiveType);
2422
    }
2423
    if let case NominalType::Placeholder(declNode) = *tyInfo {
2424
        // When resolving on-demand (e.g. from a child module), switch to the
2425
        // declaring module's scope so field type lookups find the right symbols.
2426
        let prevScope = self.scope;
2427
        let prevMod = self.currentMod;
2428
2429
        if let sym = symbolFor(self, declNode) {
2430
            if let mid = sym.moduleId {
2431
                if (mid as u32) < self.moduleScopes.len {
2432
                    if let ms = self.moduleScopes[mid as u32] {
2433
                        set self.scope = ms;
2434
                        set self.currentMod = mid;
2435
                    }
2436
                }
2437
            }
2438
        }
2439
2440
        match declNode.value {
2441
            case ast::NodeValue::RecordDecl(decl) => {
2442
                try resolveRecordBody(self, declNode, decl) catch error {
2443
                    set self.scope = prevScope;
2444
                    set self.currentMod = prevMod;
2445
                    throw error;
2446
                };
2447
            }
2448
            case ast::NodeValue::UnionDecl(decl) => {
2449
                try resolveUnionBody(self, declNode, decl) catch error {
2450
                    set self.scope = prevScope;
2451
                    set self.currentMod = prevMod;
2452
                    throw error;
2453
                };
2454
            }
2455
            else => {},
2456
        }
2457
        set self.scope = prevScope;
2458
        set self.currentMod = prevMod;
2459
    }
2460
}
2461
2462
/// Check if all elements in a node list are assignable to the target type.
2463
unsafe fn isListAssignable 'arena (self: &mut Resolver 'arena, targetType: Type, items: *[*ast::Node]) -> bool {
2464
    for itemNode in items {
2465
        let elemTy = typeFor(self, itemNode)
2466
            else return false;
2467
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
2468
            // Do nothing.
2469
        } else {
2470
            return false;
2471
        }
2472
    }
2473
    return true;
2474
}
2475
2476
/// Return whether pointer classes are compatible in the current safety context.
2477
fn pointerClassesAssignable(
2478
    to: types::PointerClass,
2479
    from: types::PointerClass,
2480
    inUnsafeContext: bool,
2481
) -> bool {
2482
    return to == from or (
2483
        to == types::PointerClass::Ref
2484
        and (types::isReference(from) or from == types::PointerClass::Owned
2485
            or (from == types::PointerClass::Unsafe and inUnsafeContext))
2486
    );
2487
}
2488
2489
/// Limit an exclusive value's implicit borrow to its owner's borrow.
2490
unsafe fn assignableValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: Type) -> Type {
2491
    match source {
2492
        case Type::Pointer { class, target, mutable: true } => {
2493
            let usable = pointerAddressClass(self, node, class, true);
2494
            return Type::Pointer { class: usable, target, mutable: true };
2495
        }
2496
        case Type::Slice { class, item, mutable: true } => {
2497
            let usable = pointerAddressClass(self, node, class, true);
2498
            return Type::Slice { class: usable, item, mutable: true };
2499
        }
2500
        case Type::TraitObject { class, traitInfo, mutable: true } => {
2501
            let usable = pointerAddressClass(self, node, class, true);
2502
            return Type::TraitObject { class: usable, traitInfo, mutable: true };
2503
        }
2504
        case Type::Optional(inner) => {
2505
            let value = assignableValueType(self, node, *inner);
2506
            if typesEqual(value, *inner) {
2507
                return source;
2508
            }
2509
            return Type::Optional(allocType(self, value));
2510
        }
2511
        else => return source,
2512
    }
2513
}
2514
2515
/// Check if the `from` type is assignable to the `to` type, and return a
2516
/// coercion plan if so.
2517
/// Referenced storage requires equal element types. Function values may gain
2518
/// an unsafe call requirement.
2519
unsafe fn isAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, rval: *ast::Node) -> ?Coercion {
2520
    let from = assignableValueType(self, rval, source);
2521
    if to == Type::Unknown or from == Type::Unknown {
2522
        return nil;
2523
    }
2524
    if from == Type::Undefined {
2525
        if containsRegion(to) {
2526
            return nil;
2527
        }
2528
        if to == Type::Never {
2529
            return nil;
2530
        }
2531
        // TODO: Don't let `undefined` be used in place of functions and other
2532
        // non-data types.
2533
        return Coercion::Identity;
2534
    }
2535
    // The "never" type can always be assigned, since the code path is never
2536
    // executed.
2537
    if from == Type::Never {
2538
        return Coercion::Identity;
2539
    }
2540
    if to == from {
2541
        return Coercion::Identity;
2542
    }
2543
    if let case Type::Cell { class, payload } = to {
2544
        let case Type::Cell { class: sourceClass, payload: sourcePayload } = from else return nil;
2545
        if pointerClassesAssignable(class, sourceClass, self.inUnsafeContext) and typesEqual(*payload, *sourcePayload) {
2546
            return Coercion::Identity;
2547
        }
2548
        return nil;
2549
    }
2550
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
2551
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
2552
            else return nil;
2553
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) {
2554
            return nil;
2555
        }
2556
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
2557
        if *lhsTarget == Type::Opaque {
2558
            if lhsMutable and not rhsMutable {
2559
                return nil;
2560
            }
2561
            return Coercion::Identity;
2562
        }
2563
        if lhsMutable and not rhsMutable {
2564
            return nil;
2565
        }
2566
        if typesEqual(*lhsTarget, *rhsTarget) {
2567
            return Coercion::Identity;
2568
        }
2569
        return nil;
2570
    }
2571
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
2572
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
2573
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2574
                or (lhsMutable and not rhsMutable)
2575
            {
2576
                return nil;
2577
            }
2578
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
2579
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
2580
            }
2581
        }
2582
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
2583
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2584
                or lhsTraitInfo <> rhsTraitInfo
2585
            {
2586
                return nil;
2587
            }
2588
            if lhsMutable and not rhsMutable {
2589
                return nil;
2590
            }
2591
            return Coercion::Identity;
2592
        }
2593
        return nil;
2594
    }
2595
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
2596
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
2597
            else return nil;
2598
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2599
            or (lhsMutable and not rhsMutable)
2600
        {
2601
            return nil;
2602
        }
2603
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
2604
        if *lhsItem == Type::Opaque {
2605
            return Coercion::Identity;
2606
        }
2607
        if typesEqual(*lhsItem, *rhsItem) {
2608
            return Coercion::Identity;
2609
        }
2610
        return nil;
2611
    }
2612
    match to {
2613
        case Type::Array(lhs) => {
2614
            let case Type::Array(rhs) = from
2615
                else return nil;
2616
2617
            if lhs.length <> rhs.length {
2618
                return nil;
2619
            }
2620
            // For array literals, check each element individually for
2621
            // assignability.
2622
            match rval.value {
2623
                case ast::NodeValue::ArrayLit(items) => {
2624
                    if rhs.length == 0 and lhs.length == 0 {
2625
                        return Coercion::Identity;
2626
                    }
2627
                    // TODO: This won't work, because we should be setting coercions
2628
                    // for every list item, but we don't. It's best to not have an
2629
                    // `isAssignable` function and just have one that records coercions.
2630
                    if isListAssignable(self, *lhs.item, items) {
2631
                        return Coercion::Identity;
2632
                    }
2633
                    return nil;
2634
                }
2635
                case ast::NodeValue::ArrayRepeatLit(repeat) => {
2636
                    return isAssignable(self, *lhs.item, *rhs.item, repeat.item);
2637
                }
2638
                else => {
2639
                    if typesEqual(*lhs.item, *rhs.item) {
2640
                        return Coercion::Identity;
2641
                    }
2642
                    return nil;
2643
                }
2644
            }
2645
        }
2646
2647
        case Type::Optional(inner) => {
2648
            if from == Type::Nil {
2649
                return Coercion::OptionalLift(to);
2650
            }
2651
            if let _ = isAssignable(self, *inner, from, rval) {
2652
                return Coercion::OptionalLift(to);
2653
            }
2654
            if let case Type::Optional(fromInner) = from {
2655
                return isAssignable(self, *inner, *fromInner, rval);
2656
            }
2657
            return nil;
2658
        }
2659
2660
        case Type::Fn(toInfo) => {
2661
            // Allow function type structural matching.
2662
            if let case Type::Fn(fromInfo) = from {
2663
                if fnTypeEqual(toInfo, fromInfo) or (
2664
                    toInfo.isUnsafe and not fromInfo.isUnsafe
2665
                    and fnSignatureEqual(toInfo, fromInfo)
2666
                ) {
2667
                    return Coercion::Identity;
2668
                }
2669
            }
2670
            return nil;
2671
        }
2672
        else => {
2673
            if isNumericType(to) and isNumericType(from) {
2674
                // Perform range validation at compile time if possible.
2675
                // For unsuffixed integer expressions (`Type::Int`), only
2676
                // validate literals directly written by the programmer.
2677
                // Folded results (e.g. `0 - 65`) may not fit the target
2678
                // type but are valid wrapping arithmetic at runtime.
2679
                if let value = constValueEntry(self, rval) {
2680
                    if from <> Type::Int or isIntegerLiteralExpr(rval) {
2681
                        if validateConstIntRange(value, to) {
2682
                            return Coercion::Identity;
2683
                        }
2684
                        return nil;
2685
                    }
2686
                    // Folded constant expression (e.g. `1 + 2`): if the
2687
                    // result fits the target, use identity. Otherwise allow
2688
                    // wrapping via numeric cast.
2689
                    if validateConstIntRange(value, to) {
2690
                        return Coercion::Identity;
2691
                    }
2692
                }
2693
                // Allow unsuffixed integer expressions to be inferred from context.
2694
                if from == Type::Int {
2695
                    return Coercion::NumericCast { from, to };
2696
                }
2697
                // Non-constant numeric values require an explicit cast.
2698
                return nil;
2699
            }
2700
        }
2701
    }
2702
    return nil;
2703
}
2704
2705
/// Check if two function type descriptors are structurally equivalent.
2706
fn fnTypeEqual(a: &FnType, b: &FnType) -> bool {
2707
    if a.isUnsafe <> b.isUnsafe {
2708
        return false;
2709
    }
2710
    return fnSignatureEqual(a, b);
2711
}
2712
2713
/// Compare parameter, return, and error types of functions.
2714
fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool {
2715
    if a.regions <> b.regions {
2716
        return false;
2717
    }
2718
    if a.paramTypes.len <> b.paramTypes.len {
2719
        return false;
2720
    }
2721
    if a.throwList.len <> b.throwList.len {
2722
        return false;
2723
    }
2724
    if not typesEqual(*a.returnType, *b.returnType) {
2725
        return false;
2726
    }
2727
    for i in 0..a.paramTypes.len {
2728
        if not typesEqual(*a.paramTypes[i], *b.paramTypes[i]) {
2729
            return false;
2730
        }
2731
    }
2732
    for i in 0..a.throwList.len {
2733
        if not typesEqual(*a.throwList[i], *b.throwList[i]) {
2734
            return false;
2735
        }
2736
    }
2737
    return true;
2738
}
2739
2740
/// Check if two types are structurally equal.
2741
export fn typesEqual(a: Type, b: Type) -> bool {
2742
    // Nominal and trait types compare by descriptor identity.
2743
    if a == b {
2744
        return true;
2745
    }
2746
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
2747
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
2748
            else return false;
2749
        return aClass == bClass and aMutable == bMutable
2750
            and typesEqual(*aTarget, *bTarget);
2751
    }
2752
    if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a {
2753
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
2754
            else return false;
2755
        return aClass == bClass and aMutable == bMutable
2756
            and typesEqual(*aItem, *bItem);
2757
    }
2758
    match a {
2759
        case Type::Cell { class, payload } => {
2760
            let case Type::Cell { class: otherClass, payload: other } = b else return false;
2761
            return class == otherClass and typesEqual(*payload, *other);
2762
        }
2763
        case Type::Array(aa) => {
2764
            let case Type::Array(ab) = b else return false;
2765
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
2766
        }
2767
        case Type::Optional(oa) => {
2768
            let case Type::Optional(ob) = b else return false;
2769
            return typesEqual(*oa, *ob);
2770
        }
2771
        case Type::Fn(fa) => {
2772
            let case Type::Fn(fb) = b else return false;
2773
            return fnTypeEqual(fa, fb);
2774
        }
2775
        else => return false,
2776
    }
2777
}
2778
2779
/// Compare types after lexical region arguments are erased.
2780
/// Nominal types retain their source declaration identity.
2781
export unsafe fn erasedTypesEqual(a: Type, b: Type) -> bool {
2782
    if typesEqual(a, b) {
2783
        return true;
2784
    }
2785
    match a {
2786
        case Type::Cell { class, payload } => {
2787
            let case Type::Cell { class: otherClass, payload: other } = b else return false;
2788
            return erasedClassesEqual(class, otherClass) and erasedTypesEqual(*payload, *other);
2789
        }
2790
        case Type::Session(_) => {
2791
            let case Type::Session(_) = b else return false;
2792
            return true;
2793
        }
2794
        case Type::Nominal(left) => {
2795
            let case Type::Nominal(right) = b else return false;
2796
            let mut leftBase = left;
2797
            let mut rightBase = right;
2798
            if let app = nominalApplication(left) {
2799
                set leftBase = app.base;
2800
            }
2801
            if let app = nominalApplication(right) {
2802
                set rightBase = app.base;
2803
            }
2804
            return leftBase == rightBase;
2805
        }
2806
        case Type::Pointer { class, target, mutable } => {
2807
            let case Type::Pointer { class: otherClass, target: other, mutable: otherMutable } = b
2808
                else return false;
2809
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2810
                and erasedTypesEqual(*target, *other);
2811
        }
2812
        case Type::Slice { class, item, mutable } => {
2813
            let case Type::Slice { class: otherClass, item: other, mutable: otherMutable } = b
2814
                else return false;
2815
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2816
                and erasedTypesEqual(*item, *other);
2817
        }
2818
        case Type::TraitObject { class, traitInfo, mutable } => {
2819
            let case Type::TraitObject { class: otherClass, traitInfo: other, mutable: otherMutable } = b
2820
                else return false;
2821
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable and traitInfo == other;
2822
        }
2823
        case Type::Array(left) => {
2824
            let case Type::Array(right) = b else return false;
2825
            return left.length == right.length and erasedTypesEqual(*left.item, *right.item);
2826
        }
2827
        case Type::Optional(left) => {
2828
            let case Type::Optional(right) = b else return false;
2829
            return erasedTypesEqual(*left, *right);
2830
        }
2831
        case Type::Fn(left) => {
2832
            let case Type::Fn(right) = b else return false;
2833
            if left.isUnsafe <> right.isUnsafe or left.paramTypes.len <> right.paramTypes.len
2834
                or left.throwList.len <> right.throwList.len {
2835
                return false;
2836
            }
2837
            for ty, i in left.paramTypes {
2838
                if not erasedTypesEqual(*ty, *right.paramTypes[i]) {
2839
                    return false;
2840
                }
2841
            }
2842
            for ty, i in left.throwList {
2843
                if not erasedTypesEqual(*ty, *right.throwList[i]) {
2844
                    return false;
2845
                }
2846
            }
2847
            return erasedTypesEqual(*left.returnType, *right.returnType);
2848
        }
2849
        else => return false,
2850
    }
2851
}
2852
2853
/// Compare pointer classes without lexical region identities.
2854
fn erasedClassesEqual(a: types::PointerClass, b: types::PointerClass) -> bool {
2855
    return a == b or (types::isReference(a) and types::isReference(b));
2856
}
2857
2858
/// Require distinct runtime tags for errors with different source types.
2859
unsafe fn validateErrorTag 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type, errors: *[*Type]) throws (ResolveError) {
2860
    for other in errors {
2861
        if not typesEqual(ty, *other) and erasedTypesEqual(ty, *other) {
2862
            throw emitError(self, node, ErrorKind::AmbiguousRegionalError);
2863
        }
2864
    }
2865
}
2866
2867
/// Return whether `ty` is a direct reference.
2868
export fn isRefType(ty: Type) -> bool {
2869
    match ty {
2870
        case Type::Cell { class, .. } => return types::isReference(class),
2871
        case Type::Pointer { class, .. } => return types::isReference(class),
2872
        case Type::Slice { class, .. } => return types::isReference(class),
2873
        case Type::TraitObject { class, .. } => return types::isReference(class),
2874
        else => return false,
2875
    }
2876
}
2877
2878
/// Get the region of a direct named reference.
2879
fn referenceRegion(ty: Type) -> ?*unsafe types::Region {
2880
    let mut class = types::PointerClass::Ref;
2881
    match ty {
2882
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
2883
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
2884
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
2885
        case Type::TraitObject { class: objectClass, .. } => set class = objectClass,
2886
        else => return nil,
2887
    }
2888
    if let case types::PointerClass::Region(region) = class {
2889
        return region;
2890
    }
2891
    return nil;
2892
}
2893
2894
/// Require every free region in a value type to remain in lexical scope.
2895
unsafe fn validateRegionDependencies 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
2896
    throws (ResolveError)
2897
{
2898
    try validateRegionStorage(self, node, ty, nil);
2899
}
2900
2901
/// Check a dependency against storage lifetime or current lexical visibility.
2902
unsafe fn regionCoversStorage(
2903
    scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region
2904
) -> bool {
2905
    if let region = destination {
2906
        return types::regionContains(dependency, region);
2907
    }
2908
    return regionInScope(scope, dependency.id);
2909
}
2910
2911
/// Require stored references to cover the lifetime of checked destination storage.
2912
unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type)
2913
    throws (ResolveError)
2914
{
2915
    if let case types::PointerClass::Region(region) = addressStorageClass(self, place) {
2916
        try validateRegionStorage(self, value, ty, region);
2917
    }
2918
}
2919
2920
/// Require all type dependencies to cover the destination or active lexical scope.
2921
unsafe fn validateRegionStorage 'arena (
2922
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, destination: ?*unsafe types::Region
2923
)
2924
    throws (ResolveError)
2925
{
2926
    if let case Type::Session(region) = ty {
2927
        if not regionCoversStorage(self.regionScope, region, destination) {
2928
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2929
        }
2930
    }
2931
    if let region = referenceRegion(ty) {
2932
        if not regionCoversStorage(self.regionScope, region, destination) {
2933
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2934
        }
2935
    }
2936
    match ty {
2937
        case Type::Pointer { target, .. } => try validateRegionStorage(self, node, *target, destination),
2938
        case Type::Slice { item, .. } => try validateRegionStorage(self, node, *item, destination),
2939
        case Type::Cell { payload, .. } => try validateRegionStorage(self, node, *payload, destination),
2940
        case Type::Array(array) => try validateRegionStorage(self, node, *array.item, destination),
2941
        case Type::Optional(inner) => try validateRegionStorage(self, node, *inner, destination),
2942
        case Type::Nominal(info) => {
2943
            if let applied = nominalApplication(info) {
2944
                for region in applied.arguments {
2945
                    if not regionCoversStorage(self.regionScope, region, destination) {
2946
                        throw emitError(self, node, ErrorKind::RegionEscape(region.name));
2947
                    }
2948
                }
2949
            }
2950
        }
2951
        case Type::Fn(info) => {
2952
            if info.regions <> nil {
2953
                return;
2954
            }
2955
            for parameter in info.paramTypes {
2956
                try validateRegionStorage(self, node, *parameter, destination);
2957
            }
2958
            for error in info.throwList {
2959
                try validateRegionStorage(self, node, *error, destination);
2960
            }
2961
            try validateRegionStorage(self, node, *info.returnType, destination);
2962
        }
2963
        else => {
2964
        },
2965
    }
2966
}
2967
2968
/// Return whether a type has an explicit region dependency.
2969
unsafe fn containsRegion(ty: Type) -> bool {
2970
    match ty {
2971
        case Type::Cell { class, payload } => {
2972
            if let case types::PointerClass::Region(_) = class {
2973
                return true;
2974
            }
2975
            return containsRegion(*payload);
2976
        }
2977
        case Type::Session(_) => return true,
2978
        case Type::Pointer { class, target, .. } => {
2979
            if let case types::PointerClass::Region(_) = class {
2980
                return true;
2981
            }
2982
            return containsRegion(*target);
2983
        }
2984
        case Type::Slice { class, item, .. } => {
2985
            if let case types::PointerClass::Region(_) = class {
2986
                return true;
2987
            }
2988
            return containsRegion(*item);
2989
        }
2990
        case Type::TraitObject { class, .. } => {
2991
            if let case types::PointerClass::Region(_) = class {
2992
                return true;
2993
            }
2994
            return false;
2995
        }
2996
        case Type::Array(array) => return containsRegion(*array.item),
2997
        case Type::Optional(inner) => return containsRegion(*inner),
2998
        case Type::Fn(info) => {
2999
            if info.regions <> nil or containsRegion(*info.returnType) {
3000
                return true;
3001
            }
3002
            for param in info.paramTypes {
3003
                if containsRegion(*param) {
3004
                    return true;
3005
                }
3006
            }
3007
            for error in info.throwList {
3008
                if containsRegion(*error) {
3009
                    return true;
3010
                }
3011
            }
3012
            return false;
3013
        }
3014
        case Type::Nominal(info) => return nominalApplication(info) <> nil,
3015
        else => return false,
3016
    }
3017
}
3018
3019
/// Return whether a stored type contains a reference without a named region.
3020
fn containsUnscopedRef(ty: Type) -> bool {
3021
    if isRefType(ty) and referenceRegion(ty) == nil {
3022
        return true;
3023
    }
3024
    if let case Type::Pointer { target, .. } = ty {
3025
        return containsUnscopedRef(*target);
3026
    }
3027
    if let case Type::Slice { item, .. } = ty {
3028
        return containsUnscopedRef(*item);
3029
    }
3030
    match ty {
3031
        case Type::Cell { payload, .. } => return containsUnscopedRef(*payload),
3032
        case Type::Array(array) => return containsUnscopedRef(*array.item),
3033
        case Type::Optional(inner) => return containsUnscopedRef(*inner),
3034
        case Type::Fn(info) => return info.regions <> nil,
3035
        // Nominal declarations validate their own fields and variants.
3036
        // Treating them as leaves also terminates recursive pointer types.
3037
        case Type::Nominal(_) => return false,
3038
        else => return false,
3039
    }
3040
}
3041
3042
/// Return whether `ty` may be duplicated implicitly.
3043
export unsafe fn isCopy(ty: Type) -> bool {
3044
    match ty {
3045
        case Type::Session(_) => return false,
3046
        case Type::Pointer { class, mutable, .. } =>
3047
            return class == types::PointerClass::Unsafe or not mutable,
3048
        case Type::Slice { class, mutable, .. } =>
3049
            return class == types::PointerClass::Unsafe or not mutable,
3050
        case Type::TraitObject { class, mutable, .. } =>
3051
            return class == types::PointerClass::Unsafe or not mutable,
3052
        case Type::Array(array) => return isCopy(*array.item),
3053
        case Type::Optional(inner) => return isCopy(*inner),
3054
        case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy,
3055
        case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy,
3056
        case Type::Nominal(NominalType::Application(applied)) => return isCopy(Type::Nominal(applied.base)),
3057
        case Type::Nominal(NominalType::Placeholder(_)), Type::Nominal(NominalType::Resolving(_)) => return false,
3058
        else => return true,
3059
    }
3060
}
3061
3062
/// Return whether a type must be consumed exactly once.
3063
export unsafe fn isLinear(ty: Type) -> bool {
3064
    match ty {
3065
        case Type::Nominal(NominalType::Application(applied)) => return isLinear(Type::Nominal(applied.base)),
3066
        case Type::Array(array) => return isLinear(*array.item),
3067
        case Type::Optional(inner) => return isLinear(*inner),
3068
        case Type::Nominal(NominalType::Record(recInfo)) => {
3069
            if recInfo.declaredLinear {
3070
                return true;
3071
            }
3072
            for field in recInfo.fields {
3073
                if isLinear(field.fieldType) {
3074
                    return true;
3075
                }
3076
            }
3077
            return false;
3078
        }
3079
        case Type::Nominal(NominalType::Union(unionType)) => {
3080
            if unionType.declaredLinear {
3081
                return true;
3082
            }
3083
            for variant in unionType.variants {
3084
                if isLinear(variant.valueType) {
3085
                    return true;
3086
                }
3087
            }
3088
            return false;
3089
        }
3090
        else => return false,
3091
    }
3092
}
3093
3094
/// Return whether a by-value use moves `ty`.
3095
unsafe fn isMoveOnly(ty: Type) -> bool {
3096
    return not isCopy(ty);
3097
}
3098
3099
/// Return whether `ty` is a direct unsafe pointer-like value.
3100
fn isUnsafePointerType(ty: Type) -> bool {
3101
    match ty {
3102
        case Type::Cell { class: types::PointerClass::Unsafe, .. },
3103
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
3104
             Type::Slice { class: types::PointerClass::Unsafe, .. },
3105
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
3106
        else => return false,
3107
    }
3108
}
3109
3110
/// Get the record info from a record type.
3111
export unsafe fn getRecord(ty: Type) -> ?RecordType {
3112
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
3113
    return recInfo;
3114
}
3115
3116
/// Auto-dereference a type: if it's a pointer, return the target type.
3117
export fn autoDeref(ty: Type) -> Type {
3118
    if let case Type::Pointer { target, .. } = ty {
3119
        return *target;
3120
    }
3121
    return ty;
3122
}
3123
3124
/// Get field info for a record-like type (records, slices) by field index.
3125
export unsafe fn getRecordField(ty: Type, index: u32) -> ?RecordField {
3126
    if let case Type::Slice { class, item, mutable } = ty {
3127
        match index {
3128
            case 0 => return RecordField {
3129
                name: PTR_FIELD,
3130
                fieldType: Type::Pointer { class, target: item, mutable },
3131
                offset: 0,
3132
            },
3133
            case 1 => return RecordField {
3134
                name: LEN_FIELD,
3135
                fieldType: Type::U32,
3136
                offset: PTR_SIZE as i32,
3137
            },
3138
            case 2 => return RecordField {
3139
                name: CAP_FIELD,
3140
                fieldType: Type::U32,
3141
                offset: PTR_SIZE as i32 + 4,
3142
            },
3143
            else => return nil,
3144
        }
3145
    }
3146
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
3147
        index < recInfo.fields.len
3148
    {
3149
        return recInfo.fields[index];
3150
    }
3151
    return nil;
3152
}
3153
3154
/// Check if the two types can be compared for equality.
3155
unsafe fn isComparable(left: Type, right: Type) -> bool {
3156
    if left == Type::Unknown or right == Type::Unknown {
3157
        return false;
3158
    }
3159
    if left == right {
3160
        return true;
3161
    }
3162
    // Comparisons with optionals.
3163
    if let case Type::Optional(l) = left {
3164
        if let case Type::Optional(r) = right {
3165
            return isComparable(*l, *r);
3166
        } else if right == Type::Nil {
3167
            return true;
3168
        }
3169
        return isComparable(*l, right);
3170
    } else if let case Type::Optional(_) = right {
3171
        return isComparable(right, left); // Flip order.
3172
    }
3173
    // Pointer comparisons ignore mutability.
3174
    if let case Type::Pointer { target: lTarget, .. } = left {
3175
        if let case Type::Pointer { target: rTarget, .. } = right {
3176
            return typesEqual(*lTarget, *rTarget);
3177
        }
3178
    }
3179
    // Numeric types.
3180
    if isNumericType(left) and isNumericType(right) {
3181
        return true;
3182
    }
3183
    return false;
3184
}
3185
3186
/// Check if the `from` type is assignable to the `to` type, and return a
3187
/// coercion plan if so, or throw an error if not.
3188
unsafe fn expectAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
3189
    let from = assignableValueType(self, site, source);
3190
    if isRefType(to) and isUnsafePointerType(from) {
3191
        try requireUnsafe(self, site);
3192
    }
3193
    // Ensure any nested nominal types are resolved before checking assignability.
3194
    try ensureTypeResolved(self, to, site);
3195
    if let coercion = isAssignable(self, to, from, site) {
3196
        return setNodeCoercion(self, site, coercion);
3197
    }
3198
    throw emitTypeMismatch(self, site, TypeMismatch {
3199
        expected: to,
3200
        actual: from,
3201
    });
3202
}
3203
3204
/// Check that a type is optional, otherwise throw an error.
3205
unsafe fn checkOptional 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *Type
3206
    throws (ResolveError)
3207
{
3208
    if let case Type::Optional(inner) = try infer(self, node) {
3209
        return inner;
3210
    }
3211
    throw emitError(self, node, ErrorKind::ExpectedOptional);
3212
}
3213
3214
/// Check that a node's type is equal to the expected type.
3215
unsafe fn checkEqual 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type
3216
    throws (ResolveError)
3217
{
3218
    let actualTy = try visit(self, node, expected);
3219
    if actualTy <> expected {
3220
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy });
3221
    }
3222
    return actualTy;
3223
}
3224
3225
/// Bind an identifier in the given scope.
3226
unsafe fn bindIdent 'arena (
3227
    self: &mut Resolver 'arena,
3228
    name: *[u8],
3229
    owner: *ast::Node,
3230
    data: SymbolData,
3231
    attrs: u32,
3232
    scope: *unsafe mut Scope
3233
) -> *unsafe mut Symbol throws (ResolveError) {
3234
    let sym = allocSymbol(self, data, name, owner, attrs);
3235
    try addSymbolToScope(self, sym, scope, owner);
3236
    setNodeSymbol(self, owner, sym);
3237
3238
    return sym;
3239
}
3240
3241
/// Add a symbol to the given scope.
3242
unsafe fn addSymbolToScope 'arena (self: &mut Resolver 'arena, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) {
3243
    for i in 0..scope.symbolsLen {
3244
        if scope.symbols[i].name == sym.name {
3245
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
3246
        }
3247
    }
3248
    if scope.symbolsLen >= scope.symbols.len {
3249
        throw emitError(self, site, ErrorKind::SymbolOverflow);
3250
    }
3251
    // Preserve the defining module when importing an existing symbol into
3252
    // another module's scope.
3253
    if sym.moduleId == nil {
3254
        if let modId = scope.moduleId {
3255
            set sym.moduleId = modId;
3256
        }
3257
    }
3258
    set scope.symbols[scope.symbolsLen] = sym;
3259
    set scope.symbolsLen += 1;
3260
}
3261
3262
/// Bind a value identifier in the current scope.
3263
/// Returns `nil` if the identifier is a placeholder (`_`).
3264
unsafe fn bindValueIdent 'arena (
3265
    self: &mut Resolver 'arena,
3266
    ident: *ast::Node,
3267
    owner: *ast::Node,
3268
    type: Type,
3269
    mutable: bool,
3270
    alignment: u32,
3271
    attrs: u32
3272
) -> ?*unsafe mut Symbol throws (ResolveError) {
3273
    if let case ast::NodeValue::Placeholder = ident.value {
3274
        setNodeType(self, owner, type);
3275
        return nil;
3276
    }
3277
    let name = try nodeName(self, ident);
3278
    let data = SymbolData::Value { mutable, alignment, type, addressTaken: false };
3279
    let scope = self.scope;
3280
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
3281
    setNodeType(self, owner, type);
3282
    setNodeType(self, ident, type);
3283
3284
    // Track number of local bindings for lowering stage.
3285
    if let owner = self.currentFnNode {
3286
        set self.nodeData.entries[owner.id].localCount += 1;
3287
    }
3288
    return sym;
3289
}
3290
3291
/// Bind a constant identifier in the current scope.
3292
unsafe fn bindConstIdent 'arena (
3293
    self: &mut Resolver 'arena,
3294
    ident: *ast::Node,
3295
    owner: *ast::Node,
3296
    type: Type,
3297
    val: ?ConstValue,
3298
    attrs: u32
3299
) -> *unsafe mut Symbol throws (ResolveError) {
3300
    let name = try nodeName(self, ident);
3301
    let data = SymbolData::Constant { type, value: val };
3302
    let scope = self.scope;
3303
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
3304
    setNodeType(self, owner, type);
3305
    setNodeType(self, ident, type);
3306
3307
    return sym;
3308
}
3309
3310
/// Bind a module identifier in the given scope.
3311
/// This is used when declaring modules with `mod` or
3312
/// importing modules with `use`.
3313
unsafe fn bindModuleIdent 'arena (
3314
    self: &mut Resolver 'arena,
3315
    entry: *module::ModuleEntry,
3316
    scope: *unsafe mut Scope,
3317
    owner: *ast::Node,
3318
    attrs: u32,
3319
    bindingScope: *unsafe mut Scope
3320
) -> *unsafe mut Symbol throws (ResolveError) {
3321
    let data = SymbolData::Module { entry, scope };
3322
    let name = entry.name;
3323
3324
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
3325
}
3326
3327
/// Bind a type identifier in the current scope.
3328
unsafe fn bindTypeIdent 'arena (
3329
    self: &mut Resolver 'arena,
3330
    ident: *ast::Node,
3331
    owner: *ast::Node,
3332
    type: *unsafe mut NominalType,
3333
    attrs: u32
3334
) -> *unsafe mut Symbol throws (ResolveError) {
3335
    let name = try nodeName(self, ident);
3336
    let data = SymbolData::Type(type);
3337
    let scope = self.scope;
3338
    return try bindIdent(self, name, owner, data, attrs, scope);
3339
}
3340
3341
/// Predicate that matches any symbol.
3342
fn isAnySymbol(_sym: &Symbol) -> bool {
3343
    return true;
3344
}
3345
3346
/// Predicate that matches value or constant symbols.
3347
fn isValueSymbol(sym: &Symbol) -> bool {
3348
    if let case SymbolData::Value { .. } = sym.data {
3349
        return true;
3350
    }
3351
    if let case SymbolData::Constant { .. } = sym.data {
3352
        return true;
3353
    }
3354
    return false;
3355
}
3356
3357
/// Predicate that matches type symbols.
3358
fn isTypeSymbol(sym: &Symbol) -> bool {
3359
    if let case SymbolData::Type(_) = sym.data {
3360
        return true;
3361
    }
3362
    return false;
3363
}
3364
3365
/// Find a symbol by name in a specific scope, filtered by a predicate.
3366
unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3367
    for i in 0..scope.symbolsLen {
3368
        let sym = scope.symbols[i];
3369
        if sym.name == name and predicate(sym) {
3370
            return sym;
3371
        }
3372
    }
3373
    return nil;
3374
}
3375
3376
/// Find a symbol by name, traversing scopes upwards, filtered by a predicate.
3377
unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3378
    let mut curr = scope;
3379
    loop {
3380
        if let sym = findInScope(curr, name, predicate) {
3381
            return sym;
3382
        }
3383
        if let parent = curr.parent {
3384
            set curr = parent;
3385
        } else {
3386
            break;
3387
        }
3388
    }
3389
    return nil;
3390
}
3391
3392
/// Find a symbol by name in a specific scope (matches any symbol kind).
3393
export unsafe fn findSymbolInScope(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3394
    return findInScope(scope, name, isAnySymbol);
3395
}
3396
3397
/// Look up a value symbol by name, searching from the given scope outward.
3398
unsafe fn findValueSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3399
    return findInScopeRecursive(scope, name, isValueSymbol);
3400
}
3401
3402
/// Look up a type symbol by name, searching from the given scope outward.
3403
unsafe fn findTypeSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3404
    return findInScopeRecursive(scope, name, isTypeSymbol);
3405
}
3406
3407
/// Like `findValueSymbol`, but finds symbols of any kinds.
3408
unsafe fn findAnySymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3409
    return findInScopeRecursive(scope, name, isAnySymbol);
3410
}
3411
3412
/// Flatten an identifier or scope access chain into an array of name segments.
3413
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
3414
/// Return the number of segments written to the buffer.
3415
fn flattenPath 'arena (
3416
    self: &mut Resolver 'arena,
3417
    node: *ast::Node,
3418
    buf: &mut [*[u8]]
3419
) -> u32 throws (ResolveError) {
3420
    let mut out: u32 = 0;
3421
3422
    match node.value {
3423
        case ast::NodeValue::Ident(name) if name.len > 0 => {
3424
            assert buf.len >= 1, "flattenPath: invalid output buffer size";
3425
            set buf[0] = name;
3426
            set out = 1;
3427
        }
3428
        case ast::NodeValue::ScopeAccess(access) => {
3429
            // Recursively flatten parent path.
3430
            let parent = try flattenPath(self, access.parent, buf);
3431
            assert parent < buf.len, "flattenPath: invalid output buffer size";
3432
            let child = try nodeName(self, access.child);
3433
            set buf[parent] = child;
3434
            set out = parent + 1;
3435
        }
3436
        case ast::NodeValue::Super => {
3437
            // `super` is handled by scope adjustment in `checkSuperAccess`.
3438
            // Return empty prefix so the path continues from the next segment.
3439
            set out = 0;
3440
            return out;
3441
        }
3442
        else => {
3443
            // Fallthrough to error.
3444
        }
3445
    }
3446
    if out < 1 {
3447
        throw emitError(self, node, ErrorKind::InvalidIdentifier(node));
3448
    }
3449
    return out;
3450
}
3451
3452
/// Find the module ID for a given scope by walking up the scope chain until
3453
/// we hit the module's scope.
3454
unsafe fn findModuleForScope(scope: *unsafe Scope) -> ?u16 {
3455
    let mut s = scope;
3456
    loop {
3457
        if let id = s.moduleId {
3458
            return id;
3459
        }
3460
        if let parent = s.parent {
3461
            set s = parent;
3462
        } else {
3463
            return nil;
3464
        }
3465
    }
3466
}
3467
3468
/// Return a retained module identity, if its ID is registered.
3469
export fn moduleFor 'arena (self: &Resolver 'arena, id: u16) -> ?*module::ModuleEntry {
3470
    if id as u32 >= self.moduleEntries.len {
3471
        return nil;
3472
    }
3473
    return self.moduleEntries[id as u32];
3474
}
3475
3476
/// Find a retained child identity by its parent and name.
3477
fn findChildModule 'arena (self: &Resolver 'arena, name: *[u8], parentId: u16) -> ?*module::ModuleEntry {
3478
    let parent = moduleFor(self, parentId) else return nil;
3479
    for i in 0..module::childCount(parent) {
3480
        let child = moduleFor(self, module::childAt(parent, i))
3481
            else panic "findChildModule: missing child identity";
3482
        if mem::eq(child.name, name) {
3483
            return child;
3484
        }
3485
    }
3486
    return nil;
3487
}
3488
3489
/// Get the parent module scope for the current module.
3490
/// Returns the scope of the parent module, or `nil` if this is a root module.
3491
fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) {
3492
    let currentMod = moduleFor(self, self.currentMod)
3493
        else throw emitError(self, node, ErrorKind::Internal);
3494
    let parentId = currentMod.parent
3495
        else return nil; // No parent module.
3496
3497
    return self.moduleScopes[parentId as u32];
3498
}
3499
3500
/// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`).
3501
/// Returns the parent scope and the original node so `flattenPath` can strip `super`.
3502
fn checkSuperAccess 'arena (
3503
    self: &mut Resolver 'arena,
3504
    node: *ast::Node
3505
) -> ?SuperAccessResult throws (ResolveError) {
3506
    // TODO: Maybe we should deal with `super` after the path is flattened.
3507
    if let case ast::NodeValue::ScopeAccess(access) = node.value {
3508
        // Direct super access: `super::x`.
3509
        if let case ast::NodeValue::Super = access.parent.value {
3510
            let parentScope = try getParentModuleScope(self, node)
3511
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
3512
            return SuperAccessResult { scope: parentScope, child: node };
3513
        }
3514
        // Nested super access: `super::x::y`, check if parent path contains `super`.
3515
        if let _ = try checkSuperAccess(self, access.parent) {
3516
            let parentScope = try getParentModuleScope(self, node)
3517
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
3518
            return SuperAccessResult { scope: parentScope, child: node };
3519
        }
3520
    }
3521
    return nil;
3522
}
3523
3524
/// Check symbol visibility from declaration attributes and module identities.
3525
/// A symbol is accessible if:
3526
/// * It has the `export` attribute, OR
3527
/// * It's being accessed from within the module where it was defined.
3528
fn isSymbolVisible(attrs: u32, symModuleId: ?u16, currentModuleId: ?u16) -> bool {
3529
    // Public symbols are visible from anywhere.
3530
    if ast::hasAttribute(attrs, ast::Attribute::Export) {
3531
        return true;
3532
    }
3533
    // In test mode, @test symbols are visible from anywhere
3534
    // so the test runner can reference them.
3535
    if ast::hasAttribute(attrs, ast::Attribute::Test) {
3536
        return true;
3537
    }
3538
    // Private symbols are only visible from the same module.
3539
    return symModuleId == currentModuleId;
3540
}
3541
3542
/// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol,
3543
/// starting from the given scope.
3544
unsafe fn resolveAccess 'arena (
3545
    self: &mut Resolver 'arena,
3546
    node: *ast::Node,
3547
    access: ast::Access,
3548
    scope: *unsafe Scope
3549
) -> *unsafe mut Symbol throws (ResolveError) {
3550
    if let case ast::NodeValue::RegionApply { .. } = access.parent.value {
3551
        let ty = try infer(self, access.parent);
3552
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3553
        try ensureNominalResolved(self, info, access.parent);
3554
        let case NominalType::Union(body) = *info else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3555
        let name = try nodeName(self, access.child);
3556
        let symbol = try resolveUnionVariantAccess(self, node, access, body, name);
3557
        setNodeType(self, node, ty);
3558
        return symbol;
3559
    }
3560
    // Handle `super` access by adjusting scope and node.
3561
    let mut startScope = scope;
3562
    let mut pathNode = node;
3563
    if let superAccess = try checkSuperAccess(self, node) {
3564
        set startScope = superAccess.scope;
3565
        set pathNode = superAccess.child;
3566
    }
3567
    // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access,
3568
    // while this function requires a scope access.
3569
    let mut buffer: [*[u8]; 32] = undefined;
3570
    let pathLen = try flattenPath(self, pathNode, &mut buffer[..]);
3571
3572
    return try resolvePath(self, node, access, &buffer[..pathLen], startScope);
3573
}
3574
3575
/// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol,
3576
/// starting from the given scope.
3577
unsafe fn resolvePath 'arena (
3578
    self: &mut Resolver 'arena,
3579
    node: *ast::Node,
3580
    access: ast::Access,
3581
    path: &[*[u8]],
3582
    scope: *unsafe Scope
3583
) -> *unsafe mut Symbol throws (ResolveError) {
3584
    assert path.len <> 0, "resolvePath: empty path";
3585
    // Start by finding the root of the path.
3586
    let root = path[0];
3587
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
3588
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
3589
3590
    // Check visibility for symbol.
3591
    if not isSymbolVisible(sym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) {
3592
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
3593
    }
3594
    // End condition.
3595
    if path.len == 1 {
3596
        return sym;
3597
    }
3598
    // Otherwise, we need to enter the next scope with the path suffix.
3599
    match sym.data {
3600
        case SymbolData::Module { scope, .. } => {
3601
            return try resolvePath(self, node, access, &path[1..], scope);
3602
        }
3603
        case SymbolData::Type(ty) => {
3604
            // Lazily resolve union body if not yet done.
3605
            try ensureNominalResolved(self, ty, node);
3606
3607
            if let case NominalType::Union(unionType) = *ty {
3608
                // TODO: Recurse with variant so we consolidate everything.
3609
                if path.len > 2 {
3610
                    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3611
                }
3612
                let variantName = path[1];
3613
                let variantSym = try resolveUnionVariantAccess(
3614
                    self, node, access, unionType, variantName
3615
                );
3616
                // TODO: This shouldn't be here.
3617
                setNodeType(self, node, Type::Nominal(ty));
3618
                return variantSym;
3619
            }
3620
        }
3621
        else => {} // Fallthrough.
3622
    }
3623
    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3624
}
3625
3626
/// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope.
3627
/// This traverses the module hierarchy, checking visibility at each step.
3628
unsafe fn resolveModulePath 'arena (
3629
    self: &mut Resolver 'arena,
3630
    module: *ast::Node
3631
) -> ResolvedModule throws (ResolveError) {
3632
    let mut startScope = self.scope;
3633
    let mut pathNode = module;
3634
3635
    // Handle `super` access.
3636
    if let superAccess = try checkSuperAccess(self, module) {
3637
        set startScope = superAccess.scope;
3638
        set pathNode = superAccess.child;
3639
    }
3640
    let mut pathBuf: [*[u8]; 16] = undefined;
3641
    let pathLen = try flattenPath(self, pathNode, &mut pathBuf[..]);
3642
    if pathLen == 0 {
3643
        throw emitError(self, module, ErrorKind::UnresolvedSymbol(""));
3644
    }
3645
    let parentName = pathBuf[0];
3646
3647
    // First, check if this is a sub-module of the start scope.
3648
    if let sym = findSymbolInScope(startScope, parentName) {
3649
        return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
3650
    }
3651
    // Not a sub-module, so look in the global scope for a package root.
3652
    let sym = findSymbolInScope(self.pkgScope, parentName)
3653
        else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName));
3654
3655
    return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
3656
}
3657
3658
/// Recursively resolve the remaining path segments by traversing child modules.
3659
unsafe fn resolveModulePathRecursive 'arena (
3660
    self: &mut Resolver 'arena,
3661
    node: *ast::Node,
3662
    path: &[*[u8]],
3663
    sym: *unsafe Symbol
3664
) -> ResolvedModule throws (ResolveError) {
3665
    let case SymbolData::Module { entry, scope } = sym.data
3666
        else throw emitError(self, node, ErrorKind::Internal);
3667
3668
    if path.len == 0 {
3669
        return ResolvedModule { entry, scope };
3670
    }
3671
    let childName = path[0];
3672
    let childSym = findSymbolInScope(scope, childName)
3673
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
3674
3675
    if not isSymbolVisible(childSym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) {
3676
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
3677
    }
3678
    return try resolveModulePathRecursive(
3679
        self,
3680
        node,
3681
        &path[1..],
3682
        childSym
3683
    );
3684
}
3685
3686
/// Resolve a type name, which could be an identifier or scoped path.
3687
unsafe fn resolveTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) {
3688
    match node.value {
3689
        case ast::NodeValue::Ident(name) => {
3690
            let sym = findTypeSymbol(self.scope, name)
3691
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
3692
            let case SymbolData::Type(ty) = sym.data
3693
                else throw emitError(self, node, ErrorKind::Internal);
3694
3695
            setNodeSymbol(self, node, sym);
3696
3697
            return ty;
3698
        }
3699
        case ast::NodeValue::ScopeAccess(access) => {
3700
            let scope = self.scope;
3701
            let sym = try resolveAccess(self, node, access, scope);
3702
            let case SymbolData::Type(ty) = sym.data
3703
                else throw emitError(self, node, ErrorKind::Internal);
3704
3705
            setNodeSymbol(self, node, sym);
3706
3707
            return ty;
3708
        }
3709
        else => panic "resolveTypeName: unsupported node value",
3710
    }
3711
}
3712
3713
/// Visit a top-level declaration in the declaration phase.
3714
/// This binds all names and analyzes signatures, types, and initializers.
3715
/// Function bodies are deferred to the definition phase.
3716
///
3717
/// Nb. User-defined types are already handled by this point.
3718
unsafe fn visitDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
3719
    match node.value {
3720
        case ast::NodeValue::FnDecl(_),
3721
             ast::NodeValue::ConstDecl(_),
3722
             ast::NodeValue::Mod(_),
3723
             ast::NodeValue::Use(_) => {
3724
            // Handled in previous passes.
3725
        }
3726
        case ast::NodeValue::StaticDecl(_) => {
3727
            try infer(self, node);
3728
        }
3729
        case ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => {
3730
            try resolveInstanceDecl(self, node, traitName, targetType, regions, methods);
3731
        }
3732
        case ast::NodeValue::MethodDecl {
3733
            ..
3734
        } => {
3735
            try resolveMethodDecl(self, node);
3736
        }
3737
        else => {
3738
            // Ignore non-declaration nodes.
3739
        }
3740
    }
3741
}
3742
3743
/// Require an unsafe function or block.
3744
fn requireUnsafe 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
3745
    if not self.inUnsafeContext {
3746
        throw emitError(self, node, ErrorKind::UnsafeOperation);
3747
    }
3748
}
3749
3750
/// Require an unsafe context for any access to an unsafe static.
3751
fn checkStaticAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, sym: &Symbol)
3752
    throws (ResolveError)
3753
{
3754
    if let case ast::NodeValue::StaticDecl(_) = sym.node.value {
3755
        if ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) {
3756
            try requireUnsafe(self, node);
3757
        }
3758
    }
3759
}
3760
3761
/// Reject calls from safe code through unsafe function types.
3762
fn checkUnsafeCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: *FnType)
3763
    throws (ResolveError)
3764
{
3765
    if info.isUnsafe and not self.inUnsafeContext {
3766
        throw emitError(self, node, ErrorKind::UnsafeCall);
3767
    }
3768
}
3769
3770
/// Visit a top-level definition, recursing into sub-modules.
3771
unsafe fn visitDef 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
3772
    match node.value {
3773
        case ast::NodeValue::FnDecl(decl) => {
3774
            try resolveFnDeclBody(self, node, decl) catch {
3775
                return;
3776
            };
3777
        }
3778
        case ast::NodeValue::Mod(decl) => {
3779
            let modName = try nodeName(self, decl.name);
3780
            if not shouldAnalyzeModule(self, decl.attrs, modName) {
3781
                return;
3782
            }
3783
            let submod = try enterSubModule(self, modName, node);
3784
            let case ast::NodeValue::Block(block) = submod.root.value
3785
                else panic "visitDef: expected block for module root";
3786
            try resolveModuleDefs(self, &block) catch e {
3787
                exitModuleScope(self, submod);
3788
                throw e;
3789
            };
3790
            exitModuleScope(self, submod);
3791
        }
3792
        case ast::NodeValue::RecordDecl(_),
3793
             ast::NodeValue::UnionDecl(_),
3794
             ast::NodeValue::Use(_),
3795
             ast::NodeValue::TraitDecl { .. } => {
3796
            // Skip: already analyzed in declaration phase.
3797
        }
3798
        case ast::NodeValue::InstanceDecl { methods, .. } => {
3799
            try resolveInstanceMethodBodies(self, methods);
3800
        }
3801
        case ast::NodeValue::MethodDecl {
3802
            ..
3803
        } => {
3804
            try resolveMethodBody(self, node);
3805
        }
3806
        else => {
3807
            // FIXME: This allows module-level statements that should
3808
            // normally only be valid inside function bodies. We currently
3809
            // need this because of how tests are written, but it should
3810
            // be eventually removed.
3811
            try infer(self, node) catch {
3812
                return;
3813
            };
3814
        }
3815
    }
3816
}
3817
3818
/// Try to infer a node's type.
3819
unsafe fn infer 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
3820
    return try visit(self, node, Type::Unknown);
3821
}
3822
3823
/// Permit named reference dependencies and direct call-scoped or local references.
3824
unsafe fn validateValueTypeReferences 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
3825
    throws (ResolveError)
3826
{
3827
    try validateRegionDependencies(self, node, ty);
3828
    if let case Type::Fn(info) = ty; info.regions == nil {
3829
        return;
3830
    }
3831
    if isRefType(ty) {
3832
        if let case Type::Pointer { target, .. } = ty {
3833
            if containsUnscopedRef(*target) {
3834
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
3835
            }
3836
        } else if let case Type::Slice { item, .. } = ty {
3837
            if containsUnscopedRef(*item) {
3838
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
3839
            }
3840
        }
3841
    } else if containsUnscopedRef(ty) {
3842
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
3843
    }
3844
}
3845
3846
/// Require a type that may be stored or escape a call.
3847
unsafe fn ensureStorableType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
3848
    throws (ResolveError)
3849
{
3850
    try validateRegionDependencies(self, node, ty);
3851
    if containsUnscopedRef(ty) {
3852
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
3853
    }
3854
}
3855
3856
/// Resolve a type signature node.
3857
unsafe fn resolveValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
3858
    let ty = try visit(self, node, Type::Unknown);
3859
    if let case Type::Nominal(info) = ty {
3860
        try requireNominalArguments(self, info, node);
3861
    }
3862
    // Opaque value types are not allowed.
3863
    if ty == Type::Opaque {
3864
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
3865
    }
3866
    try validateValueTypeReferences(self, node, ty);
3867
    return ty;
3868
}
3869
3870
/// Analyze a node's type and check that it can be assigned to the expected type.
3871
unsafe fn checkAssignable 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
3872
    let actual = try visit(self, node, expected);
3873
    let _ = try expectAssignable(self, expected, actual, node);
3874
    if isRefType(expected) and isMutablePointerLike(expected) and isMutablePointerLike(actual) {
3875
        if not try canMutateThrough(self, node) {
3876
            throw emitError(self, node, ErrorKind::ImmutableBinding);
3877
        }
3878
    }
3879
    return actual;
3880
}
3881
3882
/// Analyze a node and propagate the resolved type.
3883
/// The `hint` parameter provides type context for inference and validation.
3884
/// When `nil`, the type must be inferred from the expression itself.
3885
unsafe fn visit 'arena (self: &mut Resolver 'arena, node: *ast::Node, hint: Type) -> Type
3886
    throws (ResolveError)
3887
{
3888
    if let ty = typeFor(self, node) {
3889
        // An optional context completes a nil expression's storage type.
3890
        if ty <> Type::Nil or not isOptionalType(hint) {
3891
            return ty;
3892
        }
3893
    }
3894
    match node.value {
3895
        case ast::NodeValue::Ident(name) => {
3896
            let sym = findAnySymbol(self.scope, name)
3897
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
3898
            try checkStaticAccess(self, node, sym);
3899
            setNodeSymbol(self, node, sym);
3900
            match sym.data {
3901
                case SymbolData::Value { type, .. } =>
3902
                    return setNodeType(self, node, type),
3903
                case SymbolData::Constant { type, value } => {
3904
                    if let val = value {
3905
                        setNodeConstValue(self, node, val);
3906
                    }
3907
                    return setNodeType(self, node, type);
3908
                },
3909
                case SymbolData::Type(t) =>
3910
                    return setNodeType(self, node, Type::Nominal(hintedNominal(t, hint))),
3911
                case SymbolData::Variant { .. } =>
3912
                    return Type::Void,
3913
                case SymbolData::Module { .. } =>
3914
                    throw emitError(self, node, ErrorKind::UnexpectedModuleName),
3915
                case SymbolData::Trait(_) =>
3916
                    throw emitError(self, node, ErrorKind::UnexpectedTraitName),
3917
            }
3918
        },
3919
        case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal, hint),
3920
        case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access),
3921
        case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop),
3922
        case ast::NodeValue::RegionBlock { region, bindings, body, isSession } => {
3923
            if isSession {
3924
                return try resolveSessionBlock(self, node, region, bindings, body);
3925
            }
3926
            return try resolveBorrowBlock(self, node, region, bindings, body);
3927
        }
3928
        case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block),
3929
        case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl),
3930
        case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic(
3931
            self, node, decl.ident, decl.type, decl.value, decl.attrs, true
3932
        ),
3933
        case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic(
3934
            self, node, decl.ident, decl.type, decl.value, decl.attrs, false
3935
        ),
3936
        case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param),
3937
        case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond),
3938
        case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond, hint),
3939
        case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond),
3940
        case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode),
3941
        case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode),
3942
        case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode),
3943
        case ast::NodeValue::Loop { body } => {
3944
            let loopType = try visitLoop(self, body);
3945
            return setNodeType(self, node, loopType);
3946
        },
3947
        case ast::NodeValue::Break, ast::NodeValue::Continue => return try resolveLoopControl(self, node),
3948
        case ast::NodeValue::Match(sw) => return try resolveMatch(self, node, sw),
3949
        case ast::NodeValue::MatchProng(_) => panic "visit: `MatchProng` not handled here",
3950
        case ast::NodeValue::LetElse(letElse) => return try resolveLetElse(self, node, letElse),
3951
        case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, args),
3952
        case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign),
3953
        case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint),
3954
        case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint),
3955
        case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint),
3956
        case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index),
3957
        case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access, hint),
3958
        case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint),
3959
        case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint),
3960
        case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr),
3961
        case ast::NodeValue::Range(range) => return try resolveRange(self, node, range),
3962
        case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint),
3963
        case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value),
3964
        case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr),
3965
        case ast::NodeValue::Panic { message } => {
3966
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
3967
                class: types::PointerClass::Owned,
3968
                item: allocType(self, Type::U8),
3969
                mutable: false,
3970
            });
3971
            return setNodeType(self, node, Type::Never);
3972
        },
3973
        case ast::NodeValue::Assert { condition, message } => {
3974
            try visit(self, condition, Type::Bool);
3975
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
3976
                class: types::PointerClass::Owned,
3977
                item: allocType(self, Type::U8),
3978
                mutable: false,
3979
            });
3980
            return setNodeType(self, node, Type::Void);
3981
        },
3982
        case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop),
3983
        case ast::NodeValue::ExprStmt(expr) => {
3984
            // Pass `Void` as expected type to indicate value is discarded.
3985
            let exprTy = try visit(self, expr, Type::Void);
3986
            return setNodeType(self, node, Type::Never if exprTy == Type::Never else Type::Void);
3987
        },
3988
        case ast::NodeValue::RegionApply { value, regions } =>
3989
            return try resolveRegionApply(self, node, value, regions),
3990
        case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig),
3991
        case ast::NodeValue::Super => {
3992
            // `super` by itself is invalid, must be used in scope access.
3993
            throw emitError(self, node, ErrorKind::InvalidModulePath);
3994
        },
3995
        case ast::NodeValue::Nil => {
3996
            // Use the hint type if it's an optional, otherwise fall back to `Nil`.
3997
            if let case Type::Optional(_) = hint {
3998
                return setNodeType(self, node, hint);
3999
            }
4000
            return setNodeType(self, node, Type::Nil);
4001
        },
4002
        case ast::NodeValue::Undef => {
4003
            try requireUnsafe(self, node);
4004
            return setNodeType(self, node, Type::Undefined);
4005
        },
4006
        case ast::NodeValue::Bool(value) => {
4007
            setNodeConstValue(self, node, ConstValue::Bool(value));
4008
            return setNodeType(self, node, Type::Bool);
4009
        }
4010
        case ast::NodeValue::Char(value) => {
4011
            setNodeConstValue(self, node, ConstValue::Char(value));
4012
            return setNodeType(self, node, Type::U8);
4013
        }
4014
        case ast::NodeValue::String(text) => {
4015
            setNodeConstValue(self, node, ConstValue::String(text));
4016
            let byteTy = allocType(self, Type::U8);
4017
            let sliceTy = allocType(self, Type::Slice {
4018
                class: types::PointerClass::Owned,
4019
                item: byteTy,
4020
                mutable: false,
4021
            });
4022
            return setNodeType(self, node, *sliceTy);
4023
        },
4024
        case ast::NodeValue::Number(lit) => {
4025
            setNodeConstValue(self, node, ConstValue::Int(ConstInt {
4026
                magnitude: lit.magnitude,
4027
                bits: 64,
4028
                signed: false,
4029
                negative: false,
4030
            }));
4031
            return setNodeType(self, node, Type::Int);
4032
        },
4033
        case ast::NodeValue::Placeholder => {
4034
            return setNodeType(self, node, hint);
4035
        },
4036
        else => {
4037
            throw emitError(self, node, ErrorKind::UnexpectedNode(node));
4038
        }
4039
    }
4040
}
4041
4042
/// Visit an optional node when present.
4043
unsafe fn visitOptional 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, hint: Type) -> ?Type
4044
    throws (ResolveError)
4045
{
4046
    if let n = node {
4047
        return try visit(self, n, hint);
4048
    }
4049
    return nil;
4050
}
4051
4052
/// Visit every node contained in a list, returning the last resolved type.
4053
unsafe fn visitList 'arena (self: &mut Resolver 'arena, list: *[*ast::Node]) -> Type
4054
    throws (ResolveError)
4055
{
4056
    let mut diverges = false;
4057
    for item in list {
4058
        if try infer(self, item) == Type::Never {
4059
            set diverges = true;
4060
        }
4061
    }
4062
    if diverges {
4063
        return Type::Never;
4064
    }
4065
    return Type::Void;
4066
}
4067
4068
/// Collect attribute flags applied to a declaration.
4069
fn resolveAttributes(attrs: ?ast::Attributes) -> u32 {
4070
    let list = attrs else return 0;
4071
    let mut mask: u32 = 0;
4072
4073
    for node in list.list {
4074
        let case ast::NodeValue::Attribute(attr) = node.value
4075
            else panic "resolveAttributes: invalid attribute node";
4076
        set mask |= (attr as u32);
4077
    }
4078
    return mask;
4079
}
4080
4081
/// Ensure the `default` attribute is only applied to functions.
4082
fn ensureDefaultAttrNotAllowed 'arena (self: &mut Resolver 'arena, node: *ast::Node, attrs: u32)
4083
    throws (ResolveError)
4084
{
4085
    let defaultBit = ast::Attribute::Default as u32;
4086
    if (attrs & defaultBit) <> 0 {
4087
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
4088
    }
4089
}
4090
4091
/// Analyze a block node, allocating a nested lexical scope.
4092
unsafe fn resolveBlock 'arena (self: &mut Resolver 'arena, node: *ast::Node, block: ast::Block) -> Type
4093
    throws (ResolveError)
4094
{
4095
    enterScope(self, node);
4096
    let wasUnsafe = self.inUnsafeContext;
4097
    set self.inUnsafeContext = wasUnsafe or block.isUnsafe;
4098
    let blockTy = try visitList(self, block.statements) catch {
4099
        // One of the statements in the block failed analysis. We simply proceed
4100
        // without checking the rest of the block statements. Return `Never` to
4101
        // avoid spurious `FnMissingReturn` errors.
4102
        exitScope(self);
4103
        set self.inUnsafeContext = wasUnsafe;
4104
        return setNodeType(self, node, Type::Never);
4105
    };
4106
    exitScope(self);
4107
    set self.inUnsafeContext = wasUnsafe;
4108
4109
    return setNodeType(self, node, blockTy);
4110
}
4111
4112
/// Introduce a concrete region under an explicit parent or enclosing region block.
4113
unsafe fn borrowRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *RegionScope throws (ResolveError) {
4114
    let case ast::NodeValue::Region { name, parent } = node.value
4115
        else panic "borrowRegion: invalid region node";
4116
    if findRegion(self.regionScope, name) <> nil {
4117
        throw emitError(self, node, ErrorKind::DuplicateBinding(name));
4118
    }
4119
    let mut enclosing: ?*unsafe types::Region = nil;
4120
    let mut current = self.regionScope;
4121
    while let scope = current {
4122
        for region in scope.entries {
4123
            if region.origin == types::RegionOrigin::Block {
4124
                set enclosing = region;
4125
                break;
4126
            }
4127
        }
4128
        if enclosing <> nil {
4129
            break;
4130
        }
4131
        set current = scope.parent;
4132
    }
4133
    if let parentNode = parent {
4134
        set enclosing = try resolveRegion(self, parentNode);
4135
    }
4136
    let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
4137
        as *unsafe mut types::Region;
4138
    set *region = types::Region { id: node.id, origin: types::RegionOrigin::Block, name, parent: enclosing };
4139
    let entries = try! alloc::allocRawSlice(
4140
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1
4141
    ) as *unsafe mut [*unsafe mut types::Region];
4142
    set entries[0] = region;
4143
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
4144
    set *scope = RegionScope { declarations: RegionDeclarations::Block(node), entries, parent: self.regionScope };
4145
    return scope;
4146
}
4147
4148
/// Qualify an existing-place borrow with its block's region.
4149
unsafe fn qualifyBlockBorrow 'arena (
4150
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, region: *unsafe types::Region
4151
) -> Type throws (ResolveError) {
4152
    let mut class = types::PointerClass::Ref;
4153
    match ty {
4154
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
4155
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
4156
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
4157
        else => throw emitError(self, node, ErrorKind::RefBinding),
4158
    }
4159
    if let case types::PointerClass::Region(source) = class {
4160
        if not types::regionContains(source, region) {
4161
            throw emitError(self, node, ErrorKind::RegionParent(region.name));
4162
        }
4163
    }
4164
    match ty {
4165
        case Type::Cell { payload, .. } =>
4166
            return Type::Cell { class: types::PointerClass::Region(region), payload },
4167
        case Type::Pointer { target, mutable, .. } =>
4168
            return Type::Pointer { class: types::PointerClass::Region(region), target, mutable },
4169
        case Type::Slice { item, mutable, .. } =>
4170
            return Type::Slice { class: types::PointerClass::Region(region), item, mutable },
4171
        else => throw emitError(self, node, ErrorKind::RefBinding),
4172
    }
4173
}
4174
4175
/// Check source places before publishing the region's bindings to its body.
4176
unsafe fn resolveBorrowBlock 'arena (
4177
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
4178
    bindings: *[*ast::Node], body: *ast::Node
4179
) -> Type throws (ResolveError) {
4180
    if self.currentFn == nil {
4181
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
4182
    }
4183
    let scope = try borrowRegion(self, regionNode);
4184
    let region = scope.entries[0];
4185
    for bindingNode in bindings {
4186
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4187
            else panic "resolveBorrowBlock: invalid binding node";
4188
        let case ast::NodeValue::AddressOf(address) = binding.value.value
4189
            else panic "resolveBorrowBlock: invalid source node";
4190
        let ty = try infer(self, binding.value);
4191
        if not ast::isPlaceExpr(address.target) or borrowPlace(self, address.target).root == nil {
4192
            throw emitError(self, binding.value, ErrorKind::RefBinding);
4193
        }
4194
        let qualified = try qualifyBlockBorrow(self, binding.value, ty, region);
4195
        setNodeType(self, binding.value, qualified);
4196
    }
4197
    let previous = self.regionScope;
4198
    set self.regionScope = scope;
4199
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4200
    enterScope(self, node);
4201
    let result = try resolveBorrowBody(self, bindings, body) catch error {
4202
        exitScope(self);
4203
        set self.regionScope = previous;
4204
        throw error;
4205
    };
4206
    exitScope(self);
4207
    set self.regionScope = previous;
4208
    return setNodeType(self, node, result);
4209
}
4210
4211
/// Bind a checked region's source references and resolve its statement body.
4212
unsafe fn resolveBorrowBody 'arena (self: &mut Resolver 'arena, bindings: *[*ast::Node], body: *ast::Node) -> Type
4213
    throws (ResolveError)
4214
{
4215
    for bindingNode in bindings {
4216
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4217
            else panic "resolveBorrowBody: invalid binding node";
4218
        try resolveLet(self, bindingNode, ast::borrowBinding(binding));
4219
    }
4220
    return try infer(self, body);
4221
}
4222
4223
/// Find a declaration by spelling when the name is not interned.
4224
unsafe fn findSpelledSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
4225
    for i in 0..scope.symbolsLen {
4226
        let symbol = scope.symbols[i];
4227
        if mem::eq(symbol.name, name) {
4228
            return symbol;
4229
        }
4230
    }
4231
    return nil;
4232
}
4233
4234
/// Look up a compiler-known declaration in the standard allocation module.
4235
unsafe fn allocationSymbol 'arena (self: &Resolver 'arena, name: *[u8]) -> ?*unsafe mut Symbol {
4236
    let mut scope = self.pkgScope;
4237
    for segment in ["std", "lang", "alloc"] {
4238
        let symbol = findSpelledSymbol(scope, segment) else return nil;
4239
        let case SymbolData::Module { scope: child, .. } = symbol.data else return nil;
4240
        set scope = child;
4241
    }
4242
    return findSpelledSymbol(scope, name);
4243
}
4244
4245
/// Bind an allocation interface while retaining the source arena until region exit.
4246
unsafe fn resolveSessionBlock 'arena (
4247
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
4248
    bindings: *[*ast::Node], body: *ast::Node
4249
) -> Type throws (ResolveError) {
4250
    if self.currentFn == nil or bindings.len <> 1 {
4251
        throw emitError(self, node, ErrorKind::InvalidSessionSource);
4252
    }
4253
    let bindingNode = bindings[0];
4254
    let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4255
        else panic "resolveSessionBlock: invalid binding";
4256
    let case ast::NodeValue::AddressOf(address) = binding.value.value
4257
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4258
    let ty = try infer(self, binding.value);
4259
    let case Type::Pointer { target, .. } = ty
4260
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4261
    let allocTrait = allocationSymbol(self, "Alloc")
4262
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4263
    let case SymbolData::Trait(allocInfo) = allocTrait.data
4264
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4265
    let allocModule = moduleIdForSymbol(self, allocTrait)
4266
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4267
    let mut allocInstance: ?*unsafe InstanceEntry = nil;
4268
    for i in 0..self.instancesLen {
4269
        let candidate: *unsafe InstanceEntry = &self.instances[i];
4270
        if candidate.traitType.moduleId == allocModule and mem::eq(candidate.traitType.name, allocInfo.name)
4271
            and erasedTypesEqual(candidate.concreteType, *target)
4272
        {
4273
            set allocInstance = candidate;
4274
            break;
4275
        }
4276
    }
4277
    let selected = allocInstance
4278
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4279
    if address.kind <> ast::AddressKind::Mutable or not ast::isPlaceExpr(address.target)
4280
        or borrowPlace(self, address.target).root == nil
4281
    {
4282
        throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4283
    }
4284
    let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject {
4285
        traitInfo: allocInfo, inst: selected,
4286
    });
4287
    let scope = try borrowRegion(self, regionNode);
4288
    let region = scope.entries[0];
4289
    if let sourceRegion = referenceRegion(ty) {
4290
        set region.parent = sourceRegion;
4291
    }
4292
    setNodeType(self, binding.value, try qualifyBlockBorrow(self, binding.value, ty, region));
4293
    let previous = self.regionScope;
4294
    set self.regionScope = scope;
4295
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4296
    enterScope(self, node);
4297
    let result = try resolveSessionBody(self, bindingNode, binding, region, body) catch error {
4298
        exitScope(self);
4299
        set self.regionScope = previous;
4300
        throw error;
4301
    };
4302
    exitScope(self);
4303
    set self.regionScope = previous;
4304
    return setNodeType(self, node, result);
4305
}
4306
4307
/// Introduce the opaque session value and check its body.
4308
unsafe fn resolveSessionBody 'arena (
4309
    self: &mut Resolver 'arena, node: *ast::Node, binding: ast::Arg,
4310
    region: *unsafe types::Region, body: *ast::Node
4311
) -> Type throws (ResolveError) {
4312
    let ident = binding.label else panic "resolveSessionBody: missing binding name";
4313
    let _ = try bindValueIdent(self, ident, node, Type::Session(region), false, 0, 0);
4314
    return try infer(self, body);
4315
}
4316
4317
/// Analyze a `let` declaration and bind its identifier.
4318
unsafe fn resolveLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Let) -> Type
4319
    throws (ResolveError)
4320
{
4321
    let mut alignment: u32 = 0; // Zero is default.
4322
    let mut bindingTy = Type::Unknown;
4323
    let mut valueTy = Type::Unknown;
4324
4325
    // Check type.
4326
    if let declTy = try visitOptional(self, decl.type, Type::Unknown) {
4327
        set valueTy = try checkAssignable(self, decl.value, declTy);
4328
        set bindingTy = declTy;
4329
    } else {
4330
        set bindingTy = try infer(self, decl.value);
4331
        set valueTy = bindingTy;
4332
4333
        if not isTypeInferrable(bindingTy) {
4334
            throw emitError(self, decl.value, ErrorKind::CannotInferType);
4335
        }
4336
    }
4337
    try validateValueTypeReferences(self, node, bindingTy);
4338
    if isRefType(bindingTy) {
4339
        if self.currentFn == nil {
4340
            throw emitError(self, node, ErrorKind::InvalidRefPosition);
4341
        }
4342
        if decl.mutable and (referenceRegion(bindingTy) == nil or not isCopy(bindingTy)) {
4343
            throw emitError(self, node, ErrorKind::RefBinding);
4344
        }
4345
    }
4346
    // Variables cannot have void type.
4347
    if bindingTy == Type::Void {
4348
        throw emitError(self, decl.value, ErrorKind::CannotAssignVoid);
4349
    }
4350
    // Variables cannot have opaque type directly.
4351
    if bindingTy == Type::Opaque {
4352
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
4353
    }
4354
    // Check alignment.
4355
    if let a = decl.alignment {
4356
        let case ast::NodeValue::Align { value } = a.value
4357
            else panic "resolveLet: expected Align node";
4358
        set alignment = try checkSizeInt(self, value);
4359
    }
4360
    assert bindingTy <> Type::Unknown;
4361
4362
    // Alignment must be zero or a power of two.
4363
    if alignment <> 0 and (alignment & (alignment - 1)) <> 0 {
4364
        throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment));
4365
    }
4366
    let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0);
4367
4368
    // Untyped initializers use the declared storage type.
4369
    if not isTypeInferrable(valueTy) {
4370
        setNodeType(self, decl.value, bindingTy);
4371
    }
4372
4373
    return Type::Never if valueTy == Type::Never else Type::Void;
4374
}
4375
4376
/// Check whether a node is an integer literal, optionally under unary negation.
4377
fn isIntegerLiteralExpr(node: *ast::Node) -> bool {
4378
    match node.value {
4379
        case ast::NodeValue::Number(_) => return true,
4380
        case ast::NodeValue::UnOp(unop) => {
4381
            if unop.op == ast::UnaryOp::Neg {
4382
                return isIntegerLiteralExpr(unop.value);
4383
            }
4384
            return false;
4385
        },
4386
        else => return false,
4387
    }
4388
}
4389
4390
/// Determine whether a node represents a compile-time constant expression.
4391
export unsafe fn isConstExpr 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
4392
    match node.value {
4393
        case ast::NodeValue::Bool(_),
4394
             ast::NodeValue::Char(_),
4395
             ast::NodeValue::Number(_),
4396
             ast::NodeValue::String(_),
4397
             ast::NodeValue::Undef,
4398
             ast::NodeValue::Nil => {
4399
            return true;
4400
        },
4401
        case ast::NodeValue::ArrayLit(items) => {
4402
            for item in items {
4403
                if not isConstExpr(self, item) {
4404
                    return false;
4405
                }
4406
            }
4407
            return true;
4408
        },
4409
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
4410
            return isConstExpr(self, repeat.item);
4411
        },
4412
        case ast::NodeValue::AddressOf(addr) => {
4413
            let ty = typeFor(self, node) else {
4414
                return false;
4415
            };
4416
            if let case Type::Slice { .. } = ty {
4417
                return isConstExpr(self, addr.target);
4418
            }
4419
            return false;
4420
        },
4421
        case ast::NodeValue::RecordLit(lit) => {
4422
            // Record literals are constant if all field values are constant.
4423
            for field in lit.fields {
4424
                if let case ast::NodeValue::RecordLitField(fieldLit) = field.value {
4425
                    if not isConstExpr(self, fieldLit.value) {
4426
                        return false;
4427
                    }
4428
                }
4429
            }
4430
            return true;
4431
        },
4432
        case ast::NodeValue::Ident(_),
4433
             ast::NodeValue::ScopeAccess(_) => {
4434
            // Identifiers and scope accesses referencing constants, union
4435
            // variants, or function values are constant expressions.
4436
            if let sym = symbolFor(self, node) {
4437
                match sym.data {
4438
                    case SymbolData::Variant { .. },
4439
                         SymbolData::Constant { .. } => return true,
4440
                    case SymbolData::Value { type, .. } => {
4441
                        if let case Type::Fn(_) = type {
4442
                            return true;
4443
                        }
4444
                    }
4445
                    else => {}
4446
                }
4447
            }
4448
            return false;
4449
        },
4450
        case ast::NodeValue::Call(call) => {
4451
            // Constructor calls (union variants, unlabeled records) are constant
4452
            // if all payload args are themselves constant.
4453
            if let sym = symbolFor(self, call.callee) {
4454
                match sym.data {
4455
                    case SymbolData::Variant { .. } => {}
4456
                    case SymbolData::Type(NominalType::Record(recInfo)) => {
4457
                        if recInfo.labeled {
4458
                            return false;
4459
                        }
4460
                    },
4461
                    else => return false,
4462
                }
4463
                for arg in call.args {
4464
                    if not isConstExpr(self, arg) {
4465
                        return false;
4466
                    }
4467
                }
4468
                return true;
4469
            }
4470
            return false;
4471
        },
4472
        case ast::NodeValue::BinOp(binop) => {
4473
            // Binary expressions are constant if both operands are constant.
4474
            return isConstExpr(self, binop.left) and isConstExpr(self, binop.right);
4475
        },
4476
        case ast::NodeValue::UnOp(unop) => {
4477
            // Unary expressions are constant if the operand is constant.
4478
            return isConstExpr(self, unop.value);
4479
        },
4480
        case ast::NodeValue::As(expr) => {
4481
            // Cast expressions are constant if the source value is constant.
4482
            return isConstExpr(self, expr.value);
4483
        },
4484
        else => {
4485
            return false;
4486
        }
4487
    }
4488
}
4489
4490
/// Construct an integer constant descriptor.
4491
fn constInt(magnitude: u64, bits: u8, signed: bool, negative: bool) -> ConstValue {
4492
    return ConstValue::Int(ConstInt { magnitude, bits, signed, negative });
4493
}
4494
4495
/// Apply an integer cast to a constant value, including target-width
4496
/// truncation and signed interpretation.
4497
fn castConstInt(value: ConstInt, target: Type) -> ConstValue {
4498
    let raw = constIntToBits(value);
4499
    let range = integerRange(target)
4500
        else panic "castConstInt: expected integer type";
4501
4502
    match range {
4503
        case IntegerRange::Unsigned { bits, .. } =>
4504
            return ConstValue::Int(constIntFromBits(raw, bits, false)),
4505
        case IntegerRange::Signed { bits, .. } =>
4506
            return ConstValue::Int(constIntFromBits(raw, bits, true)),
4507
    }
4508
}
4509
4510
/// Return the constant `u32` value for a slice bound when known.
4511
fn constSliceIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?u32 {
4512
    let value = constValueEntry(self, node)
4513
        else return nil;
4514
    let case ConstValue::Int(int) = value
4515
        else return nil;
4516
    if int.negative {
4517
        return nil;
4518
    }
4519
    return int.magnitude as u32;
4520
}
4521
4522
/// Validates and extracts a non-negative integer constant from a compile-time expression.
4523
///
4524
/// This function ensures that a node represents a valid, non-negative integer constant
4525
/// that fits within a machine word. It is used for contexts requiring compile-time
4526
/// non-negative integers, such as array sizes and alignment specifications.
4527
///
4528
/// Returns the unsigned magnitude of the constant as `u32`.
4529
unsafe fn checkSizeInt 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> u32
4530
    throws (ResolveError)
4531
{
4532
    // First traverse the node expect a numeric type.
4533
    let _ = try checkNumeric(self, node);
4534
4535
    // Look up the compile-time constant value associated with this node.
4536
    let value = constValueEntry(self, node)
4537
        else throw emitError(self, node, ErrorKind::ConstExprRequired);
4538
4539
    let case ConstValue::Int(int) = value
4540
        else panic "checkSizeInt: expected integer constant";
4541
4542
    // Validate it fits within u32 range.
4543
    if not validateConstIntRange(value, Type::U32) {
4544
        throw emitError(self, node, ErrorKind::NumericLiteralOverflow);
4545
    }
4546
    assert not int.negative;
4547
    setNodeType(self, node, Type::U32);
4548
4549
    return int.magnitude as u32;
4550
}
4551
4552
/// Check that constructor arguments match record fields.
4553
///
4554
/// Verifies argument count matches field count, and that each argument is
4555
/// assignable to its corresponding field type.
4556
unsafe fn checkRecordConstructorArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType)
4557
    throws (ResolveError)
4558
{
4559
    try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: args.len }, node);
4560
    for arg, i in args {
4561
        let fieldType = recInfo.fields[i].fieldType;
4562
        try checkAssignable(self, arg, fieldType);
4563
    }
4564
}
4565
4566
/// Check that the argument count of a constructor pattern or call matches the record field count.
4567
fn checkRecordArity 'arena (self: &mut Resolver 'arena, counts: CountMismatch, pattern: *ast::Node) throws (ResolveError) {
4568
    if counts.actual <> counts.expected {
4569
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(counts));
4570
    }
4571
}
4572
4573
/// Helper for analyzing `constant` and `static` declarations.
4574
unsafe fn resolveConstOrStatic 'arena (
4575
    self: &mut Resolver 'arena,
4576
    node: *ast::Node,
4577
    ident: *ast::Node,
4578
    typeNode: *ast::Node,
4579
    valueNode: *ast::Node,
4580
    attrList: ?ast::Attributes,
4581
    isConst: bool
4582
) -> Type throws (ResolveError) {
4583
    let attrs = resolveAttributes(attrList);
4584
    let bindingTy = try infer(self, typeNode);
4585
    if containsRegion(bindingTy) {
4586
        throw emitError(self, typeNode, ErrorKind::InvalidRefPosition);
4587
    }
4588
    try ensureStorableType(self, typeNode, bindingTy);
4589
    let wasUnsafe = self.inUnsafeContext;
4590
    set self.inUnsafeContext = wasUnsafe or (
4591
        not isConst and ast::hasAttribute(attrs, ast::Attribute::Unsafe)
4592
    );
4593
    let valueTy = try checkAssignable(self, valueNode, bindingTy) catch e {
4594
        set self.inUnsafeContext = wasUnsafe;
4595
        throw e;
4596
    };
4597
    set self.inUnsafeContext = wasUnsafe;
4598
4599
    if isConst {
4600
        let mut constVal = constValueEntry(self, valueNode);
4601
        if constVal == nil and not isConstExpr(self, valueNode) {
4602
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
4603
        }
4604
        if let val = constVal {
4605
            if let case ConstValue::Int(int) = val; isNumericType(bindingTy) {
4606
                set constVal = castConstInt(int, bindingTy);
4607
            }
4608
        }
4609
        try bindConstIdent(self, ident, node, bindingTy, constVal, attrs);
4610
    } else {
4611
        if not isConstExpr(self, valueNode) {
4612
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
4613
        }
4614
        try bindValueIdent(self, ident, node, bindingTy, true, 0, attrs);
4615
    }
4616
    setNodeType(self, valueNode, bindingTy);
4617
4618
    return Type::Void;
4619
}
4620
4621
/// Analyze a function declaration signature and bind the function name.
4622
unsafe fn resolveFnDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
4623
    throws (ResolveError)
4624
{
4625
    let previous = self.regionScope;
4626
    set self.regionScope = try bindRegions(self, node, decl.regions);
4627
    let result = try resolveFnSignature(self, node, decl) catch error {
4628
        set self.regionScope = previous;
4629
        throw error;
4630
    };
4631
    set self.regionScope = previous;
4632
    return result;
4633
}
4634
4635
/// Resolve a function signature in its declared region environment.
4636
unsafe fn resolveFnSignature 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
4637
    throws (ResolveError)
4638
{
4639
    let attrMask = resolveAttributes(decl.attrs);
4640
    let mut retTy = Type::Void;
4641
    if let retNode = decl.sig.returnType {
4642
        set retTy = try infer(self, retNode);
4643
        try ensureStorableType(self, retNode, retTy);
4644
    }
4645
    let a = alloc::arenaAllocator(self.arena);
4646
    let mut paramTypes: *mut [*Type] = &mut [];
4647
    let mut throwList: *mut [*Type] = &mut [];
4648
    let mut fnType = FnType {
4649
        regions: self.regionScope,
4650
        paramTypes: &[],
4651
        returnType: allocType(self, retTy),
4652
        throwList: &[],
4653
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
4654
    };
4655
    // Enter the function scope to process parameters.
4656
    enterFn(self, node, &fnType);
4657
4658
    if decl.sig.params.len > MAX_FN_PARAMS {
4659
        exitFn(self);
4660
        throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
4661
            expected: MAX_FN_PARAMS,
4662
            actual: decl.sig.params.len,
4663
        }));
4664
    }
4665
    for paramNode in decl.sig.params {
4666
        let paramTy = try infer(self, paramNode) catch e {
4667
            exitFn(self);
4668
            throw e;
4669
        };
4670
        paramTypes.append(allocType(self, paramTy), a);
4671
    }
4672
4673
    if decl.sig.throwList.len > MAX_FN_THROWS {
4674
        exitFn(self);
4675
        throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
4676
            expected: MAX_FN_THROWS,
4677
            actual: decl.sig.throwList.len,
4678
        }));
4679
    }
4680
    for throwNode in decl.sig.throwList {
4681
        let throwTy = try infer(self, throwNode) catch e {
4682
            exitFn(self);
4683
            throw e;
4684
        };
4685
        try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
4686
        throwList.append(allocType(self, throwTy), a);
4687
        try ensureStorableType(self, throwNode, throwTy);
4688
    }
4689
    exitFn(self);
4690
    set fnType.paramTypes = &paramTypes[..];
4691
    set fnType.throwList = &throwList[..];
4692
4693
    // Bind the function name.
4694
    let ty = Type::Fn(allocFnType(self, fnType));
4695
    let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask)
4696
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
4697
4698
    return ty;
4699
}
4700
4701
/// Analyze a function body.
4702
unsafe fn resolveFnDeclBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
4703
    let sym = symbolFor(self, node) else {
4704
        // The function declaration failed to type check, therefore
4705
        // no symbol was associated with it.
4706
        return;
4707
    };
4708
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
4709
        panic "resolveFnDeclBody: unexpected symbol data for function";
4710
    };
4711
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
4712
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
4713
4714
    if let body = decl.body {
4715
        if isIntrinsic {
4716
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
4717
        }
4718
        if isExtern {
4719
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
4720
        }
4721
        let previous = self.regionScope;
4722
        set self.regionScope = try bindRegions(self, node, decl.regions);
4723
        try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body) catch error {
4724
            set self.regionScope = previous;
4725
            throw error;
4726
        };
4727
        set self.regionScope = previous;
4728
    } else if not isExtern {
4729
        throw emitError(self, node, ErrorKind::FnMissingBody);
4730
    }
4731
}
4732
4733
/// Resolve a function or method body and restore the enclosing context.
4734
unsafe fn resolveExecutableBody 'arena (
4735
    self: &mut Resolver 'arena,
4736
    node: *ast::Node,
4737
    fnType: *FnType,
4738
    receiverName: ?*ast::Node,
4739
    params: *[*ast::Node],
4740
    body: *ast::Node,
4741
) throws (ResolveError) {
4742
    let wasUnsafe = self.inUnsafeContext;
4743
    set self.inUnsafeContext = fnType.isUnsafe;
4744
    // Enter function scope.
4745
    enterFn(self, node, fnType); // Enter function scope for body analysis.
4746
4747
    let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e {
4748
        exitFn(self);
4749
        set self.inUnsafeContext = wasUnsafe;
4750
        throw e;
4751
    };
4752
    exitFn(self);
4753
    set self.inUnsafeContext = wasUnsafe;
4754
    if missingReturn {
4755
        throw emitError(self, body, ErrorKind::FnMissingReturn);
4756
    }
4757
}
4758
4759
/// Check parameters, body types, and ownership.
4760
/// Return whether a required return is missing.
4761
unsafe fn checkExecutableBody 'arena (
4762
    self: &mut Resolver 'arena,
4763
    fnType: *FnType,
4764
    receiverName: ?*ast::Node,
4765
    params: *[*ast::Node],
4766
    body: *ast::Node,
4767
) -> bool throws (ResolveError) {
4768
    if let receiver = receiverName {
4769
        // Bind the receiver parameter.
4770
        let receiverTy = *fnType.paramTypes[0];
4771
        try bindValueIdent(self, receiver, receiver, receiverTy, false, 0, 0);
4772
        // Bind the remaining parameters from the signature.
4773
        for paramNode in params {
4774
            let paramTy = try infer(self, paramNode);
4775
        }
4776
    }
4777
    // Resolve the body.
4778
    let retTy = *fnType.returnType;
4779
    let bodyTy = try checkAssignable(self, body, Type::Void);
4780
    if retTy <> Type::Void and bodyTy <> Type::Never {
4781
        return true;
4782
    }
4783
    // Ownership checks require complete type and call metadata.
4784
    if self.errors.len == 0 {
4785
        try checkLinearFn(self, receiverName, params, body);
4786
    }
4787
    return false;
4788
}
4789
4790
/// Analyze a function parameter and bind its identifier.
4791
unsafe fn resolveFnParam 'arena (self: &mut Resolver 'arena, node: *ast::Node, param: ast::FnParam) -> Type
4792
    throws (ResolveError)
4793
{
4794
    let ty = try resolveValueType(self, param.type);
4795
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
4796
4797
    return ty;
4798
}
4799
4800
/// Compiler-known ownership markers carried by a composite declaration.
4801
record OwnershipMarkers: Copy {
4802
    /// The declaration requires exact consumption.
4803
    linear: bool,
4804
    /// The declaration permits implicit copies.
4805
    copy: bool,
4806
}
4807
4808
/// Resolve compiler-known ownership markers from a derive list.
4809
unsafe fn resolveOwnershipMarkers 'arena (self: &mut Resolver 'arena, derives: *[*ast::Node]) -> OwnershipMarkers
4810
    throws (ResolveError)
4811
{
4812
    let mut result = OwnershipMarkers { linear: false, copy: false };
4813
    for derive in derives {
4814
        if let case ast::NodeValue::Region { .. } = derive.value {
4815
            continue;
4816
        }
4817
        let name = try nodeName(self, derive);
4818
        if mem::eq(name, "Once") {
4819
            if result.linear {
4820
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
4821
            }
4822
            if result.copy {
4823
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
4824
            }
4825
            set result.linear = true;
4826
        } else if mem::eq(name, "Copy") {
4827
            if result.copy {
4828
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
4829
            }
4830
            if result.linear {
4831
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
4832
            }
4833
            set result.copy = true;
4834
        } else {
4835
            // Resolve an ordinary trait derive.
4836
            try infer(self, derive);
4837
        }
4838
    }
4839
    return result;
4840
}
4841
4842
/// Resolve record fields from a node list.
4843
unsafe fn resolveRecordFields 'arena (self: &mut Resolver 'arena, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
4844
    throws (ResolveError)
4845
{
4846
    let a = alloc::arenaAllocator(self.arena);
4847
    let mut result: *mut [RecordField] = &mut [];
4848
    let mut layout = Layout { size: 0, alignment: 1 };
4849
4850
    if fields.len > parser::MAX_RECORD_FIELDS {
4851
        throw emitError(self, node, ErrorKind::Internal);
4852
    }
4853
    for field in fields {
4854
        let case ast::NodeValue::RecordField {
4855
            field: fieldNode,
4856
            type: typeNode,
4857
            value: valueNode
4858
        } = field.value else panic "resolveRecordFields: invalid record field";
4859
        let fieldTy = try resolveValueType(self, typeNode);
4860
        try ensureStorableType(self, typeNode, fieldTy);
4861
4862
        if let v = valueNode {
4863
            let _valTy = try checkAssignable(self, v, fieldTy);
4864
        }
4865
        // Get field name for labeled records.
4866
        let mut fieldName: ?*[u8] = nil;
4867
        if labeled {
4868
            let n = fieldNode
4869
                else panic "resolveRecordFields: labeled record field missing name";
4870
            set fieldName = try nodeName(self, n);
4871
        }
4872
        let fieldType = typeFor(self, typeNode)
4873
            else throw emitError(self, typeNode, ErrorKind::CannotInferType);
4874
4875
        // Ensure field type is fully resolved before computing layout.
4876
        try ensureTypeResolved(self, fieldType, typeNode);
4877
4878
        appendRecordField(&mut result, &mut layout, fieldName, fieldType, a);
4879
    }
4880
    // Compute cached layout.
4881
    let recordLayout = Layout {
4882
        size: mem::alignUp(layout.size, layout.alignment),
4883
        alignment: layout.alignment
4884
    };
4885
    return RecordType {
4886
        regions: nil,
4887
        application: nil,
4888
        fields: (&result[..]) as *unsafe [RecordField],
4889
        labeled,
4890
        layout: allocLayout(self, recordLayout),
4891
        declaredLinear: false,
4892
        declaredCopy: false,
4893
    };
4894
}
4895
4896
/// Append an owned record field and update the layout before tail padding.
4897
fn appendRecordField(fields: &mut *mut [RecordField], layout: &mut Layout, name: ?*[u8], fieldType: Type, allocator: alloc::Allocator) {
4898
    // Compute field offset by aligning to field's alignment.
4899
    let fieldLayout = typeLayout(fieldType);
4900
    let offset = mem::alignUp(layout.size, fieldLayout.alignment);
4901
    fields.append(RecordField { name, fieldType, offset: offset as i32 }, allocator);
4902
4903
    // Advance offset past this field.
4904
    set layout.size = offset + fieldLayout.size;
4905
4906
    // Track max alignment for record layout.
4907
    set layout.alignment = max(layout.alignment, fieldLayout.alignment);
4908
}
4909
4910
/// Resolve record field types for a named record declaration.
4911
unsafe fn resolveRecordBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
4912
    throws (ResolveError)
4913
{
4914
    let previous = self.regionScope;
4915
    set self.regionScope = try bindRegions(self, node, decl.regions);
4916
    try resolveRecordContents(self, node, decl) catch error {
4917
        set self.regionScope = previous;
4918
        throw error;
4919
    };
4920
    set self.regionScope = previous;
4921
}
4922
4923
/// Resolve record contents in the declaration's region environment.
4924
unsafe fn resolveRecordContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
4925
    throws (ResolveError)
4926
{
4927
    // Get the type symbol that was bound to this declaration node.
4928
    // If there's no symbol, it's because an earlier phase failed.
4929
    let sym = symbolFor(self, node)
4930
        else return;
4931
    let case SymbolData::Type(nominalTy) = sym.data
4932
        else panic "resolveRecordBody: unexpected type symbol data";
4933
4934
    // Skip if already resolved.
4935
    if let case NominalType::Record(_) = *nominalTy {
4936
        return;
4937
    }
4938
    let markers = try resolveOwnershipMarkers(self, decl.derives);
4939
    set *nominalTy = NominalType::Resolving(node);
4940
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled) catch error {
4941
        set *nominalTy = NominalType::Placeholder(node);
4942
        throw error;
4943
    };
4944
    set recordType.regions = self.regionScope;
4945
    if markers.copy {
4946
        for field in recordType.fields {
4947
            if not isCopy(field.fieldType) {
4948
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
4949
            }
4950
        }
4951
    }
4952
    set recordType.declaredLinear = markers.linear;
4953
    set recordType.declaredCopy = markers.copy;
4954
4955
    set *nominalTy = NominalType::Record(recordType);
4956
}
4957
4958
/// Bind a type name.
4959
unsafe fn bindTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
4960
    throws (ResolveError)
4961
{
4962
    let attrMask = resolveAttributes(attrs);
4963
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
4964
4965
    // Create a placeholder nominal type that will be replaced in
4966
    // the next phase.
4967
    let nominalTy = allocNominalType(self, NominalType::Placeholder(node));
4968
4969
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
4970
}
4971
4972
/// Allocate a trait type descriptor and return a pointer to it.
4973
unsafe fn allocTraitType 'arena (self: &mut Resolver 'arena, name: *[u8]) -> *unsafe mut TraitType {
4974
    let p = try! alloc::allocRaw(self.arena, @sizeOf(TraitType), @alignOf(TraitType));
4975
    let entry = p as *unsafe mut TraitType;
4976
    set *entry = TraitType { name, moduleId: self.currentMod, methods: &mut [], supertraits: &mut [] };
4977
4978
    return entry;
4979
}
4980
4981
/// Bind a trait name in the current scope.
4982
unsafe fn bindTraitName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
4983
    throws (ResolveError)
4984
{
4985
    let attrMask = resolveAttributes(attrs);
4986
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
4987
4988
    let traitName = try nodeName(self, name);
4989
    let traitType = allocTraitType(self, traitName);
4990
    let data = SymbolData::Trait(traitType);
4991
    let scope = self.scope;
4992
    let sym = try bindIdent(self, traitName, node, data, attrMask, scope);
4993
4994
    setNodeType(self, node, Type::Void);
4995
    setNodeType(self, name, Type::Void);
4996
4997
    return sym;
4998
}
4999
5000
/// Find a trait method by name and return its resolved metadata.
5001
export fn findTraitMethod(methods: &[TraitMethod], name: *[u8]) -> ?TraitMethod {
5002
    for method in methods {
5003
        if mem::eq(method.name, name) {
5004
            return method;
5005
        }
5006
    }
5007
    return nil;
5008
}
5009
5010
/// Resolve a trait declaration body: supertrait methods, then own methods.
5011
unsafe fn resolveTraitBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
5012
    throws (ResolveError)
5013
{
5014
    let sym = symbolFor(self, node)
5015
        else return;
5016
    let case SymbolData::Trait(traitType) = sym.data
5017
        else return;
5018
    if traitType.methods.len > 0 {
5019
        return;
5020
    }
5021
5022
    // Resolve supertrait bounds and copy their methods into this trait.
5023
    for superNode in supertraits {
5024
        let superSym = try resolveNamePath(self, superNode);
5025
        let case SymbolData::Trait(superTrait) = superSym.data
5026
            else throw emitError(self, superNode, ErrorKind::Internal);
5027
        // Trait bodies are otherwise resolved in source order. Recursively
5028
        // resolve a supertrait only when it is declared later.
5029
        if superSym.node.id > node.id {
5030
            let case ast::NodeValue::TraitDecl {
5031
                supertraits: inheritedTraits, methods: inheritedMethods, ..
5032
            } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal);
5033
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
5034
        }
5035
5036
        setNodeSymbol(self, superNode, superSym);
5037
5038
        let a = alloc::arenaAllocator(self.arena);
5039
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
5040
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
5041
                expected: ast::MAX_TRAIT_METHODS,
5042
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
5043
            }));
5044
        }
5045
        // Copy inherited methods into this trait's method table.
5046
        for inherited in superTrait.methods {
5047
            if let _ = findTraitMethod(&traitType.methods[..], inherited.name) {
5048
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
5049
            }
5050
            traitType.methods.append(TraitMethod {
5051
                name: inherited.name,
5052
                fnType: inherited.fnType,
5053
                mutable: inherited.mutable,
5054
                receiverClass: inherited.receiverClass,
5055
                index: traitType.methods.len as u32,
5056
            }, a);
5057
        }
5058
        traitType.supertraits.append(superTrait, a);
5059
    }
5060
5061
    if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS {
5062
        throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
5063
            expected: ast::MAX_TRAIT_METHODS,
5064
            actual: traitType.methods.len as u32 + methods.len as u32,
5065
        }));
5066
    }
5067
5068
    for methodNode in methods {
5069
        let case ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } = methodNode.value
5070
            else continue;
5071
        let attrs = modifiers.attrs;
5072
        let methodName = try nodeName(self, name);
5073
        let attrMask = resolveAttributes(attrs);
5074
        let previousRegions = self.regionScope;
5075
        set self.regionScope = try bindRegions(self, methodNode, modifiers.regions);
5076
5077
        // Reject duplicate method names.
5078
        if let _ = findTraitMethod(&traitType.methods[..], methodName) {
5079
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
5080
        }
5081
        // Determine the receiver class and mutability, and validate that it
5082
        // points to the declaring trait.
5083
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
5084
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5085
        let case ast::TypeSig::Pointer {
5086
            class: receiverSyntax, valueType: receiverValueType, mutable,
5087
        } = typeSig
5088
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5089
        let receiverClass = resolvePointerClass(receiverSyntax);
5090
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
5091
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5092
        let case ast::TypeSig::Nominal(nameNode) = innerSig
5093
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5094
        let receiverTargetName = try nodeName(self, nameNode);
5095
5096
        if receiverTargetName <> traitType.name {
5097
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5098
        }
5099
        // Resolve parameter types and return type.
5100
        let a = alloc::arenaAllocator(self.arena);
5101
        let mut paramTypes: *mut [*Type] = &mut [];
5102
        let mut throwList: *mut [*Type] = &mut [];
5103
        let mut retType = allocType(self, Type::Void);
5104
5105
        if sig.params.len > MAX_FN_PARAMS {
5106
            throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch {
5107
                expected: MAX_FN_PARAMS,
5108
                actual: sig.params.len,
5109
            }));
5110
        }
5111
        for paramNode in sig.params {
5112
            let case ast::NodeValue::FnParam(param) = paramNode.value
5113
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5114
            let paramTy = try resolveValueType(self, param.type);
5115
            paramTypes.append(allocType(self, paramTy), a);
5116
        }
5117
        if let ret = sig.returnType {
5118
            set retType = allocType(self, try infer(self, ret));
5119
        }
5120
        // Resolve throws list.
5121
        if sig.throwList.len > MAX_FN_THROWS {
5122
            throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch {
5123
                expected: MAX_FN_THROWS,
5124
                actual: sig.throwList.len,
5125
            }));
5126
        }
5127
        for throwNode in sig.throwList {
5128
            let throwTy = try infer(self, throwNode);
5129
            try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
5130
            throwList.append(allocType(self, throwTy), a);
5131
        }
5132
        let fnType = FnType {
5133
            regions: self.regionScope,
5134
            paramTypes: &paramTypes[..],
5135
            returnType: retType,
5136
            throwList: &throwList[..],
5137
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
5138
        };
5139
        traitType.methods.append(TraitMethod {
5140
            name: methodName,
5141
            fnType: allocFnType(self, fnType),
5142
            mutable,
5143
            receiverClass,
5144
            index: traitType.methods.len as u32,
5145
        }, a);
5146
5147
        setNodeType(self, methodNode, Type::Void);
5148
        set self.regionScope = previousRegions;
5149
    }
5150
}
5151
5152
/// Resolve a name path node to a symbol.
5153
/// Used for trait and type references in instance declarations and trait objects.
5154
unsafe fn resolveNamePath 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe mut Symbol
5155
    throws (ResolveError)
5156
{
5157
    match node.value {
5158
        case ast::NodeValue::Ident(name) => {
5159
            let sym = findAnySymbol(self.scope, name)
5160
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
5161
            return sym;
5162
        }
5163
        case ast::NodeValue::ScopeAccess(access) => {
5164
            let scope = self.scope;
5165
            return try resolveAccess(self, node, access, scope);
5166
        }
5167
        else => {
5168
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
5169
        }
5170
    }
5171
}
5172
5173
/// Join instance and method region parameters into one function binder.
5174
unsafe fn instanceMethodRegions 'arena (
5175
    self: &mut Resolver 'arena, instanceRegions: *[*ast::Node], methodRegions: *[*ast::Node]
5176
) -> *[*ast::Node] {
5177
    let count = instanceRegions.len + methodRegions.len;
5178
    if count == 0 {
5179
        return &[];
5180
    }
5181
    let allocator = alloc::arenaAllocator(self.arena);
5182
    let mut nodes: *mut [*ast::Node] = &mut [];
5183
    for region in instanceRegions {
5184
        nodes.append(region, allocator);
5185
    }
5186
    for region in methodRegions {
5187
        nodes.append(region, allocator);
5188
    }
5189
    return &nodes[..];
5190
}
5191
5192
/// Map one region binder to a contiguous part of another binder.
5193
unsafe fn mapRegionScopes 'arena (
5194
    self: &mut Resolver 'arena, source: ?*RegionScope, target: ?*RegionScope,
5195
    offset: u32, site: *ast::Node
5196
) -> ?RegionSubstitution throws (ResolveError) {
5197
    let sourceScope = source else return nil;
5198
    let targetScope = target else throw emitError(self, site, ErrorKind::Internal);
5199
    if offset + sourceScope.entries.len > targetScope.entries.len {
5200
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
5201
            expected: sourceScope.entries.len,
5202
            actual: targetScope.entries.len - offset,
5203
        }));
5204
    }
5205
    let map = regionSubstitution(self, sourceScope);
5206
    for _, i in sourceScope.entries {
5207
        set map.arguments[i] = targetScope.entries[offset + i];
5208
    }
5209
    try validateRegionArguments(self, &map, site);
5210
    return map;
5211
}
5212
5213
/// Resolved implementation of one trait method.
5214
record ResolvedInstanceMethod: Copy {
5215
    /// Canonical trait method.
5216
    method: TraitMethod,
5217
    /// Concrete function symbol.
5218
    symbol: *unsafe mut Symbol,
5219
}
5220
5221
/// Shared declaration state for instance method resolution.
5222
record InstanceMethodContext: Copy {
5223
    /// Implemented trait.
5224
    traitInfo: *unsafe TraitType,
5225
    /// Instance target with declaration regions applied.
5226
    concreteType: Type,
5227
    /// Instance region nodes in declaration order.
5228
    regions: *[*ast::Node],
5229
    /// Bound instance region scope.
5230
    scope: ?*RegionScope,
5231
}
5232
5233
/// Resolve one instance method in its combined region environment.
5234
unsafe fn resolveInstanceMethod 'arena (
5235
    self: &mut Resolver 'arena, methodNode: *ast::Node,
5236
    context: &InstanceMethodContext
5237
) -> ResolvedInstanceMethod throws (ResolveError) {
5238
    let case ast::NodeValue::MethodDecl {
5239
        name, modifiers, receiverType, sig, ..
5240
    } = methodNode.value else panic "resolveInstanceMethod: invalid method";
5241
    let combinedRegions = instanceMethodRegions(self, context.regions, modifiers.regions);
5242
    let methodScope = try bindRegions(self, methodNode, combinedRegions);
5243
    set self.regionScope = methodScope;
5244
5245
    let methodName = try nodeName(self, name);
5246
    let attrMask = resolveAttributes(modifiers.attrs);
5247
    let tm = findTraitMethod(&context.traitInfo.methods[..], methodName)
5248
        else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
5249
    if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe {
5250
        throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
5251
    }
5252
5253
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer {
5254
        class: receiverSyntax, valueType, mutable: receiverMut,
5255
    }) = receiverType.value else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5256
    let receiverClass = resolvePointerClass(receiverSyntax);
5257
    if receiverClass <> tm.receiverClass {
5258
        throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5259
    }
5260
    let annotatedTy = try infer(self, valueType);
5261
    let mut expectedConcrete = context.concreteType;
5262
    if let map = try mapRegionScopes(self, context.scope, methodScope, 0, methodNode) {
5263
        set expectedConcrete = substituteRegions(self, &map, context.concreteType);
5264
    }
5265
    if not typesEqual(annotatedTy, expectedConcrete) {
5266
        throw emitTypeMismatch(self, receiverType, TypeMismatch { expected: expectedConcrete, actual: annotatedTy });
5267
    }
5268
    if tm.mutable and not receiverMut {
5269
        throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
5270
    }
5271
    if receiverMut and not tm.mutable {
5272
        throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
5273
    }
5274
5275
    let mut traitFn = tm.fnType;
5276
    let mut traitRegionCount: u32 = 0;
5277
    if let traitScope = tm.fnType.regions {
5278
        set traitRegionCount = traitScope.entries.len;
5279
    }
5280
    if traitRegionCount <> modifiers.regions.len {
5281
        throw emitError(self, methodNode, ErrorKind::RegionArgumentCount(CountMismatch {
5282
            expected: traitRegionCount, actual: modifiers.regions.len,
5283
        }));
5284
    }
5285
    if let map = try mapRegionScopes(self, tm.fnType.regions, methodScope, context.regions.len, methodNode) {
5286
        set traitFn = substituteFnRegions(self, &map, tm.fnType, nil);
5287
    }
5288
    if sig.params.len <> traitFn.paramTypes.len {
5289
        throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
5290
            expected: traitFn.paramTypes.len, actual: sig.params.len,
5291
        }));
5292
    }
5293
5294
    let allocator = alloc::arenaAllocator(self.arena);
5295
    let mut paramTypes: *mut [*Type] = &mut [];
5296
    let receiverPtrType = Type::Pointer {
5297
        class: receiverClass, target: allocType(self, annotatedTy), mutable: receiverMut,
5298
    };
5299
    paramTypes.append(allocType(self, receiverPtrType), allocator);
5300
    for paramNode, i in sig.params {
5301
        let case ast::NodeValue::FnParam(param) = paramNode.value
5302
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5303
        let instanceParamTy = try resolveValueType(self, param.type);
5304
        if not typesEqual(instanceParamTy, *traitFn.paramTypes[i]) {
5305
            throw emitTypeMismatch(self, paramNode, TypeMismatch {
5306
                expected: *traitFn.paramTypes[i], actual: instanceParamTy,
5307
            });
5308
        }
5309
        paramTypes.append(allocType(self, instanceParamTy), allocator);
5310
    }
5311
    let mut returnType = Type::Void;
5312
    if let returnNode = sig.returnType {
5313
        set returnType = try resolveValueType(self, returnNode);
5314
    }
5315
    if not typesEqual(returnType, *traitFn.returnType) {
5316
        throw emitTypeMismatch(self, methodNode, TypeMismatch {
5317
            expected: *traitFn.returnType, actual: returnType,
5318
        });
5319
    }
5320
    if sig.throwList.len <> traitFn.throwList.len {
5321
        throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
5322
            expected: traitFn.throwList.len, actual: sig.throwList.len,
5323
        }));
5324
    }
5325
    let mut throwList: *mut [*Type] = &mut [];
5326
    for throwNode, i in sig.throwList {
5327
        let throwType = try resolveValueType(self, throwNode);
5328
        if not typesEqual(throwType, *traitFn.throwList[i]) {
5329
            throw emitTypeMismatch(self, throwNode, TypeMismatch {
5330
                expected: *traitFn.throwList[i], actual: throwType,
5331
            });
5332
        }
5333
        throwList.append(allocType(self, throwType), allocator);
5334
    }
5335
5336
    let fnType = FnType {
5337
        regions: methodScope, paramTypes: &paramTypes[..],
5338
        returnType: allocType(self, returnType), throwList: &throwList[..],
5339
        isUnsafe: tm.fnType.isUnsafe,
5340
    };
5341
    let fnTy = Type::Fn(allocFnType(self, fnType));
5342
    let sym = allocSymbol(self, SymbolData::Value {
5343
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
5344
    }, methodName, methodNode, attrMask);
5345
    setNodeSymbol(self, methodNode, sym);
5346
    setNodeType(self, methodNode, fnTy);
5347
    setNodeType(self, name, fnTy);
5348
    set self.regionScope = context.scope;
5349
    return ResolvedInstanceMethod { method: tm, symbol: sym };
5350
}
5351
5352
/// Resolve an instance declaration.
5353
/// Validates that the trait exists, the target type exists, and all methods
5354
/// match the trait's signatures.
5355
unsafe fn resolveInstanceDecl 'arena (
5356
    self: &mut Resolver 'arena,
5357
    node: *ast::Node,
5358
    traitName: *ast::Node,
5359
    targetType: *ast::Node,
5360
    regions: *[*ast::Node],
5361
    methods: *[*ast::Node]
5362
) throws (ResolveError) {
5363
    let previous = self.regionScope;
5364
    set self.regionScope = try bindRegions(self, node, regions);
5365
    try resolveInstanceContents(self, node, traitName, targetType, regions, methods) catch error {
5366
        set self.regionScope = previous;
5367
        throw error;
5368
    };
5369
    set self.regionScope = previous;
5370
}
5371
5372
/// Resolve an instance in its declaration region environment.
5373
unsafe fn resolveInstanceContents 'arena (
5374
    self: &mut Resolver 'arena,
5375
    node: *ast::Node,
5376
    traitName: *ast::Node,
5377
    targetType: *ast::Node,
5378
    regions: *[*ast::Node],
5379
    methods: *[*ast::Node]
5380
) throws (ResolveError) {
5381
    let instanceScope = self.regionScope;
5382
    // Look up the trait.
5383
    let traitSym = try resolveNamePath(self, traitName);
5384
    let case SymbolData::Trait(traitInfo) = traitSym.data
5385
        else throw emitError(self, traitName, ErrorKind::Internal);
5386
5387
    setNodeSymbol(self, traitName, traitSym);
5388
5389
    // Look up the target type.
5390
    let typeSym = try resolveNamePath(self, targetType);
5391
    let case SymbolData::Type(nominalTy) = typeSym.data
5392
        else throw emitError(self, targetType, ErrorKind::Internal);
5393
    setNodeSymbol(self, targetType, typeSym);
5394
    // Ensure the concrete type body is resolved.
5395
    try ensureNominalResolved(self, nominalTy, targetType);
5396
5397
    // Reject duplicate instance for the same (trait, type) pair.
5398
    let mut concreteInfo = nominalTy;
5399
    if regions.len > 0 {
5400
        set concreteInfo = try applyNominalRegions(self, nominalTy, regions, targetType);
5401
    } else {
5402
        try requireNominalArguments(self, nominalTy, targetType);
5403
    }
5404
    let concreteType = Type::Nominal(concreteInfo);
5405
    if let _ = findInstance(self, traitInfo, concreteType) {
5406
        throw emitError(self, node, ErrorKind::DuplicateInstance);
5407
    }
5408
5409
    // Build the instance entry.
5410
    if self.instancesLen >= MAX_INSTANCES {
5411
        throw emitError(self, node, ErrorKind::Internal);
5412
    }
5413
    let methodSlice = try! alloc::allocRawSlice(
5414
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
5415
    ) as *unsafe mut [*unsafe mut Symbol];
5416
    let mut entry = InstanceEntry {
5417
        traitType: traitInfo,
5418
        concreteType,
5419
        concreteTypeName: typeSym.name,
5420
        moduleId: self.currentMod,
5421
        methods: methodSlice,
5422
    };
5423
    // Track which trait methods are covered by the instance.
5424
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
5425
    let methodContext = InstanceMethodContext {
5426
        traitInfo, concreteType, regions, scope: instanceScope,
5427
    };
5428
5429
    // Match each instance method to a trait method.
5430
    for methodNode in methods {
5431
        let resolved = try resolveInstanceMethod(
5432
            self, methodNode, &methodContext
5433
        );
5434
        set entry.methods[resolved.method.index] = resolved.symbol;
5435
        set covered[resolved.method.index] = true;
5436
    }
5437
5438
    // Fill inherited method slots from supertrait instances.
5439
    for superTrait in traitInfo.supertraits {
5440
        let superInst = findInstance(self, superTrait, concreteType)
5441
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
5442
        for superMethod, mi in superTrait.methods {
5443
            let merged = findTraitMethod(&traitInfo.methods[..], superMethod.name)
5444
                else panic "resolveInstanceDecl: inherited method not found";
5445
            if not covered[merged.index] {
5446
                set entry.methods[merged.index] = superInst.methods[mi];
5447
                set covered[merged.index] = true;
5448
            }
5449
        }
5450
    }
5451
5452
    // Check that all trait methods are implemented.
5453
    for method, i in traitInfo.methods {
5454
        if not covered[i] {
5455
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
5456
        }
5457
    }
5458
    set self.instances[self.instancesLen] = entry;
5459
    set self.instancesLen += 1;
5460
5461
    setNodeType(self, node, Type::Void);
5462
}
5463
5464
/// Resolve instance method bodies.
5465
unsafe fn resolveInstanceMethodBodies 'arena (self: &mut Resolver 'arena, methods: *[*ast::Node])
5466
    throws (ResolveError)
5467
{
5468
    for methodNode in methods {
5469
        let case ast::NodeValue::MethodDecl { .. } = methodNode.value else continue;
5470
5471
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
5472
        // for this method (eg. unknown method name). Skip gracefully.
5473
        if symbolFor(self, methodNode) == nil {
5474
            continue;
5475
        }
5476
5477
        try resolveMethodBody(self, methodNode);
5478
    }
5479
}
5480
5481
/// Resolve a method body shared by instance methods and standalone methods.
5482
/// Binds the receiver and parameters, then type-checks the body.
5483
unsafe fn resolveMethodBody 'arena (
5484
    self: &mut Resolver 'arena,
5485
    node: *ast::Node,
5486
) throws (ResolveError) {
5487
    let case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } = node.value
5488
        else panic "resolveMethodBody: invalid method";
5489
    let sym = symbolFor(self, node)
5490
        else throw emitError(self, node, ErrorKind::Internal);
5491
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
5492
        else panic "resolveMethodBody: expected value symbol";
5493
    let previous = self.regionScope;
5494
    set self.regionScope = fnType.regions;
5495
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body) catch error {
5496
        set self.regionScope = previous;
5497
        throw error;
5498
    };
5499
    set self.regionScope = previous;
5500
}
5501
5502
/// Resolve a standalone method declaration (signature only).
5503
/// Validates the receiver type and registers the method in the method table.
5504
5505
/// Extract the type name from a resolved receiver type node.
5506
unsafe fn receiverTypeName 'arena (
5507
    self: &mut Resolver 'arena,
5508
    receiverType: *ast::Node,
5509
) -> *[u8] throws (ResolveError) {
5510
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
5511
        receiverType.value
5512
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5513
    let mut nameNode: *ast::Node = valueType;
5514
    match valueType.value {
5515
        case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(name)) => set nameNode = name,
5516
        case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name, .. }) => set nameNode = name,
5517
        else => throw emitError(self, receiverType, ErrorKind::Internal),
5518
    }
5519
    let sym = symbolFor(self, nameNode)
5520
        else throw emitError(self, receiverType, ErrorKind::Internal);
5521
5522
    return sym.name;
5523
}
5524
5525
/// Resolve and register a standalone method declaration.
5526
unsafe fn resolveMethodDecl 'arena (
5527
    self: &mut Resolver 'arena,
5528
    node: *ast::Node,
5529
) throws (ResolveError) {
5530
    let case ast::NodeValue::MethodDecl { modifiers, .. } = node.value
5531
        else panic "resolveMethodDecl: invalid method";
5532
    let previous = self.regionScope;
5533
    set self.regionScope = try bindRegions(self, node, modifiers.regions);
5534
    try resolveMethodSignature(self, node) catch error {
5535
        set self.regionScope = previous;
5536
        throw error;
5537
    };
5538
    set self.regionScope = previous;
5539
}
5540
5541
/// Resolve a standalone method signature in its region environment.
5542
unsafe fn resolveMethodSignature 'arena (
5543
    self: &mut Resolver 'arena,
5544
    node: *ast::Node,
5545
) throws (ResolveError) {
5546
    let case ast::NodeValue::MethodDecl {
5547
        name, modifiers, receiverType, sig, ..
5548
    } = node.value else panic "resolveMethodSignature: invalid method";
5549
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
5550
    // nominal type.
5551
    let fullReceiverTy = try infer(self, receiverType);
5552
    let case Type::Pointer {
5553
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
5554
    } = fullReceiverTy
5555
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5556
    let concreteType = *receiverTarget;
5557
    let case Type::Nominal(nominalTy) = concreteType
5558
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
5559
    try ensureNominalResolved(self, nominalTy, receiverType);
5560
5561
    // Get the type name from the inner type node's symbol.
5562
    let typeName = try receiverTypeName(self, receiverType);
5563
    let methodName = try nodeName(self, name);
5564
    let attrMask = resolveAttributes(modifiers.attrs);
5565
5566
    // Reject duplicate method for the same (type, name).
5567
    if let _ = findMethod(self, concreteType, methodName) {
5568
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
5569
    }
5570
5571
    // Resolve parameter types.
5572
    let a = alloc::arenaAllocator(self.arena);
5573
    let mut paramTypes: *mut [*Type] = &mut [];
5574
5575
    // Receiver is the first parameter.
5576
    let receiverPtrType = Type::Pointer {
5577
        class: receiverClass,
5578
        target: allocType(self, concreteType),
5579
        mutable: receiverMut,
5580
    };
5581
    paramTypes.append(allocType(self, receiverPtrType), a);
5582
5583
    for paramNode in sig.params {
5584
        let case ast::NodeValue::FnParam(param) = paramNode.value
5585
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5586
        let paramTy = try resolveValueType(self, param.type);
5587
        paramTypes.append(allocType(self, paramTy), a);
5588
    }
5589
5590
    // Resolve return type.
5591
    let mut returnType = Type::Void;
5592
    if let retNode = sig.returnType {
5593
        set returnType = try resolveValueType(self, retNode);
5594
    }
5595
5596
    // Resolve throw list.
5597
    let mut throwTypes: *mut [*Type] = &mut [];
5598
    for throwNode in sig.throwList {
5599
        let throwTy = try resolveValueType(self, throwNode);
5600
        try validateErrorTag(self, throwNode, throwTy, &throwTypes[..]);
5601
        throwTypes.append(allocType(self, throwTy), a);
5602
    }
5603
5604
    let retTypePtr = allocType(self, returnType);
5605
    let throwList = &throwTypes[..];
5606
5607
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
5608
    // Full function type (receiver + params) for lowering.
5609
    let fullFnType = FnType {
5610
        regions: self.regionScope,
5611
        paramTypes: &paramTypes[..],
5612
        returnType: retTypePtr,
5613
        throwList,
5614
        isUnsafe,
5615
    };
5616
    let fullFnInfo = allocFnType(self, fullFnType);
5617
    let fnTy = Type::Fn(fullFnInfo);
5618
5619
    // Function type excluding receiver, for call arg checking.
5620
    let checkFnType = FnType {
5621
        regions: self.regionScope,
5622
        paramTypes: &paramTypes[1..],
5623
        returnType: retTypePtr,
5624
        throwList,
5625
        isUnsafe,
5626
    };
5627
5628
    // Create a symbol for the method without binding it into the module scope.
5629
    let sym = allocSymbol(self, SymbolData::Value {
5630
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
5631
    }, methodName, node, attrMask);
5632
5633
    setNodeSymbol(self, node, sym);
5634
    setNodeType(self, node, fnTy);
5635
    setNodeType(self, name, fnTy);
5636
5637
    // Register in the method table.
5638
    if self.methodsLen >= MAX_METHODS {
5639
        throw emitError(self, node, ErrorKind::Internal);
5640
    }
5641
    set self.methods[self.methodsLen] = MethodEntry {
5642
        concreteType,
5643
        concreteTypeName: typeName,
5644
        name: methodName,
5645
        fnType: allocFnType(self, checkFnType),
5646
        mutable: receiverMut,
5647
        receiverClass,
5648
        symbolId: sym.id,
5649
        fullFnType: fullFnInfo,
5650
    };
5651
    set self.methodsLen += 1;
5652
}
5653
5654
/// Look up an instance entry by trait and concrete type.
5655
unsafe fn findInstance 'arena (self: &Resolver 'arena, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
5656
    for i in 0..self.instancesLen {
5657
        let entry: *unsafe InstanceEntry = &self.instances[i];
5658
        if entry.traitType == traitInfo and erasedTypesEqual(entry.concreteType, concreteType) {
5659
            return entry;
5660
        }
5661
    }
5662
    return nil;
5663
}
5664
5665
/// Look up a standalone method by concrete type and name.
5666
export unsafe fn findMethod 'arena (self: &Resolver 'arena, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
5667
    for i in 0..self.methodsLen {
5668
        let entry: *unsafe MethodEntry = &self.methods[i];
5669
        if erasedTypesEqual(entry.concreteType, concreteType) and entry.name == name {
5670
            return entry;
5671
        }
5672
    }
5673
    return nil;
5674
}
5675
5676
/// Look up standalone method metadata by its resolver-local symbol identity.
5677
export fn findMethodBySymbol 'arena (self: &Resolver 'arena, symbolId: u32) -> ?MethodEntry {
5678
    for i in 0..self.methodsLen {
5679
        let entry = self.methods[i];
5680
        if entry.symbolId == symbolId {
5681
            return entry;
5682
        }
5683
    }
5684
    return nil;
5685
}
5686
5687
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
5688
unsafe fn resolveUnionBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
5689
    throws (ResolveError)
5690
{
5691
    let previous = self.regionScope;
5692
    set self.regionScope = try bindRegions(self, node, decl.regions);
5693
    try resolveUnionContents(self, node, decl) catch error {
5694
        set self.regionScope = previous;
5695
        throw error;
5696
    };
5697
    set self.regionScope = previous;
5698
}
5699
5700
/// Resolve union contents in the declaration's region environment.
5701
unsafe fn resolveUnionContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
5702
    throws (ResolveError)
5703
{
5704
    // Get the type symbol that was bound to this declaration node.
5705
    // If there's no symbol, it's because an earlier phase failed.
5706
    let sym = symbolFor(self, node)
5707
        else return;
5708
    let case SymbolData::Type(nominalTy) = sym.data
5709
        else panic "resolveUnionBody: unexpected symbol data";
5710
5711
    // Check if already resolved, in which case there's no need to
5712
    // do it again.
5713
    if let case NominalType::Union(_) = *nominalTy {
5714
        return;
5715
    }
5716
    let a = alloc::arenaAllocator(self.arena);
5717
    let mut variants: *mut [UnionVariant] = &mut [];
5718
5719
    let markers = try resolveOwnershipMarkers(self, decl.derives);
5720
    set *nominalTy = NominalType::Resolving(node);
5721
5722
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
5723
    let mut iota: u32 = 0;
5724
    for variantNode, i in decl.variants {
5725
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
5726
            else panic "resolveUnionBody: invalid union variant";
5727
        let variantName = try nodeName(self, variantDecl.name);
5728
        // Resolve the variant's payload type if present.
5729
        let mut variantType = Type::Void;
5730
        if let typeNode = variantDecl.type {
5731
            set variantType = try infer(self, typeNode);
5732
            try ensureStorableType(self, typeNode, variantType);
5733
            try ensureTypeResolved(self, variantType, typeNode);
5734
        }
5735
        // Process the variant's explicit discriminant value if present.
5736
        try visitOptional(self, variantDecl.value, variantType);
5737
        let tag = variantTag(variantDecl, &mut iota);
5738
        // Create a symbol for this variant.
5739
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
5740
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
5741
5742
        variants.append(UnionVariant {
5743
            name: variantName,
5744
            valueType: variantType,
5745
            symbol: variantSym,
5746
        }, a);
5747
    }
5748
    if markers.copy {
5749
        for variant in &variants[..] {
5750
            if not isCopy(variant.valueType) {
5751
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
5752
            }
5753
        }
5754
    }
5755
    let info = computeUnionLayout(&variants[..]);
5756
5757
    // Update the nominal type with the resolved variants.
5758
    set *nominalTy = NominalType::Union(UnionType {
5759
        regions: self.regionScope,
5760
        application: nil,
5761
        variants: (&variants[..]) as *unsafe [UnionVariant],
5762
        layout: allocLayout(self, info.layout),
5763
        valOffset: info.valOffset,
5764
        isAllVoid: info.isAllVoid,
5765
        declaredLinear: markers.linear,
5766
        declaredCopy: markers.copy,
5767
    });
5768
}
5769
5770
/// Check whether an attributed module or import is active in this build.
5771
/// A test module is active only when its source module was registered.
5772
fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool {
5773
    if let attributes = attrs {
5774
        if ast::attributesContains(&attributes, ast::Attribute::Test) {
5775
            if not self.config.buildTest {
5776
                return false;
5777
            }
5778
            if let moduleName = name {
5779
                return findChildModule(self, moduleName, self.currentMod) <> nil;
5780
            }
5781
        }
5782
    }
5783
    return true;
5784
}
5785
5786
/// Analyze a module during the graph analysis phase.
5787
unsafe fn resolveModGraph 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
5788
    throws (ResolveError)
5789
{
5790
    let modName = try nodeName(self, decl.name);
5791
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
5792
        return;
5793
    }
5794
    let attrMask = resolveAttributes(decl.attrs);
5795
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
5796
    let submod = try enterSubModule(self, modName, node);
5797
5798
    // Bind the module symbol in the outer scope, ie. where the `mod` statement is.
5799
    try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope);
5800
    let case ast::NodeValue::Block(block) = submod.root.value
5801
        else panic "resolveModGraph: expected block for module root";
5802
    try resolveModuleGraph(self, &block);
5803
5804
    exitModuleScope(self, submod);
5805
}
5806
5807
/// Analyze a module in the declaration phase.
5808
unsafe fn resolveModDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
5809
    throws (ResolveError)
5810
{
5811
    // Find module under the current module.
5812
    let modName = try nodeName(self, decl.name);
5813
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
5814
        return;
5815
    }
5816
    let submod = try enterSubModule(self, modName, node);
5817
    let case ast::NodeValue::Block(block) = submod.root.value
5818
        else panic "resolveModDecl: expected block for module root";
5819
    try resolveModuleDecls(self, &block);
5820
5821
    exitModuleScope(self, submod);
5822
}
5823
5824
/// Analyze a `use` statement and create a symbol for the imported module.
5825
unsafe fn resolveUse 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Use) -> Type
5826
    throws (ResolveError)
5827
{
5828
    if not shouldAnalyzeModule(self, decl.attrs, nil) {
5829
        return Type::Void;
5830
    }
5831
    let resolved = try resolveModulePath(self, decl.path);
5832
    let attrMask = resolveAttributes(decl.attrs);
5833
5834
    if decl.wildcard {
5835
        // Import all public symbols from the target module.
5836
        for i in 0..resolved.scope.symbolsLen {
5837
            let sym = resolved.scope.symbols[i];
5838
            if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
5839
                if let existing = findSymbolInScope(self.scope, sym.name) {
5840
                    if existing == sym {
5841
                        continue;
5842
                    }
5843
                }
5844
                let scope = self.scope;
5845
                try addSymbolToScope(self, sym, scope, node);
5846
            }
5847
        }
5848
    } else {
5849
        // Regular module import.
5850
        let scope = self.scope;
5851
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope);
5852
    }
5853
    return Type::Void;
5854
}
5855
5856
/// Analyze a standard `if` statement.
5857
unsafe fn resolveIf 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::If) -> Type
5858
    throws (ResolveError)
5859
{
5860
    try checkBoolean(self, cond.condition);
5861
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
5862
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
5863
5864
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
5865
}
5866
5867
/// Analyze a conditional expression.
5868
unsafe fn resolveCondExpr 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::CondExpr, hint: Type) -> Type
5869
    throws (ResolveError)
5870
{
5871
    try checkBoolean(self, cond.condition);
5872
    let thenValue = try visit(self, cond.thenExpr, hint);
5873
    let thenTy = assignableValueType(self, cond.thenExpr, thenValue);
5874
    let elseValue = try visit(self, cond.elseExpr, hint);
5875
    let elseTy = assignableValueType(self, cond.elseExpr, elseValue);
5876
5877
    // Either branch may supply the concrete type for an otherwise context-
5878
    // dependent expression, such as an unsuffixed integer or `nil`.
5879
    if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) {
5880
        setNodeCoercion(self, cond.elseExpr, coercion);
5881
        return setNodeType(self, node, thenTy);
5882
    }
5883
    if let coercion = isAssignable(self, elseTy, thenTy, cond.thenExpr) {
5884
        setNodeCoercion(self, cond.thenExpr, coercion);
5885
        return setNodeType(self, node, elseTy);
5886
    }
5887
    try expectAssignable(self, thenTy, elseTy, cond.elseExpr);
5888
5889
    return setNodeType(self, node, thenTy);
5890
}
5891
5892
/// Analyze a pattern match structure (used by if-let, while-let).
5893
unsafe fn resolvePatternMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, pat: &ast::PatternMatch)
5894
    throws (ResolveError)
5895
{
5896
    match pat.kind {
5897
        case ast::PatternKind::Case => {
5898
            // Analyze pattern against scrutinee type.
5899
            let scrutineeTy = try infer(self, pat.scrutinee);
5900
            if isUnsafePointerType(scrutineeTy) {
5901
                try requireUnsafe(self, pat.scrutinee);
5902
            }
5903
            let subject = unwrapMatchSubject(scrutineeTy);
5904
            try resolveCasePattern(self, pat.pattern, subject.effectiveTy, IdentMode::Compare, subject.by);
5905
        }
5906
        case ast::PatternKind::Binding => {
5907
            // Scrutinee must be optional, bind the payload.
5908
            let scrutineeTy = try checkOptional(self, pat.scrutinee);
5909
            let payloadTy = *scrutineeTy;
5910
5911
            try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
5912
            setNodeType(self, pat.pattern, payloadTy);
5913
        }
5914
    }
5915
    if let guard = pat.guard {
5916
        try checkBoolean(self, guard);
5917
    }
5918
}
5919
5920
/// Analyze an `if let` or `if let case` pattern binding.
5921
unsafe fn resolveIfLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::IfLet) -> Type
5922
    throws (ResolveError)
5923
{
5924
    enterScope(self, node);
5925
    try resolvePatternMatch(self, node, &cond.pattern);
5926
5927
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
5928
    exitScope(self);
5929
5930
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
5931
5932
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
5933
}
5934
5935
/// Controls how bare identifiers are handled in case patterns.
5936
union IdentMode: Copy {
5937
    /// Identifier is a value to compare against.
5938
    Compare,
5939
    /// Identifier introduces a new binding.
5940
    Bind,
5941
}
5942
5943
/// Check whether a pattern node is a destructuring pattern that looks
5944
/// through structure (union variant, record literal, scope access).
5945
/// Identifiers, placeholders, and plain literals are not destructuring.
5946
export fn isDestructuringPattern(pattern: *ast::Node) -> bool {
5947
    match pattern.value {
5948
        case ast::NodeValue::Call(_),
5949
             ast::NodeValue::RecordLit(_),
5950
             ast::NodeValue::ScopeAccess(_) => return true,
5951
        else => return false,
5952
    }
5953
}
5954
5955
/// Analyze a case pattern for match, if-case, let-case, or while-case.
5956
///
5957
/// At the top level, bare identifiers are compared against existing values.
5958
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
5959
unsafe fn resolveCasePattern 'arena (
5960
    self: &mut Resolver 'arena,
5961
    pattern: *ast::Node,
5962
    scrutineeTy: Type,
5963
    mode: IdentMode,
5964
    matchBy: MatchBy
5965
) throws (ResolveError) {
5966
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
5967
        if isUnsafePointerType(scrutineeTy) {
5968
            try requireUnsafe(self, pattern);
5969
        }
5970
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
5971
        return;
5972
    }
5973
    // TODO: Collapse these nested matches.
5974
    match scrutineeTy {
5975
        case Type::Nominal(info) => {
5976
            try ensureNominalResolved(self, info, pattern);
5977
5978
            match *info {
5979
                case NominalType::Union(unionType) => {
5980
                    try resolveUnionPattern(self, pattern, scrutineeTy, unionType, matchBy);
5981
                    return;
5982
                }
5983
                case NominalType::Record(recInfo) => {
5984
                    match pattern.value {
5985
                        case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => {
5986
                            try resolveRecordPattern(self, pattern, scrutineeTy, recInfo, matchBy);
5987
                            return;
5988
                        } else => {}
5989
                    }
5990
                } else => {}
5991
            }
5992
        }
5993
        case Type::Array(arrayInfo) => {
5994
            if let case ast::NodeValue::ArrayLit(items) = pattern.value {
5995
                if items.len as u32 <> arrayInfo.length {
5996
                    throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(
5997
                        CountMismatch { expected: arrayInfo.length, actual: items.len as u32 }
5998
                    ));
5999
                }
6000
                let elemTy = *arrayInfo.item;
6001
                for item in items {
6002
                    try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy);
6003
                }
6004
                setNodeType(self, pattern, scrutineeTy);
6005
                return;
6006
            }
6007
        } else => {}
6008
    }
6009
    // Handle non-binding patterns (literals, placeholders) and bindings.
6010
    match pattern.value {
6011
        case ast::NodeValue::Placeholder => {
6012
            // Placeholder matches without introducing bindings.
6013
        }
6014
        case ast::NodeValue::Ident(_) => {
6015
            match mode {
6016
                case IdentMode::Bind => try bindPatternVar(self, pattern, scrutineeTy, matchBy),
6017
                case IdentMode::Compare => try checkAssignable(self, pattern, scrutineeTy),
6018
            }
6019
        }
6020
        else => {
6021
            // Literals and other expressions: check type compatibility.
6022
            try checkAssignable(self, pattern, scrutineeTy);
6023
        }
6024
    }
6025
}
6026
6027
/// Analyze a traditional `while` loop.
6028
unsafe fn resolveWhile 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::While) -> Type
6029
    throws (ResolveError)
6030
{
6031
    try checkBoolean(self, loopNode.condition);
6032
    let loopTy = try visitLoop(self, loopNode.body);
6033
    try visitOptional(self, loopNode.elseBranch, Type::Void);
6034
6035
    if loopNode.condition.value == ast::NodeValue::Bool(true) {
6036
        return setNodeType(self, node, loopTy);
6037
    }
6038
    return setNodeType(self, node, Type::Void);
6039
}
6040
6041
/// Analyze a `while let` loop with pattern binding.
6042
unsafe fn resolveWhileLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::WhileLet) -> Type
6043
    throws (ResolveError)
6044
{
6045
    enterScope(self, node);
6046
    try resolvePatternMatch(self, node, &loopNode.pattern);
6047
6048
    try visitLoop(self, loopNode.body);
6049
    exitScope(self);
6050
6051
    try visitOptional(self, loopNode.elseBranch, Type::Void);
6052
6053
    return setNodeType(self, node, Type::Void);
6054
}
6055
6056
/// Store complete iteration metadata and return the loop binding type.
6057
fn resolveForInfo 'arena (
6058
    self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For, iterableTy: Type
6059
) -> Type throws (ResolveError) {
6060
    // Extract binding names for the lowerer.
6061
    let mut bindingName: ?*[u8] = nil;
6062
    if let case ast::NodeValue::Ident(name) = forStmt.binding.value {
6063
        set bindingName = name;
6064
    }
6065
    let mut indexName: ?*[u8] = nil;
6066
    if let idx = forStmt.index {
6067
        if let case ast::NodeValue::Ident(name) = idx.value {
6068
            set indexName = name;
6069
        }
6070
    }
6071
    // Extract item type and store pre-computed loop metadata for the lowerer.
6072
    match iterableTy {
6073
        case Type::Slice { item, class, .. } => {
6074
            if class == types::PointerClass::Unsafe {
6075
                try requireUnsafe(self, forStmt.iterable);
6076
            }
6077
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6078
                elemType: item, length: nil, bindingName, indexName
6079
            });
6080
            return *item;
6081
        }
6082
        case Type::Range { start, .. } => {
6083
            // Iterable ranges must have a start, and since we enforce type
6084
            // equality for start and end, that is always the item type.
6085
            let valType = start else {
6086
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6087
            };
6088
            let case ast::NodeValue::Range(range) = forStmt.iterable.value else {
6089
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6090
            };
6091
            setForLoopInfo(self, node, ForLoopInfo::Range {
6092
                valType, range, bindingName, indexName
6093
            });
6094
            return *valType;
6095
        }
6096
        case Type::Array(arrayInfo) => {
6097
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6098
                elemType: arrayInfo.item,
6099
                length: arrayInfo.length,
6100
                bindingName,
6101
                indexName,
6102
            });
6103
            return *arrayInfo.item;
6104
        }
6105
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
6106
    }
6107
}
6108
6109
/// Analyze a `for` loop, binding iteration variables.
6110
unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type
6111
    throws (ResolveError)
6112
{
6113
    let iterableTy = try infer(self, forStmt.iterable);
6114
    let itemTy = try resolveForInfo(self, node, forStmt, iterableTy);
6115
    enterScope(self, node);
6116
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
6117
6118
    if let pat = forStmt.index {
6119
        try bindForLoopPattern(self, pat, Type::U32, false);
6120
    }
6121
    // The lowerer always creates at least one internal variable for iteration,
6122
    // even when the binding is a placeholder or no explicit index is given.
6123
    if let owner = self.currentFnNode {
6124
        set self.nodeData.entries[owner.id].localCount += 1;
6125
    }
6126
    try visitLoop(self, forStmt.body);
6127
    exitScope(self);
6128
6129
    try visitOptional(self, forStmt.elseBranch, Type::Void);
6130
6131
    return setNodeType(self, node, Type::Void);
6132
}
6133
6134
/// Get the node within a pattern that carries the `UnionVariant` extra.
6135
/// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the
6136
/// type name, and for `Call` it is the callee.
6137
export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node {
6138
    match pattern.value {
6139
        case ast::NodeValue::ScopeAccess(_) => return pattern,
6140
        case ast::NodeValue::RecordLit(lit) => return lit.typeName,
6141
        case ast::NodeValue::Call(call) => return call.callee,
6142
        else => return nil,
6143
    }
6144
}
6145
6146
/// Get the i-th sub-pattern element from a compound pattern.
6147
/// For `RecordLit` this is the i-th field's value; for `Call` it is the
6148
/// i-th argument.
6149
fn patternSubElement(pattern: *ast::Node, idx: u32) -> ?*ast::Node {
6150
    match pattern.value {
6151
        case ast::NodeValue::RecordLit(lit) => {
6152
            if idx < lit.fields.len as u32 {
6153
                if let case ast::NodeValue::RecordLitField(field) = lit.fields[idx].value {
6154
                    return field.value;
6155
                }
6156
            }
6157
        }
6158
        case ast::NodeValue::Call(call) => {
6159
            if idx < call.args.len as u32 {
6160
                return call.args[idx];
6161
            }
6162
        }
6163
        else => {}
6164
    }
6165
    return nil;
6166
}
6167
6168
/// Get the number of sub-pattern elements in a compound pattern.
6169
fn patternSubCount(pattern: *ast::Node) -> u32 {
6170
    match pattern.value {
6171
        case ast::NodeValue::RecordLit(lit) => return lit.fields.len as u32,
6172
        case ast::NodeValue::Call(call) => return call.args.len as u32,
6173
        else => return 0,
6174
    }
6175
}
6176
6177
/// Check whether a pattern contains nested sub-patterns that further
6178
/// refine the match beyond the outer variant (e.g. nested union variant
6179
/// tests or literal comparisons). Used to allow the same outer variant
6180
/// to appear in multiple match arms.
6181
fn hasNestedRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
6182
    for i in 0..patternSubCount(pattern) {
6183
        if let sub = patternSubElement(pattern, i) {
6184
            if isRefiningPattern(self, sub) {
6185
                return true;
6186
            }
6187
        }
6188
    }
6189
    return false;
6190
}
6191
6192
/// Check whether a single pattern node is a refining pattern that tests
6193
/// a value rather than just binding it. Union variants, literals, and
6194
/// scope accesses are refining; identifiers, placeholders, and plain
6195
/// record destructurings are not.
6196
fn isRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
6197
    match pattern.value {
6198
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
6199
            return false,
6200
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
6201
            if let keyNode = patternVariantKeyNode(pattern) {
6202
                if let case NodeExtra::UnionVariant { .. } = self.nodeData.entries[keyNode.id].extra {
6203
                    return true;
6204
                }
6205
            }
6206
            // Plain record destructuring / non-variant call is not directly
6207
            // refining; recurse to check sub-patterns.
6208
            return hasNestedRefiningPattern(self, pattern);
6209
        }
6210
        case ast::NodeValue::ArrayLit(items) => {
6211
            for item in items {
6212
                if isRefiningPattern(self, item) {
6213
                    return true;
6214
                }
6215
            }
6216
            return false;
6217
        }
6218
        case ast::NodeValue::ScopeAccess(_) =>
6219
            return true,
6220
        else =>
6221
            return true,
6222
    }
6223
}
6224
6225
/// Check whether any pattern in a case prong matches unconditionally.
6226
/// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies.
6227
/// Note: top-level identifiers in `case` are comparisons, not bindings,
6228
/// so they do not count as wildcards.
6229
fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool {
6230
    for pattern in patterns {
6231
        match pattern.value {
6232
            case ast::NodeValue::Placeholder => return true,
6233
            case ast::NodeValue::ArrayLit(items) => {
6234
                if isIrrefutableArrayPattern(items) {
6235
                    return true;
6236
                }
6237
            }
6238
            else => {}
6239
        }
6240
    }
6241
    return false;
6242
}
6243
6244
/// Check whether all elements of an array pattern are irrefutable.
6245
/// Inside array patterns, identifiers are bindings, not comparisons.
6246
fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool {
6247
    for item in items {
6248
        match item.value {
6249
            case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {}
6250
            case ast::NodeValue::ArrayLit(inner) => {
6251
                if not isIrrefutableArrayPattern(inner) {
6252
                    return false;
6253
                }
6254
            }
6255
            else => return false,
6256
        }
6257
    }
6258
    return true;
6259
}
6260
6261
/// Classify a match prong and reject duplicate catch-alls.
6262
/// Record whether lowering can omit the prong's pattern test.
6263
fn checkMatchProng 'arena (
6264
    self: &mut Resolver 'arena,
6265
    prongNode: *ast::Node,
6266
    prong: ast::MatchProng,
6267
    subjectTy: Type,
6268
    state: &mut MatchState
6269
) throws (ResolveError) {
6270
    // Whether this prong is catch-all.
6271
    let mut isCatchAll = false;
6272
6273
    if prong.guard <> nil {
6274
        set state.isConst = false;
6275
    } else {
6276
        match prong.arm {
6277
            case ast::ProngArm::Binding(_) => {
6278
                // For optionals, a binding matches only a present value.
6279
                set isCatchAll = not isOptionalType(subjectTy);
6280
            },
6281
            case ast::ProngArm::Else => set isCatchAll = true,
6282
            case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
6283
        }
6284
    }
6285
    if isCatchAll {
6286
        if state.catchAll {
6287
            throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
6288
        }
6289
        set state.catchAll = true;
6290
    }
6291
    setProngCatchAll(self, prongNode, isCatchAll);
6292
6293
}
6294
6295
/// Analyze a `match` expression. Dispatches to specialized functions based on
6296
/// the subject type.
6297
unsafe fn resolveMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match) -> Type
6298
    throws (ResolveError)
6299
{
6300
    let subjectTy = try infer(self, sw.subject);
6301
    if isUnsafePointerType(subjectTy) {
6302
        try requireUnsafe(self, sw.subject);
6303
    }
6304
    let subject = unwrapMatchSubject(subjectTy);
6305
6306
    if let case Type::Optional(inner) = subject.effectiveTy {
6307
        try resolveMatchOptional(self, node, sw, inner, subject.by);
6308
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
6309
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
6310
    } else {
6311
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy, subject.by);
6312
    }
6313
6314
    // Mark last non-guarded prong as exhaustive.
6315
    let lastProng = sw.prongs[sw.prongs.len - 1];
6316
    let case ast::NodeValue::MatchProng(p) = lastProng.value
6317
        else panic "resolveMatch: expected match prong";
6318
    if p.guard == nil {
6319
        setProngCatchAll(self, lastProng, true);
6320
    }
6321
    let ty = typeFor(self, node) else {
6322
        return Type::Void;
6323
    };
6324
    return ty;
6325
}
6326
6327
/// Analyze a `match` expression on an optional subject.
6328
unsafe fn resolveMatchOptional 'arena (
6329
    self: &mut Resolver 'arena,
6330
    node: *ast::Node,
6331
    sw: ast::Match,
6332
    innerTy: *Type,
6333
    matchBy: MatchBy
6334
) -> Type throws (ResolveError)
6335
{
6336
    let subjectTy = Type::Optional(innerTy);
6337
    let prongs = sw.prongs;
6338
    let mut hasValue = false;
6339
    let mut hasNil = false;
6340
    let mut state = MatchState { catchAll: false, isConst: false };
6341
    let mut matchType = Type::Never;
6342
6343
    for prongNode in prongs {
6344
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6345
            else panic "resolveMatchOptional: expected match prong";
6346
6347
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6348
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6349
6350
        // Track coverage. Guarded prongs don't count as covering a case.
6351
        if prong.guard == nil {
6352
            if let case ast::ProngArm::Binding(_) = prong.arm {
6353
                if hasValue {
6354
                    throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern);
6355
                }
6356
                set hasValue = true;
6357
            } else if let case ast::ProngArm::Case(patterns) = prong.arm {
6358
                for pat in patterns {
6359
                    if let case ast::NodeValue::Nil = pat.value {
6360
                        if hasNil {
6361
                            throw emitError(self, pat, ErrorKind::DuplicateMatchPattern);
6362
                        }
6363
                        set hasNil = true;
6364
                    }
6365
                }
6366
            }
6367
        }
6368
    }
6369
6370
    // Check exhaustiveness.
6371
    if not state.catchAll {
6372
        if not hasValue {
6373
            throw emitError(self, node, ErrorKind::OptionalMatchMissingValue);
6374
        }
6375
        if not hasNil {
6376
            throw emitError(self, node, ErrorKind::OptionalMatchMissingNil);
6377
        }
6378
    } else if hasValue and hasNil {
6379
        throw emitError(self, node, ErrorKind::UnreachableElse);
6380
    }
6381
    return setNodeType(self, node, matchType);
6382
}
6383
6384
/// Analyze a `match` expression on a union subject.
6385
unsafe fn resolveMatchUnion 'arena (
6386
    self: &mut Resolver 'arena,
6387
    node: *ast::Node,
6388
    sw: ast::Match,
6389
    subjectTy: Type,
6390
    info: UnionType,
6391
    matchBy: MatchBy
6392
) -> Type throws (ResolveError) {
6393
    let prongs = sw.prongs;
6394
    let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS];
6395
    let mut coveredCount: u32 = 0;
6396
    let mut state = MatchState { catchAll: false, isConst: false };
6397
    let mut matchType = Type::Never;
6398
6399
    for prongNode in prongs {
6400
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6401
            else panic "resolveMatchUnion: expected match prong";
6402
6403
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6404
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6405
6406
        // Guarded prongs don't count as covering. Patterns with nested
6407
        // refining sub-patterns (e.g. matching different inner union variants)
6408
        // don't count as duplicates or as fully covering.
6409
        if prong.guard == nil {
6410
            if let case ast::ProngArm::Case(patterns) = prong.arm {
6411
                for pattern in patterns {
6412
                    if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra {
6413
                        if not hasNestedRefiningPattern(self, pattern) {
6414
                            if covered[ix] {
6415
                                throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern);
6416
                            }
6417
                            set covered[ix] = true;
6418
                            set coveredCount += 1;
6419
                        }
6420
                    }
6421
                }
6422
            }
6423
        }
6424
    }
6425
    // Check that all variants are covered.
6426
    if not state.catchAll {
6427
        for variant, i in info.variants {
6428
            if not covered[i] {
6429
                throw emitError(
6430
                    self, node, ErrorKind::UnionMatchNonExhaustive(variant.name)
6431
                );
6432
            }
6433
        }
6434
    } else if coveredCount == info.variants.len as u32 {
6435
        throw emitError(self, node, ErrorKind::UnreachableElse);
6436
    }
6437
    return setNodeType(self, node, matchType);
6438
}
6439
6440
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
6441
/// booleans must cover both `true` and `false`, other types require a catch-all.
6442
unsafe fn resolveMatchGeneric 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match, subjectTy: Type, matchBy: MatchBy) -> Type
6443
    throws (ResolveError)
6444
{
6445
    let prongs = sw.prongs;
6446
    let mut state = MatchState { catchAll: false, isConst: true };
6447
    let mut matchType = Type::Never;
6448
    let mut hasTrue = false;
6449
    let mut hasFalse = false;
6450
    let mut hasConstCase = false;
6451
6452
    for prongNode in prongs {
6453
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6454
            else panic "resolveMatchGeneric: expected match prong";
6455
6456
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6457
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6458
        // Track boolean coverage. Guarded prongs don't count as covering.
6459
        if let case ast::ProngArm::Case(patterns) = prong.arm {
6460
            for p in patterns {
6461
                if prong.guard == nil {
6462
                    if let case ast::NodeValue::Bool(val) = p.value {
6463
                        if (val and hasTrue) or (not val and hasFalse) {
6464
                            throw emitError(self, p, ErrorKind::DuplicateMatchPattern);
6465
                        }
6466
                        if val {
6467
                            set hasTrue = true;
6468
                        } else {
6469
                            set hasFalse = true;
6470
                        }
6471
                    }
6472
                }
6473
                // Scalar constant patterns allow the match to be lowered
6474
                // to a switch instruction.
6475
                if let c = constValueEntry(self, p) {
6476
                    match c {
6477
                        case ConstValue::Bool(_), ConstValue::Char(_), ConstValue::Int(_) =>
6478
                            set hasConstCase = true,
6479
                        else =>
6480
                            set state.isConst = false,
6481
                    }
6482
                }
6483
            }
6484
        }
6485
    }
6486
6487
    // Check exhaustiveness.
6488
    if not state.catchAll {
6489
        if let case Type::Bool = subjectTy {
6490
            if not hasTrue {
6491
                throw emitError(self, node, ErrorKind::BoolMatchMissing(true));
6492
            }
6493
            if not hasFalse {
6494
                throw emitError(self, node, ErrorKind::BoolMatchMissing(false));
6495
            }
6496
        } else {
6497
            throw emitError(self, node, ErrorKind::MatchNonExhaustive);
6498
        }
6499
    } else if let case Type::Bool = subjectTy {
6500
        if hasTrue and hasFalse {
6501
            throw emitError(self, node, ErrorKind::UnreachableElse);
6502
        }
6503
    }
6504
    setMatchConst(self, node, state.isConst and hasConstCase);
6505
6506
    return setNodeType(self, node, matchType);
6507
}
6508
6509
/// Analyze a single `match` prong branch. Returns the unified match type.
6510
unsafe fn visitMatchProng 'arena (
6511
    self: &mut Resolver 'arena,
6512
    node: *ast::Node,
6513
    prongNode: ast::MatchProng,
6514
    subjectTy: Type,
6515
    matchType: Type,
6516
    matchBy: MatchBy
6517
) -> Type throws (ResolveError) {
6518
    enterScope(self, node);
6519
    let prongTy = try resolveMatchProngBody(self, prongNode, subjectTy, matchBy) catch e {
6520
        exitScope(self);
6521
        throw e;
6522
    };
6523
    exitScope(self);
6524
    setNodeType(self, node, prongTy);
6525
6526
    return unifyBranches(matchType, prongTy);
6527
}
6528
6529
/// Analyze the contents of a `match` prong while inside the prong scope.
6530
unsafe fn resolveMatchProngBody 'arena (
6531
    self: &mut Resolver 'arena,
6532
    prong: ast::MatchProng,
6533
    subjectTy: Type,
6534
    matchBy: MatchBy
6535
) -> Type throws (ResolveError) {
6536
    match prong.arm {
6537
        case ast::ProngArm::Binding(pat) => {
6538
            // For optionals, bind the unwrapped inner type.
6539
            let mut bindTy = subjectTy;
6540
            if let case Type::Optional(inner) = subjectTy {
6541
                set bindTy = *inner;
6542
            }
6543
            try bindPatternVar(self, pat, bindTy, matchBy);
6544
        }
6545
        case ast::ProngArm::Case(patterns) => {
6546
            for pattern in patterns {
6547
                try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy);
6548
            }
6549
        }
6550
        case ast::ProngArm::Else => {}
6551
    }
6552
    if let g = prong.guard {
6553
        try checkBoolean(self, g);
6554
    }
6555
    return try visit(self, prong.body, Type::Void);
6556
}
6557
6558
/// Ensure a scope access pattern references a compatible union variant.
6559
unsafe fn resolveUnionScopePattern 'arena (
6560
    self: &mut Resolver 'arena,
6561
    pattern: *ast::Node,
6562
    access: ast::Access,
6563
    subjectTy: Type,
6564
    unionType: UnionType
6565
) throws (ResolveError) {
6566
    let patternTy = try visit(self, pattern, subjectTy);
6567
    if not isComparable(patternTy, subjectTy) {
6568
        throw emitTypeMismatch(self, pattern, TypeMismatch {
6569
            expected: subjectTy,
6570
            actual: patternTy,
6571
        });
6572
    }
6573
    let case NodeExtra::UnionVariant { ordinal: index, .. } = self.nodeData.entries[pattern.id].extra else {
6574
        throw emitError(self, pattern, ErrorKind::Internal);
6575
    };
6576
    let variant = &unionType.variants[index];
6577
    // If this variant has a payload, throw an error, since the user hasn't
6578
    // provided one.
6579
    if variant.valueType <> Type::Void {
6580
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
6581
    }
6582
}
6583
6584
/// Validate and bind a union constructor call used as a `match` pattern.
6585
unsafe fn resolveUnionCallPattern 'arena (
6586
    self: &mut Resolver 'arena,
6587
    pattern: *ast::Node,
6588
    call: ast::Call,
6589
    subjectTy: Type,
6590
    unionType: UnionType,
6591
    matchBy: MatchBy
6592
) throws (ResolveError) {
6593
    let calleeTy = try checkEqual(self, call.callee, subjectTy);
6594
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
6595
        throw emitError(self, call.callee, ErrorKind::Internal);
6596
    };
6597
    let variant = &unionType.variants[index];
6598
    // Copy variant index to the pattern node for the lowerer.
6599
    setVariantInfo(self, pattern, index, tag);
6600
6601
    if variant.valueType <> Type::Void {
6602
        try bindUnionPatternPayload(self, pattern, call, variant.name, variant.valueType, matchBy);
6603
    } else {
6604
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
6605
    }
6606
}
6607
6608
/// Bind the payload introduced by a union constructor pattern.
6609
unsafe fn bindUnionPatternPayload 'arena (
6610
    self: &mut Resolver 'arena,
6611
    pattern: *ast::Node,
6612
    call: ast::Call,
6613
    variantName: *[u8],
6614
    payloadTy: Type,
6615
    matchBy: MatchBy
6616
) throws (ResolveError) {
6617
    if call.args.len == 0 {
6618
        throw emitError(
6619
            self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName)
6620
        );
6621
    }
6622
    // All variant payloads are records.
6623
    try ensureTypeResolved(self, payloadTy, pattern);
6624
    let recInfo = getRecord(payloadTy)
6625
        else panic "bindUnionPatternPayload: payload is not a record";
6626
6627
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
6628
}
6629
6630
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
6631
unsafe fn bindPatternVar 'arena (self: &mut Resolver 'arena, binding: *ast::Node, ty: Type, matchBy: MatchBy)
6632
    throws (ResolveError)
6633
{
6634
    let mut bindTy = ty;
6635
    match matchBy {
6636
        case MatchBy::Value => {}
6637
        case MatchBy::Ref => set bindTy = Type::Pointer {
6638
            class: types::PointerClass::Ref,
6639
            target: allocType(self, ty),
6640
            mutable: false,
6641
        },
6642
        case MatchBy::MutRef => set bindTy = Type::Pointer {
6643
            class: types::PointerClass::Ref,
6644
            target: allocType(self, ty),
6645
            mutable: true,
6646
        },
6647
    }
6648
    match binding.value {
6649
        case ast::NodeValue::Placeholder => {
6650
            // Nothing to do.
6651
        }
6652
        case ast::NodeValue::Ident(_) => {
6653
            try bindValueIdent(self, binding, binding, bindTy, false, 0, 0);
6654
        }
6655
        else => {
6656
            // Nested pattern: recursively resolve (record destructuring,
6657
            // union variant, scope access, call, literals, etc).
6658
            try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy);
6659
        }
6660
    }
6661
}
6662
6663
/// Check a record pattern's exact nominal type before binding its fields.
6664
unsafe fn resolveRecordPattern 'arena (
6665
    self: &mut Resolver 'arena, pattern: *ast::Node, subjectTy: Type, body: RecordType, matchBy: MatchBy
6666
) throws (ResolveError) {
6667
    let mut name: ?*ast::Node = nil;
6668
    match pattern.value {
6669
        case ast::NodeValue::Call(call) => set name = call.callee,
6670
        case ast::NodeValue::RecordLit(lit) => set name = lit.typeName,
6671
        else => panic "resolveRecordPattern: expected record pattern",
6672
    }
6673
    if let typeName = name {
6674
        let actual = try visit(self, typeName, subjectTy);
6675
        let symbol = symbolFor(self, typeName) else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
6676
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
6677
        if not typesEqual(actual, subjectTy) {
6678
            throw emitTypeMismatch(self, typeName, TypeMismatch { expected: subjectTy, actual });
6679
        }
6680
    }
6681
    setNodeType(self, pattern, subjectTy);
6682
    try bindRecordPatternFields(self, pattern, body, matchBy);
6683
}
6684
6685
/// Bind record pattern fields to variables in the current scope.
6686
unsafe fn bindRecordPatternFields 'arena (
6687
    self: &mut Resolver 'arena,
6688
    pattern: *ast::Node,
6689
    recInfo: RecordType,
6690
    matchBy: MatchBy
6691
) throws (ResolveError) {
6692
    match pattern.value {
6693
        case ast::NodeValue::Call(call) => {
6694
            // Unlabeled patterns: `S(x, y)`.
6695
            try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: call.args.len }, pattern);
6696
6697
            for binding, i in call.args {
6698
                let fieldType = recInfo.fields[i].fieldType;
6699
                try bindPatternVar(self, binding, fieldType, matchBy);
6700
            }
6701
        }
6702
        case ast::NodeValue::RecordLit(lit) => {
6703
            // Labeled patterns: `T { x, y }` or `T { x: binding }`.
6704
            if not lit.ignoreRest {
6705
                try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: lit.fields.len }, pattern);
6706
            }
6707
            for fieldNode in lit.fields {
6708
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
6709
                    else panic "expected RecordLitField";
6710
6711
                // Brace patterns require labeled fields.
6712
                let label = field.label else panic "expected labeled field";
6713
                let fieldName = try nodeName(self, label);
6714
                let fieldIndex = findRecordField(&recInfo.fields[..], fieldName)
6715
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
6716
                let fieldType = recInfo.fields[fieldIndex].fieldType;
6717
                // Store field index for the lowerer.
6718
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6719
                try bindPatternVar(self, field.value, fieldType, matchBy);
6720
            }
6721
        }
6722
        else => throw emitError(self, pattern, ErrorKind::Internal)
6723
    }
6724
}
6725
6726
/// Validate and bind a record literal pattern for matching labeled union variants.
6727
unsafe fn resolveUnionRecordPattern 'arena (
6728
    self: &mut Resolver 'arena,
6729
    pattern: *ast::Node,
6730
    lit: ast::RecordLit,
6731
    subjectTy: Type,
6732
    unionType: UnionType,
6733
    matchBy: MatchBy
6734
) throws (ResolveError) {
6735
    let typeName = lit.typeName else {
6736
        throw emitError(self, pattern, ErrorKind::Internal);
6737
    };
6738
    // Verify the type matches the subject.
6739
    let patternTy = try visit(self, typeName, subjectTy);
6740
    if not isComparable(patternTy, subjectTy) {
6741
        throw emitTypeMismatch(self, pattern, TypeMismatch {
6742
            expected: subjectTy,
6743
            actual: patternTy,
6744
        });
6745
    }
6746
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[typeName.id].extra else {
6747
        throw emitError(self, typeName, ErrorKind::Internal);
6748
    };
6749
    let variant = &unionType.variants[index];
6750
6751
    // Copy variant index to the pattern node for the lowerer.
6752
    setVariantInfo(self, pattern, index, tag);
6753
6754
    if variant.valueType == Type::Void {
6755
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
6756
    }
6757
    try ensureTypeResolved(self, variant.valueType, pattern);
6758
    let recInfo = getRecord(variant.valueType)
6759
        else panic "resolveUnionRecordPattern: payload is not a record";
6760
6761
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
6762
}
6763
6764
/// Analyze a pattern appearing in a union case.
6765
unsafe fn resolveUnionPattern 'arena (
6766
    self: &mut Resolver 'arena,
6767
    pattern: *ast::Node,
6768
    subjectTy: Type,
6769
    unionType: UnionType,
6770
    matchBy: MatchBy
6771
) throws (ResolveError) {
6772
    match pattern.value {
6773
        case ast::NodeValue::ScopeAccess(access) =>
6774
            try resolveUnionScopePattern(self, pattern, access, subjectTy, unionType),
6775
        case ast::NodeValue::Call(call) =>
6776
            try resolveUnionCallPattern(self, pattern, call, subjectTy, unionType, matchBy),
6777
        case ast::NodeValue::RecordLit(lit) =>
6778
            try resolveUnionRecordPattern(self, pattern, lit, subjectTy, unionType, matchBy),
6779
        else => {
6780
            let patternTy = try visit(self, pattern, subjectTy);
6781
            throw emitTypeMismatch(self, pattern, TypeMismatch {
6782
                expected: subjectTy,
6783
                actual: patternTy,
6784
            });
6785
        }
6786
    }
6787
}
6788
6789
/// Return whether a case pattern introduces value bindings.
6790
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
6791
    match pattern.value {
6792
        case ast::NodeValue::Ident(_) => return nested,
6793
        case ast::NodeValue::Call(call) => {
6794
            for arg in call.args {
6795
                if casePatternIntroducesBindings(arg, true) {
6796
                    return true;
6797
                }
6798
            }
6799
        }
6800
        case ast::NodeValue::RecordLit(lit) => {
6801
            for fieldNode in lit.fields {
6802
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
6803
                    else continue;
6804
                if casePatternIntroducesBindings(field.value, true) {
6805
                    return true;
6806
                }
6807
            }
6808
        }
6809
        case ast::NodeValue::ArrayLit(items) => {
6810
            for item in items {
6811
                if casePatternIntroducesBindings(item, true) {
6812
                    return true;
6813
                }
6814
            }
6815
        }
6816
        else => {}
6817
    }
6818
    return false;
6819
}
6820
6821
/// Analyze a `let-else` guard.
6822
unsafe fn resolveLetElse 'arena (self: &mut Resolver 'arena, node: *ast::Node, letElse: ast::LetElse) -> Type
6823
    throws (ResolveError)
6824
{
6825
    let pat = letElse.pattern;
6826
    let exprTy = try infer(self, pat.scrutinee);
6827
6828
    match pat.kind {
6829
        case ast::PatternKind::Binding => {
6830
            // Simple binding requires an optional expression.
6831
            let case Type::Optional(inner) = exprTy else {
6832
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
6833
            };
6834
            let payloadTy = *inner;
6835
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
6836
            // The `else` branch supplies the binding when the optional is nil.
6837
            try checkAssignable(self, letElse.elseBranch, payloadTy);
6838
6839
            return setNodeType(self, node, Type::Void);
6840
        }
6841
        case ast::PatternKind::Case => {
6842
            // Resolve the failure path before introducing success-only bindings.
6843
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
6844
            try resolveCasePattern(
6845
                self,
6846
                pat.pattern,
6847
                exprTy,
6848
                IdentMode::Compare,
6849
                MatchBy::Value,
6850
            );
6851
            if let guardExpr = pat.guard {
6852
                try checkBoolean(self, guardExpr);
6853
            }
6854
            if elseTy <> Type::Never and
6855
               casePatternIntroducesBindings(pat.pattern, false)
6856
            {
6857
                throw emitError(
6858
                    self,
6859
                    letElse.elseBranch,
6860
                    ErrorKind::LinearLetElseMustTerminate,
6861
                );
6862
            }
6863
        }
6864
    }
6865
    return setNodeType(self, node, Type::Void);
6866
}
6867
6868
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
6869
unsafe fn resolveBuiltinCall 'arena (
6870
    self: &mut Resolver 'arena,
6871
    node: *ast::Node,
6872
    kind: ast::Builtin,
6873
    args: *[*ast::Node]
6874
) -> Type throws (ResolveError) {
6875
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
6876
    if kind == ast::Builtin::SliceOf {
6877
        if args.len <> 2 and args.len <> 3 {
6878
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
6879
                expected: 2,
6880
                actual: args.len as u32,
6881
            }));
6882
        }
6883
        let ptrType = try visit(self, args[0], Type::Unknown);
6884
        let case Type::Pointer { class, target, mutable } = ptrType else {
6885
            throw emitError(self, node, ErrorKind::ExpectedPointer);
6886
        };
6887
        let _ = try checkAssignable(self, args[1], Type::U32);
6888
        if args.len == 3 {
6889
            let _ = try checkAssignable(self, args[2], Type::U32);
6890
        }
6891
        try requireUnsafe(self, node);
6892
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
6893
    }
6894
    if args.len <> 1 {
6895
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
6896
            expected: 1,
6897
            actual: args.len as u32,
6898
        }));
6899
    }
6900
6901
    let ty = try resolveValueType(self, args[0]);
6902
    // Ensure the type body is resolved before computing layout.
6903
    // TODO: Somehow, ensuring the type is resolved should just happen all
6904
    // the time, lazily.
6905
    try ensureTypeResolved(self, ty, args[0]);
6906
    // TODO: This should be stored in `symbol` instead of having to recompute it.
6907
    // That way there's a canonical place to look for code gen.
6908
    let layout = getTypeLayout(ty);
6909
6910
    // Evaluate the built-in.
6911
    let mut value: u32 = undefined;
6912
    match kind {
6913
        case ast::Builtin::SizeOf => {
6914
            set value = layout.size;
6915
        },
6916
        case ast::Builtin::AlignOf => {
6917
            set value = layout.alignment;
6918
        },
6919
        case ast::Builtin::SliceOf => {
6920
            panic "unreachable: @sliceOf handled above";
6921
        }
6922
    }
6923
    // Record as constant value for constant folding.
6924
    setNodeConstValue(self, node, ConstValue::Int(ConstInt {
6925
        magnitude: value as u64,
6926
        bits: 32,
6927
        signed: false,
6928
        negative: false,
6929
    }));
6930
    return setNodeType(self, node, Type::U32);
6931
}
6932
6933
/// Allocate an initially empty argument map for a region-parameterized signature.
6934
unsafe fn regionSubstitution 'arena (self: &mut Resolver 'arena, parameters: *RegionScope) -> RegionSubstitution {
6935
    let count = parameters.entries.len;
6936
    let arguments = try! alloc::allocRawSlice(
6937
        self.arena, @sizeOf(?*unsafe types::Region), @alignOf(?*unsafe types::Region), count
6938
    ) as *unsafe mut [?*unsafe types::Region];
6939
    for i in 0..count {
6940
        set arguments[i] = nil;
6941
    }
6942
    return RegionSubstitution { parameters, arguments };
6943
}
6944
6945
/// Find a region's position among the region declarations in one scope.
6946
fn regionIndex(scope: &RegionScope, regionId: u32) -> ?u32 {
6947
    match scope.declarations {
6948
        case RegionDeclarations::Parameters(nodes) => {
6949
            let mut index: u32 = 0;
6950
            for node in nodes {
6951
                let case ast::NodeValue::Region { .. } = node.value else continue;
6952
                if node.id == regionId {
6953
                    return index;
6954
                }
6955
                set index += 1;
6956
            }
6957
        }
6958
        case RegionDeclarations::Block(node) => {
6959
            if node.id == regionId {
6960
                return 0;
6961
            }
6962
        }
6963
    }
6964
    return nil;
6965
}
6966
6967
/// Infer one region argument from a pair of reference classes.
6968
unsafe fn inferRegionClass 'arena (
6969
    self: &mut Resolver 'arena, map: &RegionSubstitution,
6970
    expected: types::PointerClass, actual: types::PointerClass, site: *ast::Node
6971
) throws (ResolveError) {
6972
    let case types::PointerClass::Region(parameter) = expected else return;
6973
    let index = regionIndex(map.parameters, parameter.id) else return;
6974
    let case types::PointerClass::Region(argument) = actual
6975
        else throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
6976
    if let previous = map.arguments[index]; previous.id <> argument.id {
6977
        throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
6978
    }
6979
    set map.arguments[index] = argument;
6980
}
6981
6982
/// Infer regions through matching type structure without adding lifetime subtyping.
6983
unsafe fn inferRegionArguments 'arena (
6984
    self: &mut Resolver 'arena, map: &RegionSubstitution, expected: Type, actual: Type, site: *ast::Node
6985
) throws (ResolveError) {
6986
    match expected {
6987
        case Type::Cell { class, payload } => {
6988
            let case Type::Cell { class: otherClass, payload: other } = actual else return;
6989
            try inferRegionClass(self, map, class, otherClass, site);
6990
            try inferRegionArguments(self, map, *payload, *other, site);
6991
        }
6992
        case Type::Session(region) => {
6993
            let case Type::Session(other) = actual else return;
6994
            try inferRegionClass(self, map, types::PointerClass::Region(region),
6995
                types::PointerClass::Region(other), site);
6996
        }
6997
        case Type::Pointer { class, target, .. } => {
6998
            let case Type::Pointer { class: otherClass, target: otherTarget, .. } = actual else return;
6999
            try inferRegionClass(self, map, class, otherClass, site);
7000
            try inferRegionArguments(self, map, *target, *otherTarget, site);
7001
        }
7002
        case Type::Slice { class, item, .. } => {
7003
            let case Type::Slice { class: otherClass, item: otherItem, .. } = actual else return;
7004
            try inferRegionClass(self, map, class, otherClass, site);
7005
            try inferRegionArguments(self, map, *item, *otherItem, site);
7006
        }
7007
        case Type::TraitObject { class, .. } => {
7008
            if let case Type::TraitObject { class: otherClass, .. } = actual {
7009
                try inferRegionClass(self, map, class, otherClass, site);
7010
            }
7011
        }
7012
        case Type::Array(array) => {
7013
            if let case Type::Array(other) = actual {
7014
                try inferRegionArguments(self, map, *array.item, *other.item, site);
7015
            }
7016
        }
7017
        case Type::Optional(inner) => {
7018
            if let case Type::Optional(other) = actual {
7019
                try inferRegionArguments(self, map, *inner, *other, site);
7020
            } else {
7021
                try inferRegionArguments(self, map, *inner, actual, site);
7022
            }
7023
        }
7024
        case Type::Fn(info) => {
7025
            let case Type::Fn(other) = actual else return;
7026
            if info.paramTypes.len <> other.paramTypes.len or info.throwList.len <> other.throwList.len {
7027
                return;
7028
            }
7029
            for parameter, i in info.paramTypes {
7030
                try inferRegionArguments(self, map, *parameter, *other.paramTypes[i], site);
7031
            }
7032
            for error, i in info.throwList {
7033
                try inferRegionArguments(self, map, *error, *other.throwList[i], site);
7034
            }
7035
            try inferRegionArguments(self, map, *info.returnType, *other.returnType, site);
7036
        }
7037
        case Type::Nominal(info) => {
7038
            let applied = nominalApplication(info) else return;
7039
            let case Type::Nominal(otherInfo) = actual else return;
7040
            let other = nominalApplication(otherInfo) else return;
7041
            if applied.base <> other.base {
7042
                return;
7043
            }
7044
            for region, i in applied.arguments {
7045
                try inferRegionClass(self, map, types::PointerClass::Region(region),
7046
                    types::PointerClass::Region(other.arguments[i]), site);
7047
            }
7048
        }
7049
        else => {
7050
        }
7051
    }
7052
}
7053
7054
/// Require a total substitution whose arguments satisfy each parent relation.
7055
unsafe fn validateRegionArguments 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, site: *ast::Node)
7056
    throws (ResolveError)
7057
{
7058
    for parameter, i in map.parameters.entries {
7059
        if map.arguments[i] == nil {
7060
            throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
7061
        }
7062
    }
7063
    for parameter, i in map.parameters.entries {
7064
        let parent = parameter.parent else continue;
7065
        let index = regionIndex(map.parameters, parent.id) else panic "validateRegionArguments: unknown parent";
7066
        let parentArgument = map.arguments[index] else panic "validateRegionArguments: missing parent argument";
7067
        let argument = map.arguments[i] else panic "validateRegionArguments: missing argument";
7068
        if not types::regionContains(parentArgument, argument) {
7069
            throw emitError(self, site, ErrorKind::RegionParent(parameter.name));
7070
        }
7071
    }
7072
}
7073
7074
/// Substitute a reference's region while preserving its ownership class.
7075
unsafe fn substituteRegionClass(map: &RegionSubstitution, class: types::PointerClass) -> types::PointerClass {
7076
    let case types::PointerClass::Region(region) = class else return class;
7077
    let index = regionIndex(map.parameters, region.id) else return class;
7078
    let argument = map.arguments[index] else panic "substituteRegionClass: missing argument";
7079
    return types::PointerClass::Region(argument);
7080
}
7081
7082
/// Substitute free region arguments in a type without changing its runtime layout.
7083
unsafe fn substituteRegions 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, ty: Type) -> Type {
7084
    match ty {
7085
        case Type::Cell { class, payload } =>
7086
            return Type::Cell { class: substituteRegionClass(map, class), payload: allocType(self, substituteRegions(self, map, *payload)) },
7087
        case Type::Session(region) => {
7088
            let index = regionIndex(map.parameters, region.id) else return ty;
7089
            let argument = map.arguments[index] else panic "substituteRegions: missing session region";
7090
            return Type::Session(argument);
7091
        }
7092
        case Type::Pointer { class, target, mutable } => {
7093
            let targetType = substituteRegions(self, map, *target);
7094
            return Type::Pointer { class: substituteRegionClass(map, class), target: allocType(self, targetType), mutable };
7095
        }
7096
        case Type::Slice { class, item, mutable } => {
7097
            let itemType = substituteRegions(self, map, *item);
7098
            return Type::Slice { class: substituteRegionClass(map, class), item: allocType(self, itemType), mutable };
7099
        }
7100
        case Type::TraitObject { class, traitInfo, mutable } =>
7101
            return Type::TraitObject { class: substituteRegionClass(map, class), traitInfo, mutable },
7102
        case Type::Array(array) => {
7103
            let itemType = substituteRegions(self, map, *array.item);
7104
            return Type::Array(ArrayType { item: allocType(self, itemType), length: array.length });
7105
        }
7106
        case Type::Optional(inner) => {
7107
            let innerType = substituteRegions(self, map, *inner);
7108
            return Type::Optional(allocType(self, innerType));
7109
        }
7110
        case Type::Fn(info) => return Type::Fn(substituteFnRegions(self, map, info, info.regions)),
7111
        case Type::Nominal(info) => {
7112
            let applied = nominalApplication(info) else return ty;
7113
            let arguments = regionSubstitution(self, applied.parameters);
7114
            for region, i in applied.arguments {
7115
                let class = substituteRegionClass(map, types::PointerClass::Region(region));
7116
                let case types::PointerClass::Region(argument) = class else panic;
7117
                set arguments.arguments[i] = argument;
7118
            }
7119
            return Type::Nominal(internNominalApplication(self, applied.base, &arguments));
7120
        }
7121
        else => return ty,
7122
    }
7123
}
7124
7125
/// Create a substituted signature with the specified remaining region binder.
7126
unsafe fn substituteFnRegions 'arena (
7127
    self: &mut Resolver 'arena, map: &RegionSubstitution, info: *FnType, regions: ?*RegionScope
7128
) -> *FnType {
7129
    let a = alloc::arenaAllocator(self.arena);
7130
    let mut paramTypes: *mut [*Type] = &mut [];
7131
    let mut throwList: *mut [*Type] = &mut [];
7132
    for parameter in info.paramTypes {
7133
        let ty = substituteRegions(self, map, *parameter);
7134
        paramTypes.append(allocType(self, ty), a);
7135
    }
7136
    for error in info.throwList {
7137
        let ty = substituteRegions(self, map, *error);
7138
        throwList.append(allocType(self, ty), a);
7139
    }
7140
    let returnType = substituteRegions(self, map, *info.returnType);
7141
    return allocFnType(self, FnType {
7142
        regions,
7143
        paramTypes: &paramTypes[..],
7144
        returnType: allocType(self, returnType),
7145
        throwList: &throwList[..],
7146
        isUnsafe: info.isUnsafe,
7147
    });
7148
}
7149
7150
/// Preserve a call-scoped pointer class while inferring its region-bearing contents.
7151
/// Named reference regions are inferred from the source storage.
7152
unsafe fn regionInputHint 'arena (self: &mut Resolver 'arena, expected: Type) -> Type {
7153
    if let case Type::Optional(inner) = expected {
7154
        return regionInputHint(self, *inner);
7155
    }
7156
    match expected {
7157
        case Type::Pointer { class, mutable, .. } => {
7158
            if let case types::PointerClass::Region(_) = class {
7159
                return Type::Unknown;
7160
            }
7161
            return Type::Pointer { class, target: allocType(self, Type::Unknown), mutable };
7162
        }
7163
        case Type::Slice { class, mutable, .. } => {
7164
            if let case types::PointerClass::Region(_) = class {
7165
                return Type::Unknown;
7166
            }
7167
            return Type::Slice { class, item: allocType(self, Type::Unknown), mutable };
7168
        }
7169
        else => return Type::Unknown,
7170
    }
7171
}
7172
7173
/// Infer a source function's region arguments from its call inputs.
7174
unsafe fn instantiateCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType) -> *FnType
7175
    throws (ResolveError)
7176
{
7177
    let parameters = info.regions else return info;
7178
    if call.args.len <> info.paramTypes.len {
7179
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7180
            expected: info.paramTypes.len, actual: call.args.len,
7181
        }));
7182
    }
7183
    let map = regionSubstitution(self, parameters);
7184
    for argument, i in call.args {
7185
        let expected = *info.paramTypes[i];
7186
        if containsRegion(expected) {
7187
            let actual = try visit(self, argument, regionInputHint(self, expected));
7188
            try inferRegionArguments(self, &map, expected, actual, argument);
7189
        }
7190
    }
7191
    try validateRegionArguments(self, &map, node);
7192
    return substituteFnRegions(self, &map, info, nil);
7193
}
7194
7195
/// Infer a method's region arguments from its receiver and call arguments.
7196
unsafe fn instantiateMethodCall 'arena (
7197
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call,
7198
    receiver: *ast::Node, receiverType: Type, method: *unsafe MethodEntry
7199
) -> *FnType throws (ResolveError) {
7200
    let parameters = method.fnType.regions else return method.fnType;
7201
    if call.args.len <> method.fnType.paramTypes.len {
7202
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7203
            expected: method.fnType.paramTypes.len, actual: call.args.len,
7204
        }));
7205
    }
7206
    let map = regionSubstitution(self, parameters);
7207
    try inferRegionArguments(self, &map, method.concreteType, receiverType, receiver);
7208
    for argument, i in call.args {
7209
        let expected = *method.fnType.paramTypes[i];
7210
        if containsRegion(expected) {
7211
            let actual = try visit(self, argument, regionInputHint(self, expected));
7212
            try inferRegionArguments(self, &map, expected, actual, argument);
7213
        }
7214
    }
7215
    try validateRegionArguments(self, &map, node);
7216
    return substituteFnRegions(self, &map, method.fnType, nil);
7217
}
7218
7219
/// Apply explicit current-scope regions to a function or nominal type.
7220
unsafe fn resolveRegionApply 'arena (
7221
    self: &mut Resolver 'arena, node: *ast::Node, value: *ast::Node, regions: *[*ast::Node]
7222
) -> Type throws (ResolveError) {
7223
    let ty = try infer(self, value);
7224
    if let case Type::Nominal(base) = ty {
7225
        let symbol = symbolFor(self, value) else throw emitError(self, node, ErrorKind::CannotInferType);
7226
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, node, ErrorKind::CannotInferType);
7227
        let applied = try applyNominalRegions(self, base, regions, node);
7228
        try ensureNominalResolved(self, applied, node);
7229
        setNodeSymbol(self, node, symbol);
7230
        return setNodeType(self, node, Type::Nominal(applied));
7231
    }
7232
    let case Type::Fn(info) = ty else throw emitError(self, node, ErrorKind::CannotInferType);
7233
    let mut count: u32 = 0;
7234
    if let scope = info.regions {
7235
        set count = scope.entries.len;
7236
    }
7237
    if count <> regions.len {
7238
        throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
7239
    }
7240
    let scope = info.regions else panic "resolveRegionApply: empty region application";
7241
    let map = regionSubstitution(self, scope);
7242
    for region, i in regions {
7243
        set map.arguments[i] = try resolveRegion(self, region);
7244
    }
7245
    try validateRegionArguments(self, &map, node);
7246
    let applied = substituteFnRegions(self, &map, info, nil);
7247
    if let symbol = symbolFor(self, value) {
7248
        setNodeSymbol(self, node, symbol);
7249
    }
7250
    return setNodeType(self, node, Type::Fn(applied));
7251
}
7252
7253
/// Validate call arguments against a function type: check argument count,
7254
/// type-check each argument, and verify that throwing functions use `try`.
7255
unsafe fn checkCallArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
7256
    throws (ResolveError)
7257
{
7258
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
7259
        throw emitError(self, node, ErrorKind::MissingTry);
7260
    }
7261
    if call.args.len <> info.paramTypes.len as u32 {
7262
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7263
            expected: info.paramTypes.len as u32,
7264
            actual: call.args.len,
7265
        }));
7266
    }
7267
    for argNode, i in call.args {
7268
        let expectedTy = *info.paramTypes[i];
7269
7270
        try checkAssignable(self, argNode, expectedTy);
7271
    }
7272
}
7273
7274
/// Return whether a value can be discarded by bulk arena reclamation.
7275
/// Pointer lifetimes are checked when their values are constructed.
7276
unsafe fn isBulkDiscardable(ty: Type) -> bool {
7277
    match ty {
7278
        case Type::Void, Type::Bool, Type::U8, Type::U16, Type::U32, Type::U64,
7279
             Type::I8, Type::I16, Type::I32, Type::I64, Type::Fn(_) => return true,
7280
        case Type::Pointer { .. }, Type::Slice { .. }, Type::TraitObject { .. } => return true,
7281
        case Type::Cell { .. } => return true,
7282
        case Type::Array(array) => return isBulkDiscardable(*array.item),
7283
        case Type::Optional(inner) => return isBulkDiscardable(*inner),
7284
        case Type::Nominal(NominalType::Record(recordType)) => {
7285
            if recordType.declaredLinear {
7286
                return false;
7287
            }
7288
            for field in recordType.fields {
7289
                if not isBulkDiscardable(field.fieldType) {
7290
                    return false;
7291
                }
7292
            }
7293
            return true;
7294
        }
7295
        case Type::Nominal(NominalType::Union(unionType)) => {
7296
            if unionType.declaredLinear {
7297
                return false;
7298
            }
7299
            for variant in unionType.variants {
7300
                if not isBulkDiscardable(variant.valueType) {
7301
                    return false;
7302
                }
7303
            }
7304
            return true;
7305
        }
7306
        else => return false,
7307
    }
7308
}
7309
7310
/// Compute the end of an aligned layout within the allocator's byte-count range.
7311
fn allocationLayoutEnd(offset: u64, layout: Layout) -> ?u64 {
7312
    let mut aligned = offset;
7313
    if layout.alignment > 0 {
7314
        let mask = (layout.alignment - 1) as u64;
7315
        set aligned = (offset + mask) & ~mask;
7316
    }
7317
    let end = aligned + layout.size as u64;
7318
    if end > 4294967295 {
7319
        return nil;
7320
    }
7321
    return end;
7322
}
7323
7324
/// Check allocation layout arithmetic independently of the stored narrow offsets.
7325
unsafe fn hasAllocationLayout(ty: Type) -> bool {
7326
    let layout = getTypeLayout(ty);
7327
    match ty {
7328
        case Type::Cell { .. } => return true,
7329
        case Type::Array(array) => {
7330
            if not hasAllocationLayout(*array.item) {
7331
                return false;
7332
            }
7333
            let item = getTypeLayout(*array.item);
7334
            return item.size as u64 * array.length as u64 == layout.size as u64;
7335
        }
7336
        case Type::Optional(inner) => {
7337
            if not hasAllocationLayout(*inner) {
7338
                return false;
7339
            }
7340
            if isNullableType(*inner) {
7341
                return true;
7342
            }
7343
            let end = allocationLayoutEnd(1, getTypeLayout(*inner)) else return false;
7344
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment: layout.alignment }) else return false;
7345
            return total == layout.size as u64;
7346
        }
7347
        case Type::Nominal(NominalType::Record(recordType)) => {
7348
            let mut offset: u64 = 0;
7349
            for field in recordType.fields {
7350
                if not hasAllocationLayout(field.fieldType) {
7351
                    return false;
7352
                }
7353
                let fieldLayout = getTypeLayout(field.fieldType);
7354
                let end = allocationLayoutEnd(offset, fieldLayout) else return false;
7355
                let start = end - fieldLayout.size as u64;
7356
                if start > 2147483647 or field.offset < 0 or start <> field.offset as u64 {
7357
                    return false;
7358
                }
7359
                set offset = end;
7360
            }
7361
            let total = allocationLayoutEnd(offset, Layout { size: 0, alignment: layout.alignment }) else return false;
7362
            return total == layout.size as u64;
7363
        }
7364
        case Type::Nominal(NominalType::Union(unionType)) => {
7365
            let mut payloadSize: u32 = 0;
7366
            let mut alignment: u32 = 1;
7367
            for variant in unionType.variants {
7368
                if not hasAllocationLayout(variant.valueType) {
7369
                    return false;
7370
                }
7371
                let item = getTypeLayout(variant.valueType);
7372
                set payloadSize = max(payloadSize, item.size);
7373
                set alignment = max(alignment, item.alignment);
7374
            }
7375
            let end = allocationLayoutEnd(1, Layout { size: payloadSize, alignment }) else return false;
7376
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment }) else return false;
7377
            return total == layout.size as u64 and end - payloadSize as u64 == unionType.valOffset as u64;
7378
        }
7379
        else => return true,
7380
    }
7381
}
7382
7383
/// Validate the reservation ABI used by typed session allocation.
7384
unsafe fn sessionRuntime 'arena (
7385
    self: &mut Resolver 'arena, node: *ast::Node, slice: bool
7386
) -> TraitMethod
7387
    throws (ResolveError)
7388
{
7389
    let allocTrait = allocationSymbol(self, "Alloc")
7390
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7391
    let case SymbolData::Trait(allocInfo) = allocTrait.data
7392
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7393
    let name = "reserveSlice" if slice else "reserve";
7394
    let method = findTraitMethod(&allocInfo.methods[..], name)
7395
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7396
    let error = allocationSymbol(self, "AllocError")
7397
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7398
    let case SymbolData::Type(errorType) = error.data
7399
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7400
    let count: u32 = 3 if slice else 2;
7401
    let info = method.fnType;
7402
    if info.regions <> nil or not info.isUnsafe or info.paramTypes.len <> count or info.throwList.len <> 1 {
7403
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7404
    }
7405
    if not typesEqual(*info.throwList[0], Type::Nominal(errorType)) {
7406
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7407
    }
7408
    for i in 0..count {
7409
        if *info.paramTypes[i] <> Type::U32 {
7410
            throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7411
        }
7412
    }
7413
    let expected = Type::Slice { class: types::PointerClass::Unsafe, item: allocType(self, Type::Opaque), mutable: true }
7414
        if slice else Type::Pointer {
7415
            class: types::PointerClass::Unsafe, target: allocType(self, Type::Opaque), mutable: true
7416
        };
7417
    if not typesEqual(*info.returnType, expected) {
7418
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
7419
    }
7420
    return method;
7421
}
7422
7423
/// Check initialized session allocation and retain its source region in the result.
7424
unsafe fn resolveSessionAllocation 'arena (
7425
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, access: ast::Access,
7426
    region: *unsafe types::Region, ctx: CallCtx, hint: Type
7427
) -> Type throws (ResolveError) {
7428
    let name = try nodeName(self, access.child);
7429
    let mut kind = SessionAllocationKind::New;
7430
    if mem::eq(name, "copy") {
7431
        set kind = SessionAllocationKind::Copy;
7432
    }
7433
    else if mem::eq(name, "fill") {
7434
        set kind = SessionAllocationKind::Fill;
7435
    }
7436
    else if not mem::eq(name, "new") {
7437
        throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(name));
7438
    }
7439
    let count: u32 = 2 if kind == SessionAllocationKind::Fill else 1;
7440
    if call.args.len <> count {
7441
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { expected: count, actual: call.args.len }));
7442
    }
7443
    let slice = kind <> SessionAllocationKind::New;
7444
    let mut itemHint = Type::Unknown;
7445
    if kind == SessionAllocationKind::New {
7446
        if let case Type::Pointer { target, .. } = hint {
7447
            set itemHint = *target;
7448
        }
7449
    } else if kind == SessionAllocationKind::Fill {
7450
        if let case Type::Slice { item, .. } = hint {
7451
            set itemHint = *item;
7452
        }
7453
    } else {
7454
        set itemHint = Type::Slice {
7455
            class: types::PointerClass::Ref, item: allocType(self, Type::Unknown), mutable: false,
7456
        };
7457
    }
7458
    let valueType = try visit(self, call.args[0], itemHint);
7459
    let mut itemType = valueType;
7460
    let mut parameter = valueType;
7461
    if kind == SessionAllocationKind::Copy {
7462
        let case Type::Slice { item, .. } = valueType
7463
            else throw emitError(self, call.args[0], ErrorKind::ExpectedIndexable);
7464
        set itemType = *item;
7465
        set parameter = Type::Slice { class: types::PointerClass::Ref, item, mutable: false };
7466
    }
7467
    if not isTypeInferrable(itemType) or itemType == Type::Void or itemType == Type::Opaque {
7468
        throw emitError(self, call.args[0], ErrorKind::CannotInferType);
7469
    }
7470
    try ensureStorableType(self, call.args[0], itemType);
7471
    try ensureTypeResolved(self, itemType, call.args[0]);
7472
    try validateRegionStorage(self, call.args[0], itemType, region);
7473
    if not isBulkDiscardable(itemType) or (slice and not isCopy(itemType)) {
7474
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationValue);
7475
    }
7476
    if not hasAllocationLayout(itemType) {
7477
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationLayout);
7478
    }
7479
    let runtime = try sessionRuntime(self, node, slice);
7480
    let runtimeType = runtime.fnType;
7481
    let item = allocType(self, itemType);
7482
    let result = Type::Slice { class: types::PointerClass::Region(region), item, mutable: true }
7483
        if slice else Type::Pointer {
7484
            class: types::PointerClass::Region(region), target: item, mutable: true
7485
        };
7486
    let a = alloc::arenaAllocator(self.arena);
7487
    let mut parameters: *mut [*Type] = &mut [];
7488
    parameters.append(allocType(self, parameter), a);
7489
    if kind == SessionAllocationKind::Fill {
7490
        parameters.append(allocType(self, Type::U32), a);
7491
    }
7492
    let info = allocFnType(self, FnType {
7493
        regions: nil, paramTypes: &parameters[..], returnType: allocType(self, result),
7494
        throwList: runtimeType.throwList, isUnsafe: false,
7495
    });
7496
    try checkCallArgs(self, node, call, info, ctx);
7497
    setNodeType(self, call.callee, Type::Fn(info));
7498
    let allocTrait = allocationSymbol(self, "Alloc") else panic;
7499
    let case SymbolData::Trait(traitInfo) = allocTrait.data else panic;
7500
    set self.nodeData.entries[node.id].extra = NodeExtra::SessionAllocation(SessionAllocation {
7501
        kind, item, traitInfo, methodIndex: runtime.index,
7502
    });
7503
    return setNodeType(self, node, result);
7504
}
7505
7506
/// Return whether a nominal declaration explicitly derives Copy.
7507
fn nominalDeclaresCopy(node: *ast::Node) -> bool {
7508
    let mut derives: *[*ast::Node] = &[];
7509
    match node.value {
7510
        case ast::NodeValue::RecordDecl(decl) => set derives = decl.derives,
7511
        case ast::NodeValue::UnionDecl(decl) => set derives = decl.derives,
7512
        else => return false,
7513
    }
7514
    for derive in derives {
7515
        if let case ast::NodeValue::Ident(name) = derive.value {
7516
            if mem::eq(name, "Copy") {
7517
                return true;
7518
            }
7519
        }
7520
    }
7521
    return false;
7522
}
7523
7524
/// Require a complete payload that can be copied and discarded by value.
7525
unsafe fn validateCellPayload 'arena (self: &mut Resolver 'arena, node: *ast::Node, payload: Type) throws (ResolveError) {
7526
    try ensureStorableType(self, node, payload);
7527
    if let case Type::Nominal(info) = payload {
7528
        let mut source = info;
7529
        if let case NominalType::Application(applied) = *source {
7530
            set source = applied.base;
7531
        }
7532
        if let case NominalType::Resolving(decl) = *source {
7533
            if nominalDeclaresCopy(decl) {
7534
                return;
7535
            }
7536
            throw emitError(self, node, ErrorKind::InvalidCellPayload);
7537
        }
7538
    }
7539
    try ensureTypeResolved(self, payload, node);
7540
    if not isTypeInferrable(payload) or payload == Type::Void or payload == Type::Opaque
7541
        or not isCopy(payload) or not isBulkDiscardable(payload)
7542
    {
7543
        throw emitError(self, node, ErrorKind::InvalidCellPayload);
7544
    }
7545
    if not hasAllocationLayout(payload) {
7546
        throw emitError(self, node, ErrorKind::InvalidAllocationLayout);
7547
    }
7548
}
7549
7550
/// Analyze a function call expression.
7551
unsafe fn resolveCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, ctx: CallCtx, hint: Type) -> Type
7552
    throws (ResolveError)
7553
{
7554
    // Intercept method calls on slices before inferring the callee.
7555
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
7556
        let parentTy = try infer(self, access.parent);
7557
        if isUnsafePointerType(parentTy) {
7558
            try requireUnsafe(self, access.parent);
7559
        }
7560
        let subjectTy = autoDeref(parentTy);
7561
        if let case Type::Session(region) = subjectTy {
7562
            return try resolveSessionAllocation(self, node, call, access, region, ctx, hint);
7563
        }
7564
7565
        if let case Type::Slice { item, mutable, .. } = subjectTy {
7566
            let methodName = try nodeName(self, access.child);
7567
            if methodName == "append" {
7568
                return try resolveSliceAppend(
7569
                    self, node, access.parent, parentTy, call.args, item, mutable
7570
                );
7571
            }
7572
            if methodName == "delete" {
7573
                return try resolveSliceDelete(
7574
                    self, node, access.parent, call.args, item, mutable
7575
                );
7576
            }
7577
        }
7578
    }
7579
    let calleeTy = try visit(self, call.callee, hint);
7580
    if let case Type::Fn(info) = calleeTy {
7581
        try checkUnsafeCall(self, call.callee, info);
7582
    }
7583
7584
    // Check if callee is a union variant and dispatch to constructor handler.
7585
    // TODO: Move this out. We should decide on this earlier, based on the callee.
7586
    if let calleeSym = symbolFor(self, call.callee) {
7587
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
7588
            // TODO: Don't pass the callee type, pass the union type by getting it from
7589
            // the symbol.
7590
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid variant type";
7591
            return try resolveUnionConstructorCall(self, node, call, ty);
7592
        }
7593
        // Check if callee is an unlabeled record type for constructor call syntax.
7594
        if let case SymbolData::Type(_) = calleeSym.data {
7595
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid type callee";
7596
            try requireNominalArguments(self, ty, call.callee);
7597
            // Ensure the record body is resolved before checking if labeled.
7598
            try ensureNominalResolved(self, ty, call.callee);
7599
            if let case NominalType::Record(recInfo) = *ty {
7600
                if not recInfo.labeled {
7601
                    return try resolveRecordConstructorCall(self, node, call, ty);
7602
                }
7603
            }
7604
        }
7605
    }
7606
7607
    // Check if we have a trait method call, ie. callee is a trait object.
7608
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
7609
        let mut parentTy = Type::Unknown;
7610
        if let t = typeFor(self, access.parent) {
7611
            set parentTy = t;
7612
        }
7613
        let subjectTy = autoDeref(parentTy);
7614
7615
        if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy {
7616
            let methodName = try nodeName(self, access.child);
7617
            let method = findTraitMethod(&traitInfo.methods[..], methodName)
7618
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
7619
7620
            // Reject mutable-receiver methods called on immutable trait objects.
7621
            if method.mutable {
7622
                if not objMutable or not try canMutateThrough(self, access.parent) {
7623
                    throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
7624
                }
7625
            }
7626
            let applied = try instantiateCall(self, node, call, method.fnType);
7627
            try checkCallArgs(self, node, call, applied, ctx);
7628
            setTraitMethodCall(self, node, traitInfo, method.index);
7629
7630
            return setNodeType(self, node, *applied.returnType);
7631
        }
7632
7633
        // Check for a standalone method call on a concrete type.
7634
        if let case Type::Nominal(_) = subjectTy {
7635
            let methodName = try nodeName(self, access.child);
7636
            if let method = findMethod(self, subjectTy, methodName) {
7637
                // Reject mutable-receiver methods on immutable bindings.
7638
                // If the parent is already a mutable pointer, the receiver is fine.
7639
                // Otherwise, check that the parent can yield a mutable borrow.
7640
                if method.mutable {
7641
                    if not try canMutateThrough(self, access.parent) {
7642
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
7643
                    }
7644
                }
7645
                // Check arguments (excluding receiver).
7646
                let applied = try instantiateMethodCall(
7647
                    self, node, call, access.parent, subjectTy, method
7648
                );
7649
                try checkCallArgs(self, node, call, applied, ctx);
7650
                set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
7651
7652
                return setNodeType(self, node, *applied.returnType);
7653
            }
7654
        }
7655
    }
7656
    let case Type::Fn(info) = calleeTy else {
7657
        throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch {
7658
            expected: Type::Unknown,
7659
            actual: calleeTy,
7660
        }));
7661
    };
7662
    let applied = try instantiateCall(self, node, call, info);
7663
    try checkCallArgs(self, node, call, applied, ctx);
7664
    // Associate function type to callee.
7665
    setNodeType(self, call.callee, Type::Fn(applied));
7666
7667
    // Associate return type to call.
7668
    return setNodeType(self, node, *applied.returnType);
7669
}
7670
7671
/// Check labeled record fields against the slice allocator layout and callback ABI.
7672
fn isSliceAllocator(fields: &[RecordField]) -> bool {
7673
    if fields.len <> 2 {
7674
        return false;
7675
    }
7676
    let func = fields[0];
7677
    let ctx = fields[1];
7678
    let funcName = func.name else return false;
7679
    let ctxName = ctx.name else return false;
7680
    if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or
7681
       func.offset <> 0 or ctx.offset <> 8
7682
    {
7683
        return false;
7684
    }
7685
    let case Type::Fn(callback) = func.fieldType else return false;
7686
    let case Type::Pointer { target, .. } = ctx.fieldType else return false;
7687
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 {
7688
        return false;
7689
    }
7690
    if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or
7691
       *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32
7692
    {
7693
        return false;
7694
    }
7695
    let case Type::Pointer { class, target: result, mutable } = *callback.returnType
7696
        else return false;
7697
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
7698
}
7699
7700
/// Resolve `slice.append(val, allocator)`.
7701
unsafe fn resolveSliceAppend 'arena (
7702
    self: &mut Resolver 'arena,
7703
    node: *ast::Node,
7704
    parent: *ast::Node,
7705
    parentType: Type,
7706
    args: *[*ast::Node],
7707
    elemType: *Type,
7708
    mutable: bool
7709
) -> Type throws (ResolveError) {
7710
    if not mutable {
7711
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
7712
    }
7713
    if args.len <> 2 {
7714
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7715
            expected: 2,
7716
            actual: args.len as u32,
7717
        }));
7718
    }
7719
    // First argument must be assignable to the element type.
7720
    try checkAssignable(self, args[0], *elemType);
7721
    // The allocator stores its callback and context at fixed offsets.
7722
    let allocatorTy = try infer(self, args[1]);
7723
    if let case Type::Nominal(info) = allocatorTy {
7724
        try ensureNominalResolved(self, info, args[1]);
7725
    }
7726
    let mut validAllocator = false;
7727
    if let case Type::Nominal(NominalType::Record(rec)) = allocatorTy; rec.labeled {
7728
        set validAllocator = isSliceAllocator(&rec.fields[..]);
7729
    }
7730
    if not validAllocator {
7731
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
7732
    }
7733
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
7734
7735
    // Return the parent's type so the caller can rebind:
7736
    return setNodeType(self, node, parentType);
7737
}
7738
7739
/// Resolve `slice.delete(index)`.
7740
unsafe fn resolveSliceDelete 'arena (
7741
    self: &mut Resolver 'arena,
7742
    node: *ast::Node,
7743
    parent: *ast::Node,
7744
    args: *[*ast::Node],
7745
    elemType: *Type,
7746
    mutable: bool
7747
) -> Type throws (ResolveError) {
7748
    if not mutable {
7749
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
7750
    }
7751
    if args.len <> 1 {
7752
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7753
            expected: 1,
7754
            actual: args.len as u32,
7755
        }));
7756
    }
7757
    try checkAssignable(self, args[0], Type::U32);
7758
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType };
7759
7760
    return setNodeType(self, node, Type::Void);
7761
}
7762
7763
/// Analyze an assignment expression.
7764
unsafe fn resolveAssign 'arena (self: &mut Resolver 'arena, node: *ast::Node, assign: ast::Assign) -> Type
7765
    throws (ResolveError)
7766
{
7767
    // Slice assignment: `slice[range] = value`.
7768
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
7769
        if let case ast::NodeValue::Range(range) = index.value {
7770
            try infer(self, index);
7771
            let containerTy = try infer(self, container);
7772
            if not try canMutateThrough(self, container) {
7773
                throw emitError(self, container, ErrorKind::ImmutableBinding);
7774
            }
7775
            let subjectTy = autoDeref(containerTy);
7776
            try checkSliceRangeIndices(self, range);
7777
7778
            let info = sliceRangeInfo(subjectTy)
7779
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
7780
            if not info.mutable {
7781
                throw emitError(self, container, ErrorKind::ImmutableBinding);
7782
            }
7783
            if let capacity = info.capacity {
7784
                try validateArraySliceBounds(self, range, capacity, node);
7785
            }
7786
            let item = info.itemType;
7787
            // RHS is either a fill value or a source slice.
7788
            let rhsTy = try infer(self, assign.right);
7789
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
7790
                if *sourceItem <> *item {
7791
                    throw emitTypeMismatch(
7792
                        self,
7793
                        assign.right,
7794
                        TypeMismatch { expected: *item, actual: *sourceItem },
7795
                    );
7796
                }
7797
            } else {
7798
                try checkAssignable(self, assign.right, *item);
7799
            }
7800
            try validateRegionalStore(self, assign.left, assign.right, *item);
7801
            setSliceRangeInfo(self, node, info);
7802
            setNodeType(self, assign.left, *item);
7803
7804
            return setNodeType(self, node, Type::Void);
7805
        }
7806
    }
7807
    let leftTy = try infer(self, assign.left);
7808
7809
    if let case ast::NodeValue::Deref(target) = assign.left.value {
7810
        if let case Type::Cell { class, .. } = try infer(self, target) {
7811
            try checkAssignable(self, assign.right, leftTy);
7812
            if let case types::PointerClass::Region(region) = class {
7813
                try validateRegionStorage(self, assign.right, leftTy, region);
7814
            } else if class == types::PointerClass::Owned and containsRegion(leftTy) {
7815
                throw emitError(self, assign.right, ErrorKind::InvalidCellPayload);
7816
            }
7817
            return setNodeType(self, node, leftTy);
7818
        }
7819
    }
7820
7821
    // Check if the left-hand side can be assigned to by checking if it's a mutable location.
7822
    if not try canBorrowMutFrom(self, assign.left) {
7823
        throw emitError(self, assign.left, ErrorKind::ImmutableBinding);
7824
    }
7825
    try checkAssignable(self, assign.right, leftTy);
7826
    try validateRegionalStore(self, assign.left, assign.right, leftTy);
7827
7828
    return setNodeType(self, node, leftTy);
7829
}
7830
7831
/// Construct complete range metadata for an array or slice type.
7832
/// Callers check array place access and select the resulting borrow access.
7833
fn sliceRangeInfo(ty: Type) -> ?SliceRangeInfo {
7834
    match ty {
7835
        case Type::Slice { item, mutable, .. } =>
7836
            return SliceRangeInfo { itemType: item, mutable, capacity: nil },
7837
        case Type::Array(array) =>
7838
            return SliceRangeInfo { itemType: array.item, mutable: true, capacity: array.length },
7839
        else => return nil,
7840
    }
7841
}
7842
7843
/// Ensure slice range bounds are valid `u32` values.
7844
unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) {
7845
    if let start = range.start {
7846
        try checkIndex(self, start);
7847
    }
7848
    if let end = range.end {
7849
        try checkIndex(self, end);
7850
    }
7851
}
7852
7853
/// Emit an error when constant slice bounds exceed the array length or are reversed.
7854
fn validateArraySliceBounds 'arena (self: &mut Resolver 'arena, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
7855
    let mut startVal: ?u32 = nil;
7856
    let mut endVal: ?u32 = length;
7857
7858
    if let startNode = range.start {
7859
        if let val = constSliceIndex(self, startNode) {
7860
            set startVal = val;
7861
        }
7862
    }
7863
    if let endNode = range.end {
7864
        if let val = constSliceIndex(self, endNode) {
7865
            set endVal = val;
7866
        }
7867
    }
7868
    if let val = startVal; val > length {
7869
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
7870
    }
7871
    if let val = endVal; val > length {
7872
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
7873
    }
7874
    if let start = startVal {
7875
        if let end = endVal; start > end {
7876
            throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
7877
        }
7878
    }
7879
}
7880
7881
/// Check that an index expression has an unsigned integer type.
7882
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
7883
/// Smaller types are widened to `u32` via a numeric cast coercion.
7884
unsafe fn checkIndex 'arena (self: &mut Resolver 'arena, indexNode: *ast::Node) throws (ResolveError) {
7885
    let indexTy = try visit(self, indexNode, Type::U32);
7886
    if indexTy == Type::Int or indexTy == Type::U32 {
7887
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
7888
        return;
7889
    }
7890
    match indexTy {
7891
        case Type::U8, Type::U16 => {
7892
            setNodeCoercion(self, indexNode, Coercion::NumericCast {
7893
                from: indexTy, to: Type::U32,
7894
            });
7895
        }
7896
        else => {
7897
            throw emitTypeMismatch(self, indexNode, TypeMismatch {
7898
                expected: Type::U32,
7899
                actual: indexTy,
7900
            });
7901
        }
7902
    }
7903
}
7904
7905
/// Analyze an array or slice subscript expression.
7906
unsafe fn resolveSubscript 'arena (self: &mut Resolver 'arena, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
7907
    throws (ResolveError)
7908
{
7909
    // Range subscripts always require `&` to form a slice.
7910
    if let case ast::NodeValue::Range(range) = indexNode.value {
7911
        let _ = try infer(self, indexNode);
7912
        let _ = try infer(self, container);
7913
        try checkSliceRangeIndices(self, range);
7914
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
7915
    }
7916
    let containerTy = try infer(self, container);
7917
    if isUnsafePointerType(containerTy) {
7918
        try requireUnsafe(self, container);
7919
    }
7920
    try checkIndex(self, indexNode);
7921
    let subjectTy = autoDeref(containerTy);
7922
    if let case Type::Slice { item, .. } = subjectTy {
7923
        return setNodeType(self, node, *item);
7924
    }
7925
7926
    match subjectTy {
7927
        case Type::Array(arrayInfo) => {
7928
            return setNodeType(self, node, *arrayInfo.item);
7929
        }
7930
        else => {
7931
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
7932
        }
7933
    }
7934
}
7935
7936
/// Find a record field by name.
7937
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
7938
    for field, i in fields {
7939
        if let name = field.name {
7940
            if name == fieldName {
7941
                return i;
7942
            }
7943
        }
7944
    }
7945
    return nil;
7946
}
7947
7948
/// Analyze a union constructor call with payload.
7949
unsafe fn resolveUnionConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
7950
    throws (ResolveError)
7951
{
7952
    // Get the union nominal type.
7953
    let case NominalType::Union(unionType) = *unionNominal
7954
        else panic "resolveUnionConstructorCall: not a union type";
7955
7956
    // Callee was already visited; get the variant index it set.
7957
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
7958
        throw emitError(self, call.callee, ErrorKind::Internal);
7959
    };
7960
    let variant = &unionType.variants[index];
7961
7962
    // Associate variant index with `call` node for the lowerer.
7963
    setVariantInfo(self, node, index, tag);
7964
7965
    // Check if this variant expects a payload.
7966
    let payloadType = variant.valueType;
7967
    if payloadType <> Type::Void {
7968
        try ensureTypeResolved(self, payloadType, node);
7969
        let recInfo = getRecord(payloadType)
7970
            else panic "resolveUnionVariantConstructor: payload is not a record";
7971
        try checkRecordConstructorArgs(self, node, call.args, recInfo);
7972
    } else {
7973
        if call.args.len > 0 {
7974
            throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
7975
        }
7976
    }
7977
    return setNodeType(self, node, Type::Nominal(unionNominal));
7978
}
7979
7980
/// Analyze an unlabeled record constructor call.
7981
///
7982
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
7983
/// number of arguments matches the record's field count and that each argument
7984
/// is assignable to its corresponding field type.
7985
unsafe fn resolveRecordConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
7986
    throws (ResolveError)
7987
{
7988
    let case NominalType::Record(recInfo) = *recordType
7989
        else panic "resolveRecordConstructorCall: not a record type";
7990
7991
    try checkRecordConstructorArgs(self, node, call.args, recInfo);
7992
    return setNodeType(self, node, Type::Nominal(recordType));
7993
}
7994
7995
/// Resolve the type name of a record literal, handling both record types and
7996
/// union variant payloads like `Union::Variant { ... }`.
7997
unsafe fn resolveRecordLitType 'arena (
7998
    self: &mut Resolver 'arena, node: *ast::Node, typeIdent: *ast::Node, hint: Type
7999
) -> ResolvedRecordLitType
8000
    throws (ResolveError)
8001
{
8002
    if let case ast::NodeValue::RegionApply { .. } = typeIdent.value {
8003
        let ty = try infer(self, typeIdent);
8004
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::ExpectedRecord);
8005
        return ResolvedRecordLitType { recordType: info, resultType: ty };
8006
    }
8007
    // Check if this is a scope access that might be a union variant.
8008
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
8009
        let scope = self.scope;
8010
        let sym = try resolveAccess(self, typeIdent, access, scope);
8011
8012
        // Check if resolved symbol is a union variant.
8013
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
8014
            let sourceTy = typeFor(self, typeIdent) else panic "resolveRecordLitType: missing union type";
8015
            let case Type::Nominal(source) = sourceTy else panic "resolveRecordLitType: invalid union type";
8016
            let unionNominalType = hintedNominal(source, hint);
8017
            try requireNominalArguments(self, unionNominalType, typeIdent);
8018
            try ensureNominalResolved(self, unionNominalType, typeIdent);
8019
            let case NominalType::Union(body) = *unionNominalType else panic;
8020
            let case Type::Nominal(payloadInfo) = body.variants[ordinal].valueType
8021
                else throw emitError(self, node, ErrorKind::ExpectedRecord);
8022
8023
            // Store the variant index for the lowerer.
8024
            setVariantInfo(self, node, ordinal, index);
8025
8026
            return ResolvedRecordLitType {
8027
                recordType: payloadInfo,
8028
                resultType: Type::Nominal(unionNominalType),
8029
            };
8030
        }
8031
        // Not a variant, must be a type.
8032
        let case SymbolData::Type(ty) = sym.data
8033
            else throw emitError(self, node, ErrorKind::ExpectedRecord);
8034
        return ResolvedRecordLitType {
8035
            recordType: ty,
8036
            resultType: Type::Nominal(ty),
8037
        };
8038
    }
8039
    // Simple identifier, resolve as type name.
8040
    let tyInfo = try resolveTypeName(self, typeIdent);
8041
    return ResolvedRecordLitType {
8042
        recordType: tyInfo,
8043
        resultType: Type::Nominal(tyInfo),
8044
    };
8045
}
8046
8047
/// Analyze a record literal expression.
8048
unsafe fn resolveRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
8049
    throws (ResolveError)
8050
{
8051
    // If no type name, infer an anonymous tuple type.
8052
    let typeIdent = lit.typeName else {
8053
        return try resolveAnonRecordLit(self, node, lit, hint);
8054
    };
8055
    // Resolve the type name, handling both record types and union variants.
8056
    let resolved = try resolveRecordLitType(self, node, typeIdent, hint);
8057
    let mut tyInfo = resolved.recordType;
8058
    let mut resultType = resolved.resultType;
8059
    let mut target = hint;
8060
    if let case Type::Optional(inner) = target {
8061
        set target = *inner;
8062
    }
8063
    if nominalApplication(tyInfo) == nil {
8064
        if let case Type::Nominal(info) = target {
8065
            if let applied = nominalApplication(info); applied.base == tyInfo {
8066
                set tyInfo = info;
8067
                set resultType = target;
8068
            }
8069
        }
8070
    }
8071
    try requireNominalArguments(self, tyInfo, typeIdent);
8072
8073
    // Lazily resolve record body if not yet done.
8074
    try ensureNominalResolved(self, tyInfo, typeIdent);
8075
    let case NominalType::Record(recordType) = *tyInfo
8076
        else throw emitError(self, node, ErrorKind::ExpectedRecord);
8077
8078
    // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax.
8079
    if not recordType.labeled {
8080
        throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch);
8081
    }
8082
    // Check field count. With `{ .. }` syntax, fewer fields are allowed.
8083
    if lit.fields.len > recordType.fields.len {
8084
        throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
8085
            expected: recordType.fields.len as u32,
8086
            actual: lit.fields.len,
8087
        }));
8088
    }
8089
    if not lit.ignoreRest and lit.fields.len < recordType.fields.len {
8090
        let missingName = recordType.fields[lit.fields.len].name else panic;
8091
        throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
8092
    }
8093
8094
    // Fields must be in declaration order.
8095
    for fieldNode, idx in lit.fields {
8096
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
8097
            else panic "resolveRecordLit: expected field node value";
8098
        let label = fieldArg.label
8099
            else panic "resolveRecordLit: expected labeled field";
8100
        let fieldName = try nodeName(self, label);
8101
        let expected = recordType.fields[idx];
8102
        let expectedName = expected.name else panic;
8103
8104
        if fieldName <> expectedName {
8105
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
8106
                field: fieldName,
8107
                prev: expectedName,
8108
            });
8109
        }
8110
        setRecordFieldIndex(self, fieldNode, idx);
8111
        try checkAssignable(self, fieldArg.value, expected.fieldType);
8112
        setNodeType(self, fieldNode, expected.fieldType);
8113
    }
8114
    return setNodeType(self, node, resultType);
8115
}
8116
8117
/// Analyze an anonymous record literal, checking fields against the hint type.
8118
unsafe fn resolveAnonRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
8119
    throws (ResolveError)
8120
{
8121
    // Unwrap optional hint to get the inner record type.
8122
    let mut innerHint = hint;
8123
    if let case Type::Optional(inner) = hint {
8124
        set innerHint = *inner;
8125
    }
8126
    let mut hintInfo: ?RecordType = nil;
8127
    if let case Type::Nominal(info) = innerHint {
8128
        try ensureNominalResolved(self, info, node);
8129
        if let case NominalType::Record(s) = *info {
8130
            set hintInfo = s;
8131
        }
8132
    }
8133
    let targetInfo = hintInfo else {
8134
        throw emitError(self, node, ErrorKind::CannotInferType);
8135
    };
8136
8137
    // Check field count.
8138
    if lit.fields.len <> targetInfo.fields.len {
8139
        if lit.fields.len < targetInfo.fields.len {
8140
            let missingName = targetInfo.fields[lit.fields.len].name else panic;
8141
            throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
8142
        } else {
8143
            throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
8144
                expected: targetInfo.fields.len as u32,
8145
                actual: lit.fields.len,
8146
            }));
8147
        }
8148
    }
8149
8150
    // Fields must be in declaration order.
8151
    for fieldNode, idx in lit.fields {
8152
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
8153
            else panic "resolveAnonRecordLit: expected field node value";
8154
        let label = fieldArg.label
8155
            else panic "resolveAnonRecordLit: expected labeled field";
8156
        let fieldName = try nodeName(self, label);
8157
        let expected = targetInfo.fields[idx];
8158
        let expectedName = expected.name else panic;
8159
8160
        if fieldName <> expectedName {
8161
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
8162
                field: fieldName,
8163
                prev: expectedName,
8164
            });
8165
        }
8166
        setRecordFieldIndex(self, fieldNode, idx);
8167
        let fieldType = try visit(self, fieldArg.value, expected.fieldType);
8168
8169
        try expectAssignable(self, expected.fieldType, fieldType, fieldArg.value);
8170
        setNodeType(self, fieldNode, fieldType);
8171
    }
8172
    return setNodeType(self, node, innerHint);
8173
}
8174
8175
/// Analyze an array literal expression.
8176
unsafe fn resolveArrayLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
8177
    throws (ResolveError)
8178
{
8179
    let length = items.len;
8180
    let mut expectedTy: Type = Type::Unknown;
8181
8182
    if let case Type::Array(ary) = hint {
8183
        set expectedTy = *ary.item;
8184
    } else if let case Type::Optional(inner) = hint {
8185
        if let case Type::Array(ary) = *inner {
8186
            set expectedTy = *ary.item;
8187
        }
8188
    };
8189
    for itemNode in items {
8190
        let itemTy = try visit(self, itemNode, expectedTy);
8191
        assert itemTy <> Type::Unknown;
8192
8193
        // Set the expected type to the first type we encounter.
8194
        if expectedTy == Type::Unknown {
8195
            set expectedTy = itemTy;
8196
        } else {
8197
            try expectAssignable(self, expectedTy, itemTy, itemNode);
8198
        }
8199
    }
8200
    if expectedTy == Type::Unknown {
8201
        throw emitError(self, node, ErrorKind::CannotInferType);
8202
    };
8203
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
8204
    return setNodeType(self, node, arrayTy);
8205
}
8206
8207
/// Analyze an array repeat literal expression.
8208
unsafe fn resolveArrayRepeat 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
8209
    throws (ResolveError)
8210
{
8211
    let mut itemHint = hint;
8212
    if let case Type::Array(ary) = hint {
8213
        set itemHint = *ary.item;
8214
    } else if let case Type::Optional(inner) = hint {
8215
        if let case Type::Array(ary) = *inner {
8216
            set itemHint = *ary.item;
8217
        }
8218
    }
8219
    let valueTy = try visit(self, lit.item, itemHint);
8220
    let count = try checkSizeInt(self, lit.count);
8221
    let arrayTy = Type::Array(ArrayType {
8222
        item: allocType(self, valueTy),
8223
        length: count,
8224
    });
8225
    return setNodeType(self, node, arrayTy);
8226
}
8227
8228
/// Resolve union variant access.
8229
unsafe fn resolveUnionVariantAccess 'arena (
8230
    self: &mut Resolver 'arena,
8231
    node: *ast::Node,
8232
    access: ast::Access,
8233
    unionType: UnionType,
8234
    variantName: *[u8]
8235
) -> *unsafe mut Symbol throws (ResolveError) {
8236
    // Look up the variant in the union's nominal type.
8237
    for i in 0..unionType.variants.len {
8238
        let variant = &unionType.variants[i];
8239
        if variant.name == variantName {
8240
            let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data
8241
                else panic "resolveUnionVariantAccess: expected variant symbol";
8242
8243
            // Associate the variant symbol with the child node.
8244
            setNodeSymbol(self, access.child, variant.symbol);
8245
            setNodeSymbol(self, node, variant.symbol);
8246
8247
            // Store the variant index for the lowerer.
8248
            setVariantInfo(self, node, ordinal, index);
8249
8250
            return variant.symbol;
8251
        }
8252
    }
8253
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
8254
}
8255
8256
/// Analyze a scope access expression.
8257
unsafe fn resolveScopeAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access, hint: Type) -> Type
8258
    throws (ResolveError)
8259
{
8260
    let scope = self.scope;
8261
    let sym = try resolveAccess(self, node, access, scope);
8262
    try checkStaticAccess(self, node, sym);
8263
    let mut ty: Type = undefined;
8264
8265
    match sym.data {
8266
        case SymbolData::Value { type, .. } => {
8267
            setNodeSymbol(self, node, sym);
8268
            set ty = type;
8269
        }
8270
        case SymbolData::Constant { type, value } => {
8271
            // Propagate the constant value.
8272
            if let val = value {
8273
                setNodeConstValue(self, node, val);
8274
            }
8275
            setNodeSymbol(self, node, sym);
8276
            set ty = type;
8277
        }
8278
        case SymbolData::Type(t) => {
8279
            setNodeSymbol(self, node, sym);
8280
            set ty = Type::Nominal(hintedNominal(t, hint));
8281
        }
8282
        case SymbolData::Variant { index, .. } => {
8283
            let ty = typeFor(self, node)
8284
                else throw emitError(self, node, ErrorKind::Internal);
8285
            let case Type::Nominal(info) = ty else panic "resolveScopeAccess: invalid variant type";
8286
            let applied = hintedNominal(info, hint);
8287
            try requireNominalArguments(self, applied, node);
8288
            try ensureNominalResolved(self, applied, node);
8289
            let variantTy = Type::Nominal(applied);
8290
            // For unions without payload, store the variant index as a constant.
8291
            if isVoidUnion(variantTy) {
8292
                setNodeConstValue(self, node, ConstValue::Int(ConstInt {
8293
                    magnitude: index as u64,
8294
                    bits: 32,
8295
                    signed: false,
8296
                    negative: false,
8297
                }));
8298
            }
8299
            return setNodeType(self, node, variantTy);
8300
        }
8301
        case SymbolData::Module { .. } => {
8302
            throw emitError(self, node, ErrorKind::UnexpectedModuleName);
8303
        }
8304
        case SymbolData::Trait(_) => { // Trait names are not values.
8305
            throw emitError(self, node, ErrorKind::UnexpectedTraitName);
8306
        }
8307
    }
8308
    return setNodeType(self, node, ty);
8309
}
8310
8311
/// Analyze a field access expression.
8312
unsafe fn resolveFieldAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access) -> Type
8313
    throws (ResolveError)
8314
{
8315
    let parentTy = try infer(self, access.parent);
8316
    if isUnsafePointerType(parentTy) {
8317
        try requireUnsafe(self, access.parent);
8318
    }
8319
    let subjectTy = autoDeref(parentTy);
8320
    try ensureTypeResolved(self, subjectTy, access.parent);
8321
8322
    if let case Type::Slice { class, item, mutable } = subjectTy {
8323
        let fieldNode = access.child;
8324
        let fieldName = try nodeName(self, fieldNode);
8325
        if mem::eq(fieldName, PTR_FIELD) {
8326
            try requireUnsafe(self, node);
8327
            setRecordFieldIndex(self, fieldNode, 0);
8328
            return setNodeType(
8329
                self,
8330
                node,
8331
                Type::Pointer { class, target: item, mutable },
8332
            );
8333
        }
8334
        if mem::eq(fieldName, LEN_FIELD) {
8335
            setRecordFieldIndex(self, fieldNode, 1);
8336
            return setNodeType(self, node, Type::U32);
8337
        }
8338
        if mem::eq(fieldName, CAP_FIELD) {
8339
            setRecordFieldIndex(self, fieldNode, 2);
8340
            return setNodeType(self, node, Type::U32);
8341
        }
8342
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
8343
    }
8344
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
8345
        let fieldName = try nodeName(self, access.child);
8346
        let method = findTraitMethod(&traitInfo.methods[..], fieldName)
8347
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
8348
        return setNodeType(self, node, Type::Fn(method.fnType));
8349
    }
8350
8351
    match subjectTy {
8352
        case Type::Nominal(NominalType::Record(recordType)) => {
8353
            let fieldNode = access.child;
8354
            let fieldName = try nodeName(self, fieldNode);
8355
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
8356
                let fieldTy = recordType.fields[fieldIndex].fieldType;
8357
                setRecordFieldIndex(self, fieldNode, fieldIndex);
8358
                return setNodeType(self, node, fieldTy);
8359
            }
8360
            // Not a field: check for a standalone method.
8361
            if let method = findMethod(self, subjectTy, fieldName) {
8362
                return setNodeType(self, node, Type::Fn(method.fnType));
8363
            }
8364
            throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
8365
        }
8366
        case Type::Array(arrayInfo) => {
8367
            let fieldNode = access.child;
8368
            let fieldName = try nodeName(self, fieldNode);
8369
8370
            if mem::eq(fieldName, LEN_FIELD) {
8371
                let lengthConst = constInt(arrayInfo.length as u64, 32, false, false);
8372
                setNodeConstValue(self, node, lengthConst);
8373
8374
                return setNodeType(self, node, Type::U32);
8375
            }
8376
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
8377
        }
8378
8379
        else => {
8380
            // Check for standalone methods on any nominal type (e.g. unions).
8381
            if let case Type::Nominal(_) = subjectTy {
8382
                let fieldName = try nodeName(self, access.child);
8383
                if let method = findMethod(self, subjectTy, fieldName) {
8384
                    return setNodeType(self, node, Type::Fn(method.fnType));
8385
                }
8386
            }
8387
            throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
8388
        }
8389
    }
8390
}
8391
8392
/// Return whether a pointer-like value grants mutable access.
8393
fn isMutablePointerLike(ty: Type) -> bool {
8394
    match ty {
8395
        case Type::Pointer { mutable, .. } => return mutable,
8396
        case Type::Slice { mutable, .. } => return mutable,
8397
        case Type::TraitObject { mutable, .. } => return mutable,
8398
        else => return false,
8399
    }
8400
}
8401
8402
/// Check exclusive access to an element or field of a container.
8403
unsafe fn canAccessExclusiveProjection 'arena (self: &mut Resolver 'arena, container: *ast::Node) -> bool
8404
    throws (ResolveError)
8405
{
8406
    let ty = try infer(self, container);
8407
    if let case Type::Slice { mutable: false, .. } = autoDeref(ty) {
8408
        return false;
8409
    }
8410
    match ty {
8411
        case Type::Pointer { class, mutable, .. } => {
8412
            if not mutable {
8413
                return false;
8414
            }
8415
            if class == types::PointerClass::Unsafe {
8416
                return true;
8417
            }
8418
        }
8419
        case Type::Slice { class, mutable, .. } => {
8420
            if not mutable {
8421
                return false;
8422
            }
8423
            if class == types::PointerClass::Unsafe {
8424
                return true;
8425
            }
8426
        }
8427
        else => {
8428
        },
8429
    }
8430
    return try canAccessExclusiveHandle(self, container);
8431
}
8432
8433
/// Check that a stored exclusive handle is not reached through shared access.
8434
unsafe fn canAccessExclusiveHandle 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8435
    throws (ResolveError)
8436
{
8437
    match node.value {
8438
        case ast::NodeValue::FieldAccess(access) =>
8439
            return try canAccessExclusiveProjection(self, access.parent),
8440
        case ast::NodeValue::Subscript { container, .. } =>
8441
            return try canAccessExclusiveProjection(self, container),
8442
        case ast::NodeValue::Deref(inner) =>
8443
            return try canAccessExclusiveProjection(self, inner),
8444
        case ast::NodeValue::As(expr) =>
8445
            return try canAccessExclusiveHandle(self, expr.value),
8446
        case ast::NodeValue::CondExpr(cond) => {
8447
            if not try canAccessExclusiveHandle(self, cond.thenExpr) {
8448
                return false;
8449
            }
8450
            return try canAccessExclusiveHandle(self, cond.elseExpr);
8451
        }
8452
        else => return true,
8453
    }
8454
}
8455
8456
/// Check target mutability for implicit pointer access.
8457
unsafe fn canMutateThrough 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8458
    throws (ResolveError)
8459
{
8460
    let ty = try infer(self, node);
8461
    match ty {
8462
        case Type::Pointer { class, mutable, .. } => {
8463
            if not mutable {
8464
                return false;
8465
            }
8466
            if class == types::PointerClass::Unsafe {
8467
                return true;
8468
            }
8469
            return try canAccessExclusiveHandle(self, node);
8470
        }
8471
        case Type::Slice { class, mutable, .. } => {
8472
            if not mutable {
8473
                return false;
8474
            }
8475
            if class == types::PointerClass::Unsafe {
8476
                return true;
8477
            }
8478
            return try canAccessExclusiveHandle(self, node);
8479
        }
8480
        case Type::TraitObject { class, mutable, .. } => {
8481
            if not mutable {
8482
                return false;
8483
            }
8484
            if class == types::PointerClass::Unsafe {
8485
                return true;
8486
            }
8487
            return try canAccessExclusiveHandle(self, node);
8488
        }
8489
        else => return try canBorrowMutFrom(self, node),
8490
    }
8491
}
8492
8493
/// Determine whether an expression can yield a mutable location for borrowing.
8494
unsafe fn canBorrowMutFrom 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8495
    throws (ResolveError)
8496
{
8497
    match node.value {
8498
        case ast::NodeValue::Ident(name) => {
8499
            let sym = findValueSymbol(self.scope, name)
8500
                else return false;
8501
            let case SymbolData::Value { mutable, .. } = sym.data
8502
                else return false;
8503
            return mutable;
8504
        }
8505
        case ast::NodeValue::FieldAccess(access) => {
8506
            let parentTy = try infer(self, access.parent);
8507
            if let case Type::Slice { .. } = autoDeref(parentTy) {
8508
                try requireUnsafe(self, node);
8509
            }
8510
            return try canMutateThrough(self, access.parent);
8511
        }
8512
        case ast::NodeValue::ScopeAccess(_) => {
8513
            // Module-qualified access to a top-level symbol. A `static`
8514
            // binds as a mutable value; a `constant` does not.
8515
            let _ = try infer(self, node);
8516
            let sym = symbolFor(self, node)
8517
                else return false;
8518
8519
            if let case SymbolData::Value { mutable, .. } = sym.data {
8520
                return mutable;
8521
            }
8522
            return false;
8523
        }
8524
        case ast::NodeValue::Subscript { container, .. } => {
8525
            let containerTy = try infer(self, container);
8526
            // Subscript auto-derefs pointers, so check the actual indexed type.
8527
            let subjectTy = autoDeref(containerTy);
8528
8529
            if let case Type::Slice { mutable, .. } = subjectTy {
8530
                if not mutable {
8531
                    return false;
8532
                }
8533
                return try canMutateThrough(self, container);
8534
            }
8535
            if let case Type::Array(_) = subjectTy {
8536
                return try canMutateThrough(self, container);
8537
            }
8538
            return false;
8539
        }
8540
        case ast::NodeValue::ArrayLit(_),
8541
             ast::NodeValue::ArrayRepeatLit(_) =>
8542
        {
8543
            return true;
8544
        }
8545
        case ast::NodeValue::Call(_) => {
8546
            // A call returning `*mut T` (or `&mut [T]`) yields a
8547
            // mutable place. Non-pointer returns cannot be mutably borrowed.
8548
            let ty = try infer(self, node);
8549
            if let case Type::Pointer { mutable, .. } = ty {
8550
                return mutable;
8551
            }
8552
            if let case Type::Slice { mutable, .. } = ty {
8553
                return mutable;
8554
            }
8555
            return false;
8556
        }
8557
        case ast::NodeValue::Deref(inner) => {
8558
            let innerTy = try infer(self, inner);
8559
8560
            if let case Type::Pointer { .. } = innerTy {
8561
                return try canMutateThrough(self, inner);
8562
            }
8563
            if let case Type::Slice { .. } = innerTy {
8564
                return try canMutateThrough(self, inner);
8565
            }
8566
            // Record deref: mutability depends on the inner binding.
8567
            if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy {
8568
                if not recInfo.labeled and recInfo.fields.len == 1 {
8569
                    return try canBorrowMutFrom(self, inner);
8570
                }
8571
            }
8572
            return false;
8573
        }
8574
        else => {
8575
            return false;
8576
        }
8577
    }
8578
}
8579
8580
/// Restrict a pointee lifetime to the borrow of its exclusive owner.
8581
unsafe fn constrainAddressClass(storage: types::PointerClass, owner: types::PointerClass) -> types::PointerClass {
8582
    if owner == types::PointerClass::Owned or owner == types::PointerClass::Unsafe {
8583
        return storage;
8584
    }
8585
    if owner == types::PointerClass::Ref {
8586
        return owner;
8587
    }
8588
    let case types::PointerClass::Region(ownerRegion) = owner else panic;
8589
    if let case types::PointerClass::Region(storageRegion) = storage {
8590
        if types::regionContains(storageRegion, ownerRegion) {
8591
            return owner;
8592
        }
8593
        if types::regionContains(ownerRegion, storageRegion) {
8594
            return storage;
8595
        }
8596
        return types::PointerClass::Ref;
8597
    }
8598
    if storage == types::PointerClass::Owned {
8599
        return owner;
8600
    }
8601
    return storage;
8602
}
8603
8604
/// Get the usable pointee lifetime of a pointer or slice value.
8605
unsafe fn pointerAddressClass 'arena (
8606
    self: &Resolver 'arena, node: *ast::Node, class: types::PointerClass, mutable: bool
8607
) -> types::PointerClass {
8608
    if not mutable or class == types::PointerClass::Unsafe {
8609
        return class;
8610
    }
8611
    return constrainAddressClass(class, exclusiveOwnerClass(self, node));
8612
}
8613
8614
/// Get the lifetime of storage selected by a pointer or slice subscript.
8615
unsafe fn indexedPointerClass 'arena (self: &Resolver 'arena, container: *ast::Node) -> ?types::PointerClass {
8616
    let ty = typeFor(self, container) else return nil;
8617
    if let case Type::Pointer { class, target, mutable } = ty {
8618
        let parentClass = pointerAddressClass(self, container, class, mutable);
8619
        if let case Type::Slice { class: sliceClass, mutable: sliceMutable, .. } = *target {
8620
            if not sliceMutable or sliceClass == types::PointerClass::Unsafe {
8621
                return sliceClass;
8622
            }
8623
            return constrainAddressClass(sliceClass, parentClass);
8624
        }
8625
        return parentClass;
8626
    }
8627
    if let case Type::Slice { class, mutable, .. } = ty {
8628
        return pointerAddressClass(self, container, class, mutable);
8629
    }
8630
    return nil;
8631
}
8632
8633
/// Get the borrow that controls access to a stored exclusive handle.
8634
/// Directly owned values have no additional borrow restriction.
8635
unsafe fn exclusiveOwnerClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
8636
    match node.value {
8637
        case ast::NodeValue::FieldAccess(access) => {
8638
            if let ty = typeFor(self, access.parent) {
8639
                match ty {
8640
                    case Type::Pointer { class, mutable, .. } =>
8641
                        return pointerAddressClass(self, access.parent, class, mutable),
8642
                    case Type::Slice { class, mutable, .. } =>
8643
                        return pointerAddressClass(self, access.parent, class, mutable),
8644
                    else => {
8645
                    },
8646
                }
8647
            }
8648
            return exclusiveOwnerClass(self, access.parent);
8649
        }
8650
        case ast::NodeValue::Subscript { container, .. } => {
8651
            if let class = indexedPointerClass(self, container) {
8652
                return class;
8653
            }
8654
            return exclusiveOwnerClass(self, container);
8655
        }
8656
        case ast::NodeValue::Deref(target) => {
8657
            if let ty = typeFor(self, target) {
8658
                if let case Type::Pointer { class, mutable, .. } = ty {
8659
                    return pointerAddressClass(self, target, class, mutable);
8660
                }
8661
            }
8662
            return exclusiveOwnerClass(self, target);
8663
        }
8664
        case ast::NodeValue::As(expr) => return exclusiveOwnerClass(self, expr.value),
8665
        case ast::NodeValue::CondExpr(cond) =>
8666
            return constrainAddressClass(exclusiveOwnerClass(self, cond.thenExpr), exclusiveOwnerClass(self, cond.elseExpr)),
8667
        else => return types::PointerClass::Owned,
8668
    }
8669
}
8670
8671
/// Return the storage class of an addressed location.
8672
unsafe fn addressStorageClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
8673
    match node.value {
8674
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
8675
            if let sym = symbolFor(self, node) {
8676
                match sym.node.value {
8677
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
8678
                        return types::PointerClass::Owned,
8679
                    else => {}
8680
                }
8681
            }
8682
        }
8683
        case ast::NodeValue::FieldAccess(access) => {
8684
            if let ty = typeFor(self, access.parent) {
8685
                if let case Type::Pointer { class, mutable, .. } = ty {
8686
                    return pointerAddressClass(self, access.parent, class, mutable);
8687
                }
8688
            }
8689
            return addressStorageClass(self, access.parent);
8690
        }
8691
        case ast::NodeValue::Subscript { container, .. } => {
8692
            if let class = indexedPointerClass(self, container) {
8693
                return class;
8694
            }
8695
            return addressStorageClass(self, container);
8696
        }
8697
        case ast::NodeValue::Deref(target) => {
8698
            if let ty = typeFor(self, target) {
8699
                if let case Type::Pointer { class, mutable, .. } = ty {
8700
                    return pointerAddressClass(self, target, class, mutable);
8701
                }
8702
            }
8703
            return addressStorageClass(self, target);
8704
        }
8705
        else => {}
8706
    }
8707
    return types::PointerClass::Ref;
8708
}
8709
8710
/// Select an address type without extending the target storage lifetime.
8711
unsafe fn addressClass 'arena (self: &mut Resolver 'arena, target: *ast::Node, hint: Type) -> types::PointerClass
8712
    throws (ResolveError)
8713
{
8714
    if isUnsafePointerType(hint) {
8715
        try requireUnsafe(self, target);
8716
        return types::PointerClass::Unsafe;
8717
    }
8718
    if isRefType(hint) {
8719
        if referenceRegion(hint) <> nil {
8720
            return addressStorageClass(self, target);
8721
        }
8722
        return types::PointerClass::Ref;
8723
    }
8724
    match target.value {
8725
        case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => {
8726
            if isConstExpr(self, target) {
8727
                return types::PointerClass::Owned;
8728
            }
8729
        }
8730
        else => {}
8731
    }
8732
    return addressStorageClass(self, target);
8733
}
8734
8735
/// Return whether a place projects into a cell payload snapshot.
8736
unsafe fn isCellPayloadPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
8737
    throws (ResolveError)
8738
{
8739
    match node.value {
8740
        case ast::NodeValue::Deref(target) => {
8741
            if let case Type::Cell { .. } = try infer(self, target) {
8742
                return true;
8743
            }
8744
        }
8745
        case ast::NodeValue::FieldAccess(access) => return try isCellPayloadPlace(self, access.parent),
8746
        case ast::NodeValue::Subscript { container, .. } => return try isCellPayloadPlace(self, container),
8747
        else => {}
8748
    }
8749
    return false;
8750
}
8751
8752
/// Return whether a typed expression accesses a whole cell payload.
8753
export fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
8754
    if let case ast::NodeValue::Deref(target) = node.value {
8755
        if let ty = typeFor(self, target) {
8756
            if let case Type::Cell { .. } = ty {
8757
                return true;
8758
            }
8759
        }
8760
    }
8761
    return false;
8762
}
8763
8764
/// Return whether an expression creates shared mutable access to a source place.
8765
fn createsCellBorrow(node: *ast::Node) -> bool {
8766
    match node.value {
8767
        case ast::NodeValue::AddressOf(address) => return address.kind == ast::AddressKind::Cell,
8768
        case ast::NodeValue::As(expr) => return createsCellBorrow(expr.value),
8769
        case ast::NodeValue::RegionApply { value, .. } => return createsCellBorrow(value),
8770
        case ast::NodeValue::CondExpr(cond) =>
8771
            return createsCellBorrow(cond.thenExpr) or createsCellBorrow(cond.elseExpr),
8772
        else => return false,
8773
    }
8774
}
8775
8776
/// Find the exclusive handle that owns an addressed place.
8777
fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node {
8778
    let mut parent: ?*ast::Node = nil;
8779
    match node.value {
8780
        case ast::NodeValue::Deref(target) => set parent = target,
8781
        case ast::NodeValue::FieldAccess(access) => set parent = access.parent,
8782
        case ast::NodeValue::Subscript { container, .. } => set parent = container,
8783
        else => {}
8784
    }
8785
    let parentNode = parent else return nil;
8786
    if let ty = typeFor(self, parentNode) {
8787
        match ty {
8788
            case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parentNode,
8789
            else => {}
8790
        }
8791
    }
8792
    return addressOwner(self, parentNode);
8793
}
8794
8795
/// Analyze an address-of expression.
8796
unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
8797
    throws (ResolveError)
8798
{
8799
    if try isCellPayloadPlace(self, addr.target) {
8800
        throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8801
    }
8802
    if addr.kind == ast::AddressKind::Cell and not ast::isPlaceExpr(addr.target) {
8803
        throw emitError(self, addr.target, ErrorKind::RefBinding);
8804
    }
8805
    if ast::isExclusiveAddress(addr) {
8806
        if not try canBorrowMutFrom(self, addr.target) {
8807
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8808
        }
8809
    }
8810
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
8811
        if let case ast::NodeValue::Range(range) = index.value {
8812
            if addr.kind == ast::AddressKind::Cell {
8813
                throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
8814
            }
8815
            let containerTy = try infer(self, container);
8816
            let subjectTy = autoDeref(containerTy);
8817
8818
            try checkSliceRangeIndices(self, range);
8819
8820
            let mut info = sliceRangeInfo(subjectTy)
8821
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
8822
            if ast::isExclusiveAddress(addr) and not info.mutable {
8823
                throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8824
            }
8825
            if let capacity = info.capacity {
8826
                try validateArraySliceBounds(self, range, capacity, node);
8827
            }
8828
            set info.mutable = addr.kind == ast::AddressKind::Mutable;
8829
            let class = try addressClass(self, addr.target, hint);
8830
            let sliceTy = Type::Slice { class, item: info.itemType, mutable: info.mutable };
8831
            let alloc = allocType(self, sliceTy);
8832
            setSliceRangeInfo(self, node, info);
8833
            setNodeType(self, addr.target, *alloc);
8834
            return setNodeType(self, node, *alloc);
8835
        }
8836
    }
8837
    // Derive a hint for the target type from the slice hint.
8838
    let mut targetHint: Type = Type::Unknown;
8839
    if let case Type::Slice { item, .. } = hint {
8840
        set targetHint = Type::Array(ArrayType { item, length: 0 });
8841
    }
8842
    let targetTy = try visit(self, addr.target, targetHint);
8843
    let class = try addressClass(self, addr.target, hint);
8844
8845
    // Mark local variable symbols as address-taken so the lowerer
8846
    // allocates a stack slot eagerly.
8847
    if let case ast::NodeValue::Ident(name) = addr.target.value {
8848
        if let sym = findValueSymbol(self.scope, name) {
8849
            match &mut sym.data {
8850
                case SymbolData::Value { addressTaken, .. } => {
8851
                    set *addressTaken = true;
8852
                }
8853
                else => {}
8854
            }
8855
        }
8856
    }
8857
8858
    if addr.kind == ast::AddressKind::Cell {
8859
        try validateCellPayload(self, node, targetTy);
8860
        if let case types::PointerClass::Region(region) = class {
8861
            try validateRegionStorage(self, addr.target, targetTy, region);
8862
        } else if class == types::PointerClass::Owned and containsRegion(targetTy) {
8863
            throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
8864
        }
8865
        return setNodeType(self, node, Type::Cell { class, payload: allocType(self, targetTy) });
8866
    }
8867
    if let case Type::Array(arrayInfo) = targetTy {
8868
        match addr.target.value {
8869
            case ast::NodeValue::ArrayLit(_),
8870
                 ast::NodeValue::ArrayRepeatLit(_) =>
8871
            {
8872
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.kind == ast::AddressKind::Mutable };
8873
                return setNodeType(self, node, *allocType(self, sliceTy));
8874
            }
8875
            else => {}
8876
        }
8877
    }
8878
    let pointerTy = Type::Pointer {
8879
        class, target: allocType(self, targetTy), mutable: addr.kind == ast::AddressKind::Mutable,
8880
    };
8881
    return setNodeType(self, node, pointerTy);
8882
}
8883
8884
/// Analyze a dereference expression.
8885
unsafe fn resolveDeref 'arena (self: &mut Resolver 'arena, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
8886
    throws (ResolveError)
8887
{
8888
    let operandTy = try visit(self, targetNode, hint);
8889
    if let case Type::Cell { class, payload } = operandTy {
8890
        if class == types::PointerClass::Unsafe {
8891
            try requireUnsafe(self, targetNode);
8892
        }
8893
        try validateCellPayload(self, node, *payload);
8894
        return setNodeType(self, node, *payload);
8895
    }
8896
    if let case Type::Pointer { class, target, .. } = operandTy {
8897
        if class == types::PointerClass::Unsafe {
8898
            try requireUnsafe(self, targetNode);
8899
        }
8900
        // Disallow dereferencing opaque pointers.
8901
        if *target == Type::Opaque {
8902
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
8903
        }
8904
        return setNodeType(self, node, *target);
8905
    }
8906
    // Auto-deref for single-field unlabeled records.
8907
    if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy {
8908
        if not recInfo.labeled and recInfo.fields.len == 1 {
8909
            let fieldTy = recInfo.fields[0].fieldType;
8910
            setRecordFieldIndex(self, node, 0);
8911
            return setNodeType(self, node, fieldTy);
8912
        }
8913
    }
8914
    throw emitError(self, targetNode, ErrorKind::ExpectedPointer);
8915
}
8916
8917
/// Check if a type is a pointer to opaque.
8918
fn isOpaquePointer(ty: Type) -> bool {
8919
    if let case Type::Pointer { target, .. } = ty {
8920
        return *target == Type::Opaque;
8921
    }
8922
    return false;
8923
}
8924
8925
/// Check if a type is an opaque slice.
8926
fn isOpaqueSlice(ty: Type) -> bool {
8927
    if let case Type::Slice { item, .. } = ty {
8928
        return *item == Type::Opaque;
8929
    }
8930
    return false;
8931
}
8932
8933
/// Check if an `as` cast between two types is valid.
8934
unsafe fn isValidCast(source: Type, target: Type) -> bool {
8935
    // Allow identity casts.
8936
    if source == target {
8937
        return true;
8938
    }
8939
    // Allow numeric to numeric.
8940
    if isNumericType(source) and isNumericType(target) {
8941
        return true;
8942
    }
8943
    // Allow `void` union to numeric.
8944
    // TODO: Check that variant index fits in target type.
8945
    if isVoidUnion(source) and isNumericType(target) {
8946
        return true;
8947
    }
8948
    // Allow address to numeric.
8949
    if let case Type::Slice { .. } = source {
8950
        // Disallow slice to numeric; slices are fat pointers.
8951
    } else if isAddressType(source) and isNumericType(target) {
8952
        return true;
8953
    }
8954
    // Allow pointer casts if one side is `*opaque` or target types are castable.
8955
    if let case Type::Pointer {
8956
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
8957
    } = source {
8958
        if let case Type::Pointer {
8959
            class: targetClass, target: targetTarget, mutable: targetMutable,
8960
        } = target {
8961
            if sourceClass <> targetClass {
8962
                return false;
8963
            }
8964
            if targetMutable and not sourceMutable {
8965
                return false;
8966
            }
8967
            if isOpaquePointer(source) or isOpaquePointer(target) {
8968
                return true;
8969
            }
8970
            return isValidCast(*sourceTarget, *targetTarget);
8971
        }
8972
    }
8973
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
8974
    // or element types are castable.
8975
    if let case Type::Slice {
8976
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
8977
    } = source {
8978
        if let case Type::Slice {
8979
            class: targetClass, item: targetItem, mutable: targetMutable,
8980
        } = target {
8981
            if sourceClass <> targetClass {
8982
                return false;
8983
            }
8984
            if targetMutable and not sourceMutable {
8985
                return false;
8986
            }
8987
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
8988
                return true;
8989
            }
8990
            if *targetItem == Type::U8 {
8991
                return true;
8992
            }
8993
            return isValidCast(*sourceItem, *targetItem);
8994
        }
8995
    }
8996
    return false;
8997
}
8998
8999
/// Require casts into region-dependent storage to preserve its typed contents.
9000
fn regionalCastPreservesType(source: Type, target: Type) -> bool {
9001
    if typesEqual(source, target) {
9002
        return true;
9003
    }
9004
    if let case Type::Pointer { target: sourceItem, .. } = source {
9005
        let case Type::Pointer { target: targetItem, .. } = target else return false;
9006
        return typesEqual(*sourceItem, *targetItem);
9007
    }
9008
    if let case Type::Slice { item: sourceItem, .. } = source {
9009
        let case Type::Slice { item: targetItem, .. } = target else return false;
9010
        return typesEqual(*sourceItem, *targetItem);
9011
    }
9012
    return false;
9013
}
9014
9015
/// Analyze an `as` cast expression.
9016
unsafe fn resolveAs 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: ast::As) -> Type
9017
    throws (ResolveError)
9018
{
9019
    let targetTy = try infer(self, expr.type);
9020
    let sourceTy = try visit(self, expr.value, targetTy);
9021
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
9022
        try requireUnsafe(self, node);
9023
    }
9024
9025
    assert sourceTy <> Type::Unknown;
9026
    assert targetTy <> Type::Unknown;
9027
9028
    if let case Type::Cell { class, payload } = targetTy {
9029
        if let case Type::Pointer { class: sourceClass, target, mutable: true } = sourceTy;
9030
            sourceClass == class and class <> types::PointerClass::Unsafe and typesEqual(*target, *payload)
9031
        {
9032
            return setNodeType(self, node, targetTy);
9033
        }
9034
        if typesEqual(sourceTy, targetTy) {
9035
            return setNodeType(self, node, targetTy);
9036
        }
9037
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { from: sourceTy, to: targetTy }));
9038
    }
9039
    if containsRegion(targetTy) and not regionalCastPreservesType(sourceTy, targetTy) {
9040
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
9041
            from: sourceTy, to: targetTy,
9042
        }));
9043
    }
9044
    let mut valid = isValidCast(sourceTy, targetTy);
9045
    if let case Type::Pointer {
9046
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
9047
    } = sourceTy {
9048
        if let case Type::Pointer {
9049
            class: targetClass, target: targetTarget, mutable: targetMutable,
9050
        } = targetTy {
9051
            if types::isReference(sourceClass) and
9052
               targetClass == types::PointerClass::Unsafe and
9053
               (not targetMutable or sourceMutable) and
9054
               isValidCast(*sourceTarget, *targetTarget)
9055
            {
9056
                set valid = true;
9057
            }
9058
        }
9059
    }
9060
    if let case Type::Slice {
9061
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
9062
    } = sourceTy {
9063
        if let case Type::Slice {
9064
            class: targetClass, item: targetItem, mutable: targetMutable,
9065
        } = targetTy {
9066
            if types::isReference(sourceClass) and
9067
               targetClass == types::PointerClass::Unsafe and
9068
               (not targetMutable or sourceMutable) and
9069
               isValidCast(*sourceItem, *targetItem)
9070
            {
9071
                set valid = true;
9072
            }
9073
        }
9074
    }
9075
    if valid {
9076
        if let case Type::Pointer { target: sourceTarget, .. } = sourceTy {
9077
            if let case Type::Pointer { target: targetTarget, .. } = targetTy {
9078
                if *targetTarget <> Type::Opaque and not typesEqual(*sourceTarget, *targetTarget) {
9079
                    try requireUnsafe(self, node);
9080
                }
9081
            }
9082
        }
9083
        if let case Type::Slice { item: sourceItem, .. } = sourceTy {
9084
            if let case Type::Slice { item: targetItem, .. } = targetTy {
9085
                if *targetItem <> Type::Opaque and not typesEqual(*sourceItem, *targetItem) {
9086
                    try requireUnsafe(self, node);
9087
                }
9088
            }
9089
        }
9090
        // Propagate the constant value after applying the cast's target-width
9091
        // truncation and signed interpretation.
9092
        if let value = constValueEntry(self, expr.value) {
9093
            if let case ConstValue::Int(i) = value {
9094
                setNodeConstValue(self, node, castConstInt(i, targetTy));
9095
            }
9096
        }
9097
        return setNodeType(self, node, targetTy);
9098
    }
9099
    throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
9100
        from: sourceTy,
9101
        to: targetTy,
9102
    }));
9103
}
9104
9105
/// Analyze a range expression.
9106
unsafe fn resolveRange 'arena (self: &mut Resolver 'arena, node: *ast::Node, range: ast::Range) -> Type
9107
    throws (ResolveError)
9108
{
9109
    let mut start: ?*Type = nil;
9110
    let mut end: ?*Type = nil;
9111
9112
    if let s = range.start {
9113
        let startTy = try checkNumeric(self, s);
9114
9115
        if let e = range.end {
9116
            let endTy = try checkNumeric(self, e);
9117
            let mut resolvedTy = startTy;
9118
9119
            // Infer unsuffixed integer literals from the opposite bound.
9120
            if startTy == Type::Int and endTy <> Type::Int {
9121
                let _ = try checkAssignable(self, s, endTy);
9122
                set resolvedTy = endTy;
9123
            } else if endTy == Type::Int and startTy <> Type::Int {
9124
                let _ = try checkAssignable(self, e, startTy);
9125
                set resolvedTy = startTy;
9126
            } else {
9127
                let _ = try checkAssignable(self, e, startTy);
9128
            }
9129
            set start = allocType(self, resolvedTy);
9130
            set end = allocType(self, resolvedTy);
9131
        } else {
9132
            set start = allocType(self, startTy);
9133
        }
9134
    } else if let e = range.end {
9135
        set end = allocType(self, try checkNumeric(self, e));
9136
    }
9137
    return setNodeType(self, node, Type::Range { start, end });
9138
}
9139
9140
/// Analyze a `try` expression and its handlers.
9141
/// The `expected` type is used to determine if the value is discarded (`Void`)
9142
/// or if the catch expression needs type checking.
9143
unsafe fn resolveTry 'arena (self: &mut Resolver 'arena, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
9144
    throws (ResolveError)
9145
{
9146
    let call = tryExpr.expr;
9147
    let case ast::NodeValue::Call(callExpr) = call.value
9148
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
9149
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try, Type::Unknown);
9150
9151
    // TODO: It's annoying that we need to re-fetch the function type after
9152
    // analyzing the call.
9153
    let calleeTy = typeFor(self, callExpr.callee)
9154
        else return setNodeType(self, node, resultTy);
9155
    let case Type::Fn(calleeInfo) = calleeTy
9156
        else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
9157
9158
    if calleeInfo.throwList.len == 0 {
9159
        throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
9160
    }
9161
    // If we're not catching the error, nor panicking on error, nor returning
9162
    // optional, then the current function must be able to propagate it.
9163
    let mut tryResultTy = resultTy;
9164
    if tryExpr.returnsOptional {
9165
        // `try?` converts errors to `nil` and wraps the result in an optional.
9166
        if let case Type::Optional(_) = resultTy {
9167
            // Already optional, no wrapping needed.
9168
        } else {
9169
            set tryResultTy = Type::Optional(allocType(self, resultTy));
9170
        }
9171
    } else if tryExpr.catches.len > 0 {
9172
        // `try ... catch` -- one or more catch clauses.
9173
        set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint);
9174
    } else if not tryExpr.shouldPanic {
9175
        let fnInfo = self.currentFn
9176
            else throw emitError(self, node, ErrorKind::TryRequiresThrows);
9177
        if fnInfo.throwList.len == 0 {
9178
            throw emitError(self, node, ErrorKind::TryRequiresThrows);
9179
        }
9180
        // Check that *all* thrown errors of the callee can be propagated by
9181
        // the caller.
9182
        for throwTy in calleeInfo.throwList {
9183
            let mut found = false;
9184
9185
            for callerThrowTy in fnInfo.throwList {
9186
                if callerThrowTy == throwTy {
9187
                    set found = true;
9188
                    break;
9189
                }
9190
            }
9191
            if not found {
9192
                throw emitError(self, node, ErrorKind::TryIncompatibleError);
9193
            }
9194
        }
9195
    }
9196
    return setNodeType(self, node, tryResultTy);
9197
}
9198
9199
/// Check that a `catch` body is assignable to the expected result type, but only
9200
/// in expression context (`hint` is neither `Unknown` nor `Void`).
9201
unsafe fn checkCatchBody 'arena (self: &mut Resolver 'arena, body: *ast::Node, resultTy: Type, hint: Type)
9202
    throws (ResolveError)
9203
{
9204
    if hint <> Type::Unknown and hint <> Type::Void {
9205
        try checkAssignable(self, body, resultTy);
9206
    }
9207
}
9208
9209
/// Resolve catch clauses for a `try ... catch` expression.
9210
///
9211
/// For a single untyped catch (with or without binding), resolves the catch
9212
/// body and returns the result type. Multi-error callees with inferred bindings
9213
/// are rejected; you must use typed catches.
9214
unsafe fn resolveTryCatches 'arena (
9215
    self: &mut Resolver 'arena,
9216
    node: *ast::Node,
9217
    catches: *[*ast::Node],
9218
    calleeInfo: *FnType,
9219
    resultTy: Type,
9220
    hint: Type
9221
) -> Type throws (ResolveError) {
9222
    let firstNode = catches[0];
9223
    let case ast::NodeValue::CatchClause(first) = firstNode.value else
9224
        throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode));
9225
9226
    // Typed catches: dispatch to dedicated handler.
9227
    if first.typeNode <> nil {
9228
        return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint);
9229
    }
9230
    // Single untyped catch clause.
9231
    if let binding = first.binding {
9232
        if calleeInfo.throwList.len > 1 {
9233
            throw emitError(self, binding, ErrorKind::TryCatchMultiError);
9234
        }
9235
        enterScope(self, node);
9236
9237
        let errTy = *calleeInfo.throwList[0];
9238
        try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
9239
    }
9240
    let bodyTy = try visit(self, first.body, resultTy);
9241
9242
    if let _ = first.binding {
9243
        exitScope(self);
9244
    }
9245
    try checkCatchBody(self, first.body, resultTy, hint);
9246
9247
    return bodyTy if resultTy == Type::Never else resultTy;
9248
}
9249
9250
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
9251
///
9252
/// Validates that each type annotation is in the callee's throw list, that
9253
/// there are no duplicate catch types, and that the clauses are exhaustive.
9254
unsafe fn resolveTypedCatches 'arena (
9255
    self: &mut Resolver 'arena,
9256
    node: *ast::Node,
9257
    catches: *[*ast::Node],
9258
    calleeInfo: *FnType,
9259
    resultTy: Type,
9260
    hint: Type
9261
) -> Type throws (ResolveError) {
9262
    // Track which of the callee's throw types have been covered.
9263
    let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS];
9264
    let mut hasCatchAll = false;
9265
    let mut catchTy = Type::Never;
9266
9267
    for clauseNode in catches {
9268
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value else
9269
            throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode));
9270
9271
        if let typeNode = clause.typeNode {
9272
            // Typed catch clause: validate against callee's throw list.
9273
            let errTy = try infer(self, typeNode);
9274
            let mut foundIdx: ?u32 = nil;
9275
9276
            for throwType, j in calleeInfo.throwList {
9277
                if errTy == *throwType {
9278
                    set foundIdx = j;
9279
                    break;
9280
                }
9281
            }
9282
            let idx = foundIdx else {
9283
                throw emitError(self, typeNode, ErrorKind::TryIncompatibleError);
9284
            };
9285
            if covered[idx] {
9286
                throw emitError(self, typeNode, ErrorKind::TryCatchDuplicateType);
9287
            }
9288
            set covered[idx] = true;
9289
9290
            // Bind the error variable if present.
9291
            if let binding = clause.binding {
9292
                enterScope(self, clauseNode);
9293
                try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
9294
            }
9295
        } else {
9296
            // Catch-all clause with no type annotation or binding.
9297
            set hasCatchAll = true;
9298
        }
9299
        // Resolve the catch body and check assignability.
9300
        let bodyTy = try visit(self, clause.body, resultTy);
9301
        if bodyTy <> Type::Never { set catchTy = Type::Void; }
9302
        // Only typed clauses can have bindings.
9303
        if let _ = clause.binding {
9304
            exitScope(self);
9305
        }
9306
        try checkCatchBody(self, clause.body, resultTy, hint);
9307
    }
9308
9309
    // Check exhaustiveness: all callee error types must be covered.
9310
    if not hasCatchAll {
9311
        for i in 0..calleeInfo.throwList.len {
9312
            if not covered[i] {
9313
                throw emitError(self, node, ErrorKind::TryCatchNonExhaustive);
9314
            }
9315
        }
9316
    }
9317
    return catchTy if resultTy == Type::Never else resultTy;
9318
}
9319
9320
/// Analyze a `throw` statement.
9321
unsafe fn resolveThrow 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: *ast::Node) -> Type
9322
    throws (ResolveError)
9323
{
9324
    let fnInfo = self.currentFn
9325
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
9326
    if fnInfo.throwList.len == 0 {
9327
        throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
9328
    }
9329
    let throwTy = try infer(self, expr);
9330
    for errTy in fnInfo.throwList {
9331
        if let coerce = isAssignable(self, *errTy, throwTy, expr) {
9332
            setNodeCoercion(self, expr, coerce);
9333
            return setNodeType(self, node, Type::Never);
9334
        }
9335
    }
9336
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
9337
}
9338
9339
/// Analyze a `return` statement.
9340
unsafe fn resolveReturn 'arena (self: &mut Resolver 'arena, node: *ast::Node, retVal: ?*ast::Node) -> Type
9341
    throws (ResolveError)
9342
{
9343
    let f = self.currentFn
9344
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
9345
    let expected = *f.returnType;
9346
9347
    if let val = retVal {
9348
        let _actualTy = try checkAssignable(self, val, expected);
9349
    } else if expected <> Type::Void {
9350
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void });
9351
    }
9352
    // In throwing functions, return values are wrapped in the success variant.
9353
    if f.throwList.len > 0 {
9354
        setNodeCoercion(self, node, Coercion::ResultWrap);
9355
    }
9356
    return setNodeType(self, node, Type::Never);
9357
}
9358
9359
/// Convert a [`ConstInt`] to its two's-complement bit pattern.
9360
fn constIntToBits(c: ConstInt) -> u64 {
9361
    return (0 - c.magnitude) if c.negative else c.magnitude;
9362
}
9363
9364
/// Convert a [`ConstInt`] to its signed two's-complement representation.
9365
fn constIntToSigned(c: ConstInt) -> i64 {
9366
    return constIntToBits(c) as i64;
9367
}
9368
9369
/// Build a [`ConstInt`] from a signed result, preserving bit width and signedness.
9370
fn constIntFromSigned(value: i64, bits: u8, signed: bool) -> ConstInt {
9371
    if value < 0 {
9372
        // Compute magnitude without signed overflow.
9373
        let uval = value as u64;
9374
        return ConstInt {
9375
            magnitude: 0 - uval,
9376
            bits,
9377
            signed,
9378
            negative: true,
9379
        };
9380
    }
9381
    return ConstInt {
9382
        magnitude: value as u64,
9383
        bits,
9384
        signed,
9385
        negative: false,
9386
    };
9387
}
9388
9389
/// Build a [`ConstInt`] from a two's-complement bit pattern.
9390
fn constIntFromBits(raw: u64, bits: u8, signed: bool) -> ConstInt {
9391
    let mask = parser::U64_MAX if bits == 64 else parser::U64_MAX >> (64 - bits) as u64;
9392
    let truncated = raw & mask;
9393
9394
    if signed {
9395
        let signBit = (mask >> 1) + 1;
9396
        if (truncated & signBit) <> 0 {
9397
            return ConstInt {
9398
                magnitude: (0 - truncated) & mask,
9399
                bits,
9400
                signed,
9401
                negative: true,
9402
            };
9403
        }
9404
    }
9405
    return ConstInt { magnitude: truncated, bits, signed, negative: false };
9406
}
9407
9408
/// Try to fold a binary operation on two integer constants.
9409
/// Returns the resulting constant value if successful.
9410
fn foldIntBinOp(op: ast::BinaryOp, left: ConstInt, right: ConstInt) -> ?ConstValue {
9411
    // Use the wider bit width and propagate signedness.
9412
    let mut bits = left.bits;
9413
    if right.bits > bits {
9414
        set bits = right.bits;
9415
    }
9416
    let signed = left.signed or right.signed;
9417
    let l = constIntToSigned(left);
9418
    let r = constIntToSigned(right);
9419
9420
    match op {
9421
        // Shift counts are masked to the left operand's width, matching
9422
        // the runtime word instructions.
9423
        case ast::BinaryOp::Shl => {
9424
            let raw = constIntToBits(left);
9425
            let shamt = constIntToBits(right) % left.bits as u64;
9426
            return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed));
9427
        },
9428
        case ast::BinaryOp::Shr => {
9429
            let shamt = constIntToBits(right) % left.bits as u64;
9430
            if left.signed {
9431
                let shifted = constIntToSigned(left) >> shamt as i64;
9432
                return ConstValue::Int(
9433
                    constIntFromBits(shifted as u64, left.bits, true)
9434
                );
9435
            }
9436
            return ConstValue::Int(
9437
                constIntFromBits(left.magnitude >> shamt, left.bits, false)
9438
            );
9439
        },
9440
        case ast::BinaryOp::Eq  => return ConstValue::Bool(l == r),
9441
        case ast::BinaryOp::Ne  => return ConstValue::Bool(l <> r),
9442
        case ast::BinaryOp::Lt =>
9443
            return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude),
9444
        case ast::BinaryOp::Gt =>
9445
            return ConstValue::Bool(l > r if signed else left.magnitude > right.magnitude),
9446
        case ast::BinaryOp::Lte =>
9447
            return ConstValue::Bool(l <= r if signed else left.magnitude <= right.magnitude),
9448
        case ast::BinaryOp::Gte =>
9449
            return ConstValue::Bool(l >= r if signed else left.magnitude >= right.magnitude),
9450
        case ast::BinaryOp::Add => return ConstValue::Int(constIntFromSigned(l + r, bits, signed)),
9451
        case ast::BinaryOp::Sub => return ConstValue::Int(constIntFromSigned(l - r, bits, signed)),
9452
        case ast::BinaryOp::Mul => return ConstValue::Int(constIntFromSigned(l * r, bits, signed)),
9453
        case ast::BinaryOp::Div => {
9454
            if signed {
9455
                if r == 0 {
9456
                    return nil;
9457
                }
9458
                return ConstValue::Int(constIntFromSigned(l / r, bits, true));
9459
            }
9460
            if right.magnitude == 0 {
9461
                return nil;
9462
            }
9463
            return constInt(left.magnitude / right.magnitude, bits, false, false);
9464
        },
9465
        case ast::BinaryOp::Mod => {
9466
            if signed {
9467
                if r == 0 {
9468
                    return nil;
9469
                }
9470
                return ConstValue::Int(constIntFromSigned(l % r, bits, true));
9471
            }
9472
            if right.magnitude == 0 {
9473
                return nil;
9474
            }
9475
            return constInt(left.magnitude % right.magnitude, bits, false, false);
9476
        },
9477
        case ast::BinaryOp::BitAnd => return ConstValue::Int(constIntFromSigned(l & r, bits, signed)),
9478
        case ast::BinaryOp::BitOr  => return ConstValue::Int(constIntFromSigned(l | r, bits, signed)),
9479
        case ast::BinaryOp::BitXor => return ConstValue::Int(constIntFromSigned(l ^ r, bits, signed)),
9480
        else => return nil,
9481
    }
9482
}
9483
9484
/// Try to constant-fold a binary operation on two resolved operands.
9485
/// Only folds when the result type is concrete.
9486
fn tryFoldBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
9487
    let leftVal = constValueEntry(self, binop.left)
9488
        else return;
9489
    let rightVal = constValueEntry(self, binop.right)
9490
        else return;
9491
9492
    // Fold integer binary ops.
9493
    if let case ConstValue::Int(leftInt) = leftVal {
9494
        if let case ConstValue::Int(rightInt) = rightVal {
9495
            if let result = foldIntBinOp(binop.op, leftInt, rightInt) {
9496
                setNodeConstValue(self, node, result);
9497
            }
9498
            return;
9499
        }
9500
    }
9501
9502
    // Fold boolean binary ops.
9503
    if let case ConstValue::Bool(l) = leftVal {
9504
        if let case ConstValue::Bool(r) = rightVal {
9505
            match binop.op {
9506
                case ast::BinaryOp::And => setNodeConstValue(self, node, ConstValue::Bool(l and r)),
9507
                case ast::BinaryOp::Or => setNodeConstValue(self, node, ConstValue::Bool(l or r)),
9508
                case ast::BinaryOp::Eq => setNodeConstValue(self, node, ConstValue::Bool(l == r)),
9509
                case ast::BinaryOp::Ne,
9510
                     ast::BinaryOp::Xor => setNodeConstValue(self, node, ConstValue::Bool(l <> r)),
9511
                else => {}
9512
            }
9513
        }
9514
    }
9515
}
9516
9517
/// Analyze a binary expression.
9518
unsafe fn resolveBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp) -> Type
9519
    throws (ResolveError)
9520
{
9521
    let mut resultTy = Type::Unknown;
9522
9523
    match binop.op {
9524
        case ast::BinaryOp::And,
9525
             ast::BinaryOp::Or,
9526
             ast::BinaryOp::Xor =>
9527
        {
9528
            try checkBoolean(self, binop.left);
9529
            try checkBoolean(self, binop.right);
9530
9531
            set resultTy = Type::Bool;
9532
        },
9533
        case ast::BinaryOp::Eq,
9534
             ast::BinaryOp::Ne =>
9535
        {
9536
            let leftTy = try infer(self, binop.left);
9537
            let rightTy = try visit(self, binop.right, leftTy);
9538
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
9539
                try requireUnsafe(self, node);
9540
            }
9541
9542
            if not isComparable(leftTy, rightTy) {
9543
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
9544
                    expected: leftTy,
9545
                    actual: rightTy,
9546
                });
9547
            }
9548
            // When comparing `T == ?T`, record a coercion on the
9549
            // non-optional side so the lowerer lifts it before comparing.
9550
            // We use the already-optional type from the other side rather than
9551
            // constructing a new optional, so that e.g. `?u8 == 42` coerces
9552
            // `42` to `?u8` (not `?i32`). We also record OptionalLift directly
9553
            // rather than using expectAssignable, because comparisons should
9554
            // allow e.g. `?*mut T == *T` where mutability differs.
9555
            if let case Type::Optional(_) = leftTy {
9556
                if not isOptionalType(rightTy) {
9557
                    setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy));
9558
                }
9559
            } else if let case Type::Optional(_) = rightTy {
9560
                setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy));
9561
            }
9562
            set resultTy = Type::Bool;
9563
        },
9564
        else => {
9565
            // Check for pointer arithmetic before numeric check.
9566
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
9567
                let leftTy = try infer(self, binop.left);
9568
                let rightTy = try visit(self, binop.right, leftTy);
9569
9570
                // Allow arithmetic on owning pointers and unsafe pointers, but
9571
                // never on references.
9572
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
9573
                    if *leftTarget == Type::Opaque {
9574
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
9575
                    }
9576
                    if not types::isReference(leftClass)
9577
                        and isNumericType(rightTy)
9578
                    {
9579
                        try requireUnsafe(self, node);
9580
                        return setNodeType(self, node, leftTy);
9581
                    }
9582
                }
9583
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
9584
                    if *rightTarget == Type::Opaque {
9585
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
9586
                    }
9587
                    if binop.op == ast::BinaryOp::Add
9588
                        and not types::isReference(rightClass)
9589
                        and isNumericType(leftTy)
9590
                    {
9591
                        try requireUnsafe(self, node);
9592
                        return setNodeType(self, node, rightTy);
9593
                    }
9594
                }
9595
            }
9596
            let leftTy = try checkNumeric(self, binop.left);
9597
            let rightTy = try checkNumeric(self, binop.right);
9598
9599
            let mut operandTy = leftTy;
9600
            if leftTy <> rightTy {
9601
                if leftTy == Type::Int {
9602
                    set operandTy = rightTy;
9603
                } else if rightTy <> Type::Int {
9604
                    throw emitTypeMismatch(self, binop.right, TypeMismatch {
9605
                        expected: leftTy,
9606
                        actual: rightTy,
9607
                    });
9608
                }
9609
            }
9610
9611
            // Ordering comparisons return `bool`, not the operand type.
9612
            match binop.op {
9613
                case ast::BinaryOp::Lt, ast::BinaryOp::Gt,
9614
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
9615
                    set resultTy = Type::Bool,
9616
                else =>
9617
                    set resultTy = operandTy,
9618
            }
9619
9620
        }
9621
    };
9622
    // Try constant folding after both operands are resolved.
9623
    tryFoldBinOp(self, node, binop, resultTy);
9624
9625
    return setNodeType(self, node, resultTy);
9626
}
9627
9628
/// Analyze a unary expression.
9629
unsafe fn resolveUnOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, unop: ast::UnOp) -> Type
9630
    throws (ResolveError)
9631
{
9632
    let mut resultTy = Type::Unknown;
9633
9634
    match unop.op {
9635
        case ast::UnaryOp::Not => {
9636
            set resultTy = try checkBoolean(self, unop.value);
9637
            if let value = constValueEntry(self, unop.value) {
9638
                if let case ConstValue::Bool(val) = value {
9639
                    setNodeConstValue(self, node, ConstValue::Bool(not val));
9640
                }
9641
            }
9642
        },
9643
        case ast::UnaryOp::Neg => {
9644
            // TODO: Check that we're allowed to use `-` here? Should negation
9645
            // only be valid for signed integers?
9646
            set resultTy = try checkNumeric(self, unop.value);
9647
            if let value = constValueEntry(self, unop.value) {
9648
                // Get the constant expression for the value, flip the sign,
9649
                // and store that new expression on the unary op node.
9650
                if let case ConstValue::Int(intVal) = value {
9651
                    setNodeConstValue(
9652
                        self,
9653
                        node,
9654
                        constInt(intVal.magnitude, intVal.bits, true, not intVal.negative)
9655
                    );
9656
                }
9657
            }
9658
        },
9659
        case ast::UnaryOp::BitNot => {
9660
            set resultTy = try checkNumeric(self, unop.value);
9661
            if let value = constValueEntry(self, unop.value) {
9662
                if let case ConstValue::Int(intVal) = value {
9663
                    let signed = constIntToSigned(intVal);
9664
                    let inverted = constIntFromSigned(-(signed + 1), intVal.bits, intVal.signed);
9665
                    setNodeConstValue(self, node, ConstValue::Int(inverted));
9666
                }
9667
            }
9668
        },
9669
    };
9670
    return setNodeType(self, node, resultTy);
9671
}
9672
9673
/// Resolve a type signature node and set its type.
9674
unsafe fn inferTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
9675
    throws (ResolveError)
9676
{
9677
    let resolved = try resolveTypeSig(self, node, sig);
9678
9679
    return setNodeType(self, node, resolved);
9680
}
9681
9682
/// Convert a parsed pointer qualifier to its semantic class.
9683
fn resolvePointerClass(class: ast::PointerClass) -> types::PointerClass {
9684
    match class {
9685
        case ast::PointerClass::Owned => return types::PointerClass::Owned,
9686
        case ast::PointerClass::Ref => return types::PointerClass::Ref,
9687
        case ast::PointerClass::Unsafe => return types::PointerClass::Unsafe,
9688
    }
9689
}
9690
9691
/// Convert a type signature node into a type value.
9692
unsafe fn resolveTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
9693
    throws (ResolveError)
9694
{
9695
    match sig {
9696
        case ast::TypeSig::Cell { class, payload } => {
9697
            let inner = try infer(self, payload);
9698
            try validateCellPayload(self, node, inner);
9699
            return Type::Cell { class: resolvePointerClass(class), payload: allocType(self, inner) };
9700
        }
9701
        case ast::TypeSig::RegionRef { region, type } => {
9702
            let identity = try resolveRegion(self, region);
9703
            let base = try infer(self, type);
9704
            match base {
9705
                case Type::Cell { payload, .. } =>
9706
                    return Type::Cell { class: types::PointerClass::Region(identity), payload },
9707
                case Type::Pointer { target, mutable, .. } =>
9708
                    return Type::Pointer { class: types::PointerClass::Region(identity), target, mutable },
9709
                case Type::Slice { item, mutable, .. } =>
9710
                    return Type::Slice { class: types::PointerClass::Region(identity), item, mutable },
9711
                case Type::TraitObject { traitInfo, mutable, .. } =>
9712
                    return Type::TraitObject { class: types::PointerClass::Region(identity), traitInfo, mutable },
9713
                else => throw emitError(self, node, ErrorKind::InvalidRefPosition),
9714
            }
9715
        }
9716
        case ast::TypeSig::Applied { name, regions } => {
9717
            if let case ast::NodeValue::Ident(spelling) = name.value;
9718
                mem::eq(spelling, "Session") and findTypeSymbol(self.scope, spelling) == nil
9719
            {
9720
                if regions.len <> 1 {
9721
                    throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch {
9722
                        expected: 1, actual: regions.len,
9723
                    }));
9724
                }
9725
                return Type::Session(try resolveRegion(self, regions[0]));
9726
            }
9727
            let base = try resolveTypeName(self, name);
9728
            return Type::Nominal(try applyNominalRegions(self, base, regions, node));
9729
        }
9730
        case ast::TypeSig::Void => {
9731
            return Type::Void;
9732
        }
9733
        case ast::TypeSig::Never => {
9734
            return Type::Never;
9735
        }
9736
        case ast::TypeSig::Opaque => {
9737
            return Type::Opaque;
9738
        }
9739
        case ast::TypeSig::Bool => {
9740
            return Type::Bool;
9741
        }
9742
        case ast::TypeSig::Integer { width, sign } => {
9743
            let u = sign == ast::Signedness::Unsigned;
9744
            match width {
9745
                case 1 => return Type::U8 if u else Type::I8,
9746
                case 2 => return Type::U16 if u else Type::I16,
9747
                case 4 => return Type::U32 if u else Type::I32,
9748
                case 8 => return Type::U64 if u else Type::I64,
9749
                else => {
9750
                    panic "resolveTypeSig: invalid integer width";
9751
                }
9752
            }
9753
        }
9754
        case ast::TypeSig::Array { itemType, length } => {
9755
            let item = try infer(self, itemType);
9756
            let length = try checkSizeInt(self, length);
9757
9758
            return Type::Array(ArrayType { item: allocType(self, item), length });
9759
        }
9760
        case ast::TypeSig::Slice { class, itemType, mutable } => {
9761
            let item = try infer(self, itemType);
9762
            return Type::Slice {
9763
                class: resolvePointerClass(class),
9764
                item: allocType(self, item),
9765
                mutable,
9766
            };
9767
        }
9768
        case ast::TypeSig::Pointer { class, valueType, mutable } => {
9769
            let target = try infer(self, valueType);
9770
            return Type::Pointer {
9771
                class: resolvePointerClass(class),
9772
                target: allocType(self, target),
9773
                mutable,
9774
            };
9775
        }
9776
        case ast::TypeSig::Optional { valueType } => {
9777
            let payload = try infer(self, valueType);
9778
            return Type::Optional(allocType(self, payload));
9779
        }
9780
        case ast::TypeSig::Nominal(name) => {
9781
            let ty = try resolveTypeName(self, name);
9782
            try requireNominalArguments(self, ty, node);
9783
            return Type::Nominal(ty);
9784
        }
9785
        case ast::TypeSig::Record { fields, labeled } => {
9786
            let mut recordType = try resolveRecordFields(self, node, fields, labeled);
9787
            set recordType.declaredCopy = true;
9788
            for field in recordType.fields {
9789
                if not isCopy(field.fieldType) {
9790
                    set recordType.declaredCopy = false;
9791
                }
9792
            }
9793
            set recordType.regions = self.regionScope;
9794
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
9795
            if let scope = self.regionScope {
9796
                let map = regionSubstitution(self, scope);
9797
                for parameter, i in scope.entries {
9798
                    set map.arguments[i] = parameter;
9799
                }
9800
                return Type::Nominal(internNominalApplication(self, nominalTy, &map));
9801
            }
9802
            return Type::Nominal(nominalTy);
9803
        }
9804
        case ast::TypeSig::Fn { sig: t, isUnsafe } => {
9805
            let a = alloc::arenaAllocator(self.arena);
9806
            let mut paramTypes: *mut [*Type] = &mut [];
9807
            let mut throwList: *mut [*Type] = &mut [];
9808
9809
            if t.params.len > MAX_FN_PARAMS {
9810
                throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
9811
                    expected: MAX_FN_PARAMS,
9812
                    actual: t.params.len,
9813
                }));
9814
            }
9815
            if t.throwList.len > MAX_FN_THROWS {
9816
                throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
9817
                    expected: MAX_FN_THROWS,
9818
                    actual: t.throwList.len,
9819
                }));
9820
            }
9821
9822
            for paramNode in t.params {
9823
                let paramTy = try resolveValueType(self, paramNode);
9824
                paramTypes.append(allocType(self, paramTy), a);
9825
            }
9826
            for tyNode in t.throwList {
9827
                let throwTy = try resolveValueType(self, tyNode);
9828
                try ensureStorableType(self, tyNode, throwTy);
9829
                try validateErrorTag(self, tyNode, throwTy, &throwList[..]);
9830
                throwList.append(allocType(self, throwTy), a);
9831
            }
9832
            let mut retType = allocType(self, Type::Void);
9833
            if let ret = t.returnType {
9834
                let resolvedRet = try resolveValueType(self, ret);
9835
                try ensureStorableType(self, ret, resolvedRet);
9836
                set retType = allocType(self, resolvedRet);
9837
            }
9838
            let fnType = FnType {
9839
                regions: nil,
9840
                paramTypes: &paramTypes[..],
9841
                returnType: retType,
9842
                throwList: &throwList[..],
9843
                isUnsafe,
9844
            };
9845
            return Type::Fn(allocFnType(self, fnType));
9846
        }
9847
        // Resolve an opaque trait object signature.
9848
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
9849
            let sym = try resolveNamePath(self, traitName);
9850
            let case SymbolData::Trait(traitInfo) = sym.data
9851
                else throw emitError(self, traitName, ErrorKind::Internal);
9852
            setNodeSymbol(self, traitName, sym);
9853
9854
            return Type::TraitObject { class: resolvePointerClass(class), traitInfo, mutable };
9855
        }
9856
    }
9857
}
9858
9859
/// Check if a type can be used for inferrence.
9860
fn isTypeInferrable(type: Type) -> bool {
9861
    if let case Type::Pointer { target, .. } = type {
9862
        return isTypeInferrable(*target);
9863
    }
9864
    match type {
9865
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
9866
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
9867
        case Type::Optional(opt) => return isTypeInferrable(*opt),
9868
        else => return true,
9869
    }
9870
}
9871
9872
/// Analyze a standalone expression by wrapping it in a synthetic function.
9873
export unsafe fn resolveExpr 'arena (
9874
    self: &mut Resolver 'arena, expr: *ast::Node, arena: &mut ast::NodeArena
9875
) -> Diagnostics throws (ResolveError) {
9876
    let a = alloc::arenaAllocator(&mut arena.arena);
9877
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
9878
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
9879
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
9880
9881
    let case ast::NodeValue::Block(block) = module.modBody.value
9882
        else panic "resolveExpr: expected block for module body";
9883
    enterScope(self, module.modBody);
9884
    try resolveModuleDecls(self, &block) catch {
9885
        return diagnostics(self);
9886
    };
9887
    try resolveModuleDefs(self, &block) catch {
9888
        return diagnostics(self);
9889
    };
9890
    exitScope(self);
9891
9892
    return diagnostics(self);
9893
}
9894
9895
/// Analyze a parsed module root, ie. a block of top-level statements.
9896
export unsafe fn resolveModuleRoot 'arena (self: &mut Resolver 'arena, root: *ast::Node) -> Diagnostics throws (ResolveError) {
9897
    let case ast::NodeValue::Block(block) = root.value
9898
        else panic "resolveModuleRoot: expected block for module root";
9899
9900
    enterScope(self, root);
9901
    try resolveModuleDecls(self, &block) catch {
9902
        return diagnostics(self);
9903
    };
9904
    try resolveModuleDefs(self, &block) catch {
9905
        return diagnostics(self);
9906
    };
9907
    exitScope(self);
9908
    setNodeType(self, root, Type::Void);
9909
9910
    return diagnostics(self);
9911
}
9912
9913
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
9914
/// and scopes for them, and also binds type names in each module so that cross-module
9915
/// type references work regardless of declaration order.
9916
unsafe fn resolveModuleGraph 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
9917
    try bindTypeNames(self, block);
9918
9919
    for node in block.statements {
9920
        if let case ast::NodeValue::Mod(decl) = node.value {
9921
            try resolveModGraph(self, node, decl);
9922
        }
9923
    }
9924
}
9925
9926
/// Bind all type names in a module.
9927
/// Skips declarations that have already been bound.
9928
unsafe fn bindTypeNames 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
9929
    for node in block.statements {
9930
        match node.value {
9931
            case ast::NodeValue::RecordDecl(decl) => {
9932
                if symbolFor(self, node) == nil {
9933
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
9934
                }
9935
            }
9936
            case ast::NodeValue::UnionDecl(decl) => {
9937
                if symbolFor(self, node) == nil {
9938
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
9939
                }
9940
            }
9941
            case ast::NodeValue::TraitDecl { name, attrs, .. } => {
9942
                if symbolFor(self, node) == nil {
9943
                    try bindTraitName(self, node, name, attrs) catch {};
9944
                }
9945
            }
9946
            else => {}
9947
        }
9948
    }
9949
}
9950
9951
/// Resolve all type bodies in a module.
9952
unsafe fn resolveTypeBodies 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
9953
    for node in block.statements {
9954
        match node.value {
9955
            case ast::NodeValue::RecordDecl(decl) => {
9956
                try resolveRecordBody(self, node, decl) catch {
9957
                    // Continue resolving other types even if one fails.
9958
                };
9959
            }
9960
            case ast::NodeValue::UnionDecl(decl) => {
9961
                try resolveUnionBody(self, node, decl) catch {
9962
                    // Continue resolving other types even if one fails.
9963
                };
9964
            }
9965
            case ast::NodeValue::TraitDecl { supertraits, methods, .. } => {
9966
                try resolveTraitBody(self, node, supertraits, methods) catch {
9967
                    // Continue resolving other types even if one fails.
9968
                };
9969
            }
9970
            else => {
9971
                // Ignore other declarations.
9972
            }
9973
        }
9974
    }
9975
}
9976
9977
/// Analyze module declarations. This pass processes all top-level statements. When it hits
9978
/// a `mod` statement, it recurses inside the module, analyzing its statements. Module import
9979
/// statements (`use`) are processed here, and make use of the module graph established in the
9980
/// previous pass.
9981
///
9982
/// This function uses a two-phase approach:
9983
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
9984
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
9985
unsafe fn resolveModuleDecls 'arena (res: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
9986
    // Phase 1: Bind all type names as placeholders.
9987
    try bindTypeNames(res, block);
9988
    // Phase 2: Process imports so names available from the module graph can
9989
    // be used in function signatures.
9990
    for node in block.statements {
9991
        if let case ast::NodeValue::Use(decl) = node.value {
9992
            try resolveUse(res, node, decl);
9993
        }
9994
    }
9995
    // Phase 3: Bind function signatures so that function references are
9996
    // available in constant and static initializers.
9997
    for node in block.statements {
9998
        if let case ast::NodeValue::FnDecl(decl) = node.value {
9999
            try resolveFnDecl(res, node, decl);
10000
        }
10001
    }
10002
    // Phase 4: Process constants before submodules, so that child modules
10003
    // can reference parent constants via `super::`.
10004
    for node in block.statements {
10005
        if let case ast::NodeValue::ConstDecl(_) = node.value {
10006
            try infer(res, node);
10007
        }
10008
    }
10009
    // Phase 5: Process submodule declarations -- recurses into child modules.
10010
    // Child modules may trigger on-demand type resolution via
10011
    // [`ensureNominalResolved`] which switches to the declaring module's
10012
    // scope.
10013
    for node in block.statements {
10014
        if let case ast::NodeValue::Mod(decl) = node.value {
10015
            try resolveModDecl(res, node, decl);
10016
        }
10017
    }
10018
    // Phase 5b: Process wildcard imports after submodules are resolved,
10019
    // so that transitive re-exports (export use foo::*) are visible.
10020
    for node in block.statements {
10021
        if let case ast::NodeValue::Use(decl) = node.value {
10022
            if decl.wildcard {
10023
                try resolveUse(res, node, decl);
10024
            }
10025
        }
10026
    }
10027
    // Phase 6: Resolve type bodies (record fields, union variants).
10028
    try resolveTypeBodies(res, block);
10029
    // Phase 7: Process all other declarations (statics, etc.).
10030
    for stmt in block.statements {
10031
        try visitDecl(res, stmt);
10032
    }
10033
}
10034
10035
/// Create a place with no storage root or field projections.
10036
fn emptyBorrowPlace() -> BorrowPlace {
10037
    return BorrowPlace { root: nil, fields: [0; MAX_BORROW_FIELDS], len: 0, precise: true };
10038
}
10039
10040
/// Create initialized loan and loop scratch state for one function.
10041
fn linearChecker 'arena 'checking (
10042
    resolver: &'checking mut Resolver 'arena, regions: ?*RegionScope
10043
) -> LinearChecker 'arena 'checking where 'arena: 'checking {
10044
    let place = emptyBorrowPlace();
10045
    return LinearChecker 'arena 'checking {
10046
        resolver,
10047
        regional: [nil; MAX_REGIONAL_LOANS],
10048
        regionalLen: 0,
10049
        regions,
10050
        loopBackLoans: [0; MAX_LINEAR_LOOP_DEPTH],
10051
        loopExitLoans: [0; MAX_LINEAR_LOOP_DEPTH],
10052
        loopRegions: [nil; MAX_LINEAR_LOOP_DEPTH],
10053
        loans: [place; MAX_LINEAR_BINDINGS],
10054
        loanLen: 0,
10055
        locals: [LocalLoan { binding: nil, place, exclusive: false }; MAX_LINEAR_BINDINGS],
10056
        localLen: 0,
10057
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
10058
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
10059
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
10060
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
10061
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
10062
        loopDepth: 0,
10063
    };
10064
}
10065
10066
/// Create an empty ownership environment with initialized binding slots.
10067
fn linearEnv() -> LinearEnv {
10068
    return LinearEnv {
10069
        regionalLoans: 0,
10070
        symbols: [nil; MAX_LINEAR_BINDINGS],
10071
        available: 0,
10072
        len: 0,
10073
        terminated: false,
10074
    };
10075
}
10076
10077
/// Read initialized binding metadata from the active prefix.
10078
fn linearSymbol(env: &LinearEnv, index: u32) -> TrackedSymbol {
10079
    assert index < env.len, "linearSymbol: binding index outside active prefix";
10080
    let symbol = env.symbols[index] else panic "linearSymbol: missing active binding";
10081
    return symbol;
10082
}
10083
10084
/// Find a tracked binding by symbol identity.
10085
fn findLinearBinding(env: &LinearEnv, symbolId: u32) -> ?u32 {
10086
    for i in 0..env.len {
10087
        if linearSymbol(env, i).id == symbolId {
10088
            return i;
10089
        }
10090
    }
10091
    return nil;
10092
}
10093
10094
/// Return whether a tracked binding is still available.
10095
fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool {
10096
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
10097
}
10098
10099
/// Add a local binding when its resolved type moves by value.
10100
unsafe fn addLinearBinding 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
10101
    throws (ResolveError) where 'arena: 'checking
10102
{
10103
    let sym = symbolFor(checker.resolver, node) else return;
10104
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
10105
    if not isMoveOnly(ty) {
10106
        return;
10107
    }
10108
    if env.len >= MAX_LINEAR_BINDINGS {
10109
        throw emitError(checker.resolver, node, ErrorKind::Internal);
10110
    }
10111
    let usage = BindingUse::Linear if isLinear(ty) else BindingUse::Affine;
10112
    set env.symbols[env.len] = TrackedSymbol { id: sym.id, name: sym.name, node: sym.node, usage };
10113
    set env.available |= (1 as u64) << (env.len as u64);
10114
    set env.len += 1;
10115
}
10116
10117
/// Mark a tracked binding as uninitialized.
10118
fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) {
10119
    let binding = self.nodeData.entries[node.id].binding else return;
10120
    let index = findLinearBinding(env, binding.id) else return;
10121
    set env.available &= ~((1 as u64) << (index as u64));
10122
}
10123
10124
/// Require exact-use bindings introduced after `start` to be consumed.
10125
fn finishLinearScope 'arena 'checking (
10126
    checker: &mut LinearChecker 'arena 'checking,
10127
    env: &mut LinearEnv,
10128
    start: u32,
10129
) throws (ResolveError) where 'arena: 'checking {
10130
    if not env.terminated {
10131
        for i in start..env.len {
10132
            if linearBindingAvailable(env, i) {
10133
                let sym = linearSymbol(env, i);
10134
                if sym.usage == BindingUse::Linear {
10135
                    throw emitError(
10136
                        checker.resolver,
10137
                        sym.node,
10138
                        ErrorKind::LinearNotConsumed(sym.name),
10139
                    );
10140
                }
10141
            }
10142
        }
10143
    }
10144
    set env.len = start;
10145
}
10146
10147
/// Require a tracked identifier to remain available for any access.
10148
fn checkLinearIdent 'arena 'checking (
10149
    checker: &mut LinearChecker 'arena 'checking,
10150
    env: &mut LinearEnv,
10151
    node: *ast::Node,
10152
) throws (ResolveError) where 'arena: 'checking {
10153
    let binding = checker.resolver.nodeData.entries[node.id].binding else return;
10154
    let index = findLinearBinding(env, binding.id) else return;
10155
    if not linearBindingAvailable(env, index) {
10156
        let sym = linearSymbol(env, index);
10157
        let kind = ErrorKind::LinearUseAfterConsume(sym.name) if sym.usage == BindingUse::Linear
10158
            else ErrorKind::AffineUseAfterMove(sym.name);
10159
        throw emitError(checker.resolver, node, kind);
10160
    }
10161
}
10162
10163
/// Move or consume a tracked identifier once.
10164
fn consumeLinearIdent 'arena 'checking (
10165
    checker: &mut LinearChecker 'arena 'checking,
10166
    env: &mut LinearEnv,
10167
    node: *ast::Node,
10168
) throws (ResolveError) where 'arena: 'checking {
10169
    try checkLinearIdent(checker, env, node);
10170
    let binding = checker.resolver.nodeData.entries[node.id].binding else return;
10171
    let index = findLinearBinding(env, binding.id) else return;
10172
    set env.available &= ~((1 as u64) << (index as u64));
10173
}
10174
10175
/// Merge ownership availability across two live branches.
10176
/// Validate both inputs before writing to an output that can alias either input.
10177
fn joinLinearBranches 'arena 'checking (
10178
    checker: &mut LinearChecker 'arena 'checking,
10179
    env: &mut LinearEnv,
10180
    left: &LinearEnv,
10181
    right: &LinearEnv,
10182
    node: *ast::Node,
10183
) throws (ResolveError) where 'arena: 'checking {
10184
    if left.terminated and right.terminated {
10185
        set *env = *left;
10186
        set env.terminated = true;
10187
        return;
10188
    }
10189
    if left.terminated {
10190
        set *env = *right;
10191
        return;
10192
    }
10193
    if right.terminated {
10194
        set *env = *left;
10195
        return;
10196
    }
10197
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
10198
    let mut available = left.available;
10199
    for i in 0..left.len {
10200
        if linearBindingAvailable(left, i) <> linearBindingAvailable(right, i) {
10201
            let sym = linearSymbol(left, i);
10202
            if sym.usage == BindingUse::Linear {
10203
                throw emitError(
10204
                    checker.resolver,
10205
                    node,
10206
                    ErrorKind::LinearBranchMismatch(sym.name),
10207
                );
10208
            }
10209
            set available &= ~((1 as u64) << (i as u64));
10210
        }
10211
    }
10212
    let regionalLoans = left.regionalLoans | right.regionalLoans;
10213
    set *env = *left;
10214
    set env.available = available;
10215
    set env.regionalLoans = regionalLoans;
10216
}
10217
10218
/// Require all available exact-use bindings to be consumed at a function exit.
10219
fn finishLinearExit 'arena 'checking (
10220
    checker: &mut LinearChecker 'arena 'checking,
10221
    env: &mut LinearEnv,
10222
) throws (ResolveError) where 'arena: 'checking {
10223
    if env.terminated {
10224
        return;
10225
    }
10226
    for i in 0..env.len {
10227
        if linearBindingAvailable(env, i) {
10228
            let sym = linearSymbol(env, i);
10229
            if sym.usage == BindingUse::Linear {
10230
                throw emitError(
10231
                    checker.resolver,
10232
                    sym.node,
10233
                    ErrorKind::LinearNotConsumed(sym.name),
10234
                );
10235
            }
10236
        }
10237
    }
10238
    set env.terminated = true;
10239
}
10240
10241
/// Find the local root borrowed or consumed by an argument expression.
10242
fn linearRootSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
10243
    match node.value {
10244
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) =>
10245
            return symbolFor(self, node),
10246
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
10247
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
10248
        case ast::NodeValue::FieldAccess(access) =>
10249
            return linearRootSymbol(self, access.parent),
10250
        case ast::NodeValue::Subscript { container, .. } =>
10251
            return linearRootSymbol(self, container),
10252
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
10253
        else => return nil,
10254
    }
10255
}
10256
10257
/// A projection loan that remains active until its named region ends.
10258
record RegionalLoan: Copy {
10259
    /// Address expression that supplies access to the loan.
10260
    source: *ast::Node,
10261
    /// Source identity of the projection's declared lifetime.
10262
    regionId: u32,
10263
    /// Storage protected by the projection.
10264
    place: BorrowPlace,
10265
    /// Whether accesses through other references are excluded.
10266
    exclusive: bool,
10267
}
10268
10269
/// Read an initialized regional loan from the active prefix.
10270
fn regionalLoan 'arena 'checking (
10271
    checker: &LinearChecker 'arena 'checking, index: u32
10272
) -> RegionalLoan where 'arena: 'checking {
10273
    assert index < checker.regionalLen, "regionalLoan: index outside active prefix";
10274
    let loan = checker.regional[index] else panic "regionalLoan: missing active loan";
10275
    return loan;
10276
}
10277
10278
/// Return whether a region identity is visible in a lexical environment.
10279
fn regionInScope(scope: ?*RegionScope, regionId: u32) -> bool {
10280
    let mut cursor = scope;
10281
    while let current = cursor {
10282
        if regionIndex(current, regionId) <> nil {
10283
            return true;
10284
        }
10285
        set cursor = current.parent;
10286
    }
10287
    return false;
10288
}
10289
10290
/// Retain only loans whose regions remain active at a control-flow destination.
10291
fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking {
10292
    let mut result: u64 = 0;
10293
    for i in 0..checker.regionalLen {
10294
        let bit = (1 as u64) << (i as u64);
10295
        if (mask & bit) <> 0 and regionInScope(scope, regionalLoan(checker, i).regionId) {
10296
            set result |= bit;
10297
        }
10298
    }
10299
    return result;
10300
}
10301
10302
/// Remap one loan mask after the regional loan table is compacted.
10303
fn remapRegionalLoans(mask: u64, mapping: &[u64]) -> u64 {
10304
    let mut result: u64 = 0;
10305
    for replacement, i in mapping {
10306
        if (mask & ((1 as u64) << (i as u64))) <> 0 {
10307
            set result |= replacement;
10308
        }
10309
    }
10310
    return result;
10311
}
10312
10313
/// Reclaim ended-region entries and preserve loans for enclosing regions.
10314
fn compactRegionalLoans 'arena 'checking (
10315
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
10316
    scope: ?*RegionScope
10317
) where 'arena: 'checking {
10318
    let oldLen = checker.regionalLen;
10319
    let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS];
10320
    let mut next: u32 = 0;
10321
    for i in 0..oldLen {
10322
        let loan = regionalLoan(checker, i);
10323
        if regionInScope(scope, loan.regionId) {
10324
            set checker.regional[next] = loan;
10325
            set mapping[i] = (1 as u64) << (next as u64);
10326
            set next += 1;
10327
        }
10328
    }
10329
    set env.regionalLoans = remapRegionalLoans(env.regionalLoans, &mapping[..oldLen]);
10330
    for i in 0..checker.loopDepth {
10331
        set checker.loopBackLoans[i] = remapRegionalLoans(checker.loopBackLoans[i], &mapping[..oldLen]);
10332
        set checker.loopExitLoans[i] = remapRegionalLoans(checker.loopExitLoans[i], &mapping[..oldLen]);
10333
    }
10334
    set checker.regionalLen = next;
10335
}
10336
10337
/// Check whether an access comes from the reference created by a projection.
10338
unsafe fn usesRegionalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: *ast::Node) -> bool {
10339
    if node.id == source.id {
10340
        return true;
10341
    }
10342
    let root = linearRootSymbol(self, node) else return false;
10343
    let origin = localReferenceSource(root) else return false;
10344
    return usesRegionalLoan(self, origin, source);
10345
}
10346
10347
/// Retain a full-region projection independently of its local binding scope.
10348
unsafe fn addRegionalLoan 'arena 'checking (
10349
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node, address: ast::AddressOf
10350
) throws (ResolveError) where 'arena: 'checking {
10351
    let ty = typeFor(checker.resolver, node) else return;
10352
    let mut class = types::PointerClass::Ref;
10353
    match ty {
10354
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
10355
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
10356
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
10357
        else => return,
10358
    }
10359
    let case types::PointerClass::Region(region) = class else return;
10360
    let storage = addressStorageClass(checker.resolver, address.target);
10361
    let case types::PointerClass::Region(parent) = storage else return;
10362
    if parent.id <> region.id {
10363
        return;
10364
    }
10365
    let place = borrowPlace(checker.resolver, address.target);
10366
    if place.root == nil {
10367
        return;
10368
    }
10369
    for i in 0..checker.regionalLen {
10370
        let loan = regionalLoan(checker, i);
10371
        if loan.source.id == node.id {
10372
            set env.regionalLoans |= (1 as u64) << (i as u64);
10373
            return;
10374
        }
10375
    }
10376
    if checker.regionalLen >= MAX_REGIONAL_LOANS {
10377
        throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow);
10378
    }
10379
    let index = checker.regionalLen;
10380
    set checker.regional[index] = RegionalLoan { source: node, regionId: region.id, place, exclusive: ast::isExclusiveAddress(address) };
10381
    set checker.regionalLen += 1;
10382
    set env.regionalLoans |= (1 as u64) << (index as u64);
10383
}
10384
10385
/// Return whether an initializer copies a shared reference with a named region.
10386
/// Address expressions and casts retain a loan on their source storage.
10387
fn copiesRegionalReference(ty: Type, value: *ast::Node) -> bool {
10388
    if referenceRegion(ty) == nil or isMutablePointerLike(ty) {
10389
        return false;
10390
    }
10391
    match value.value {
10392
        case ast::NodeValue::AddressOf(_), ast::NodeValue::As(_) => return false,
10393
        else => return true,
10394
    }
10395
}
10396
10397
/// Return the initializer that supplies a local reference's storage.
10398
fn localReferenceSource(sym: &Symbol) -> ?*ast::Node {
10399
    let case SymbolData::Value { type: ty, .. } = sym.data else return nil;
10400
    if let case Type::Session(_) = ty {
10401
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
10402
            return binding.value;
10403
        }
10404
    }
10405
    if isRefType(ty) {
10406
        if let case ast::NodeValue::Let(binding) = sym.node.value {
10407
            if copiesRegionalReference(ty, binding.value) {
10408
                return nil;
10409
            }
10410
            return binding.value;
10411
        }
10412
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
10413
            return binding.value;
10414
        }
10415
    }
10416
    return nil;
10417
}
10418
10419
/// Resolve a place through reference locals without extending its storage lifetime.
10420
unsafe fn borrowPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> BorrowPlace {
10421
    let mut place = emptyBorrowPlace();
10422
    match node.value {
10423
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
10424
            let sym = symbolFor(self, node) else return place;
10425
            if let source = localReferenceSource(sym) {
10426
                let origin = borrowPlace(self, source);
10427
                if origin.root <> nil {
10428
                    return origin;
10429
                }
10430
            }
10431
            set place.root = sym;
10432
        }
10433
        case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target),
10434
        case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value),
10435
        case ast::NodeValue::FieldAccess(access) => {
10436
            set place = borrowPlace(self, access.parent);
10437
            if let ty = typeFor(self, access.parent) {
10438
                if let case Type::Pointer { .. } = ty; not isRefType(ty) and place.len > 0 {
10439
                    set place.len = 0;
10440
                    set place.precise = false;
10441
                }
10442
                if let case Type::Nominal(NominalType::Record(_)) = autoDeref(ty);
10443
                    place.precise and place.len < MAX_BORROW_FIELDS
10444
                {
10445
                    if let index = recordFieldIndexFor(self, access.child) {
10446
                        set place.fields[place.len] = index;
10447
                        set place.len += 1;
10448
                        return place;
10449
                    }
10450
                }
10451
            }
10452
            set place.precise = false;
10453
        }
10454
        case ast::NodeValue::Subscript { container, .. } => {
10455
            set place = borrowPlace(self, container);
10456
            if let ty = typeFor(self, container) {
10457
                if let case Type::Slice { class, .. } = autoDeref(ty); not types::isReference(class) {
10458
                    set place.len = 0;
10459
                }
10460
            }
10461
            set place.precise = false;
10462
        }
10463
        case ast::NodeValue::Deref(target) => {
10464
            set place = borrowPlace(self, target);
10465
            if let ty = typeFor(self, target); not isRefType(ty) and place.len > 0 {
10466
                set place.len = 0;
10467
                set place.precise = false;
10468
            }
10469
        }
10470
        else => {}
10471
    }
10472
    return place;
10473
}
10474
10475
/// Two places overlap unless distinct inline fields prove separation.
10476
fn placesOverlap(left: &BorrowPlace, right: &BorrowPlace) -> bool {
10477
    if left.root == nil or left.root <> right.root {
10478
        return false;
10479
    }
10480
    let count = left.len if left.len < right.len else right.len;
10481
    for i in 0..count {
10482
        if left.fields[i] <> right.fields[i] {
10483
            return false;
10484
        }
10485
    }
10486
    return true;
10487
}
10488
10489
/// Check whether access uses a reference or one of its lexical reborrows.
10490
unsafe fn usesLocalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, binding: *unsafe mut Symbol) -> bool {
10491
    let root = linearRootSymbol(self, node) else return false;
10492
    if root == binding {
10493
        return true;
10494
    }
10495
    let source = localReferenceSource(root) else return false;
10496
    return usesLocalLoan(self, source, binding);
10497
}
10498
10499
/// Reject accesses that conflict with a reference in an active lexical scope.
10500
unsafe fn checkLocalLoans 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, exclusive: bool)
10501
    throws (ResolveError) where 'arena: 'checking
10502
{
10503
    let place = borrowPlace(checker.resolver, node);
10504
    let root = place.root else return;
10505
    for i in 0..checker.regionalLen {
10506
        if (env.regionalLoans & ((1 as u64) << (i as u64))) == 0 {
10507
            continue;
10508
        }
10509
        let loan = regionalLoan(checker, i);
10510
        if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
10511
            and not usesRegionalLoan(checker.resolver, node, loan.source)
10512
        {
10513
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
10514
        }
10515
    }
10516
    for i in 0..checker.localLen {
10517
        let loan = checker.locals[i];
10518
        let mut throughBinding = false;
10519
        if let binding = loan.binding {
10520
            set throughBinding = usesLocalLoan(checker.resolver, node, binding);
10521
        }
10522
        if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
10523
            and not throughBinding
10524
        {
10525
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
10526
        }
10527
    }
10528
}
10529
10530
/// Retain source storage for local borrows and region headers.
10531
unsafe fn addLocalLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, binding: ast::Let)
10532
    throws (ResolveError) where 'arena: 'checking
10533
{
10534
    let ty = typeFor(checker.resolver, binding.ident) else return;
10535
    if not isRefType(ty) {
10536
        let case Type::Session(_) = ty else return;
10537
        let case ast::NodeValue::RegionBinding(_) = node.value else return;
10538
    }
10539
    if let case ast::NodeValue::Let(_) = node.value;
10540
        copiesRegionalReference(ty, binding.value)
10541
    {
10542
        return;
10543
    }
10544
    let place = borrowPlace(checker.resolver, binding.value);
10545
    if place.root == nil {
10546
        if referenceRegion(ty) <> nil {
10547
            return;
10548
        }
10549
        throw emitError(checker.resolver, node, ErrorKind::RefBinding);
10550
    }
10551
    if checker.localLen >= MAX_LINEAR_BINDINGS {
10552
        throw emitError(checker.resolver, node, ErrorKind::Internal);
10553
    }
10554
    let sym = symbolFor(checker.resolver, node) else panic "reference without binding";
10555
    let mut exclusive = isExclusiveArgument(ty) or createsCellBorrow(binding.value);
10556
    if let case Type::Cell { .. } = ty {
10557
        if let case ast::NodeValue::As(expr) = binding.value.value {
10558
            if let source = typeFor(checker.resolver, expr.value) {
10559
                if let case Type::Pointer { mutable: true, .. } = source {
10560
                    set exclusive = true;
10561
                }
10562
            }
10563
        }
10564
    }
10565
    try checkLocalLoans(checker, env, binding.value, exclusive);
10566
    set checker.locals[checker.localLen] = LocalLoan { binding: sym, place, exclusive };
10567
    set checker.localLen += 1;
10568
}
10569
10570
/// Protect storage borrowed by a pointer pattern until its bindings leave scope.
10571
unsafe fn addPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, subject: *ast::Node)
10572
    throws (ResolveError) where 'arena: 'checking
10573
{
10574
    let ty = typeFor(checker.resolver, subject) else return;
10575
    if unwrapMatchSubject(ty).by == MatchBy::Value {
10576
        return;
10577
    }
10578
    let place = borrowPlace(checker.resolver, subject);
10579
    if place.root == nil {
10580
        return;
10581
    }
10582
    if checker.loanLen >= MAX_LINEAR_BINDINGS {
10583
        throw emitError(checker.resolver, subject, ErrorKind::Internal);
10584
    }
10585
    set checker.loans[checker.loanLen] = place;
10586
    set checker.loanLen += 1;
10587
}
10588
10589
/// Reject a write, mutable loan, or ownership transfer of a pattern source.
10590
unsafe fn checkPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, node: *ast::Node)
10591
    throws (ResolveError) where 'arena: 'checking
10592
{
10593
    let place = borrowPlace(checker.resolver, node);
10594
    let root = place.root else return;
10595
    for i in 0..checker.loanLen {
10596
        if placesOverlap(&checker.loans[i], &place) {
10597
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
10598
        }
10599
    }
10600
}
10601
10602
/// Return whether a parameter borrows its argument only for the call.
10603
fn isBorrowedReferenceParameter(ty: Type) -> bool {
10604
    if not isRefType(ty) {
10605
        return false;
10606
    }
10607
    match ty {
10608
        case Type::Cell { class, .. } => return class == types::PointerClass::Ref,
10609
        case Type::Pointer { class, .. } => return class == types::PointerClass::Ref,
10610
        case Type::Slice { class, .. } => return class == types::PointerClass::Ref,
10611
        case Type::TraitObject { class, .. } => return class == types::PointerClass::Ref,
10612
        else => return false,
10613
    }
10614
}
10615
10616
/// Return whether a parameter can mutate or consume its argument's storage.
10617
unsafe fn isExclusiveArgument(ty: Type) -> bool {
10618
    match ty {
10619
        case Type::Pointer { mutable, .. } => return mutable,
10620
        case Type::Slice { mutable, .. } => return mutable,
10621
        case Type::TraitObject { mutable, .. } => return mutable,
10622
        else => return isMoveOnly(ty),
10623
    }
10624
}
10625
10626
/// Add the value identifiers introduced by a pattern.
10627
/// Return whether the pattern introduces references to its source storage.
10628
unsafe fn addLinearPatternBindings 'arena 'checking (
10629
    checker: &mut LinearChecker 'arena 'checking,
10630
    env: &mut LinearEnv,
10631
    pattern: *ast::Node,
10632
) -> bool throws (ResolveError) where 'arena: 'checking {
10633
    let mut hasReferences = false;
10634
    match pattern.value {
10635
        case ast::NodeValue::Ident(_) => {
10636
            try addLinearBinding(checker, env, pattern);
10637
            if let ty = typeFor(checker.resolver, pattern) {
10638
                return isRefType(ty);
10639
            }
10640
        }
10641
        case ast::NodeValue::Call(call) => {
10642
            for arg in call.args {
10643
                if try addLinearPatternBindings(checker, env, arg) {
10644
                    set hasReferences = true;
10645
                }
10646
            }
10647
        }
10648
        case ast::NodeValue::RecordLit(lit) => {
10649
            for fieldNode in lit.fields {
10650
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
10651
                    else panic "addLinearPatternBindings: expected field";
10652
                if try addLinearPatternBindings(checker, env, field.value) {
10653
                    set hasReferences = true;
10654
                }
10655
            }
10656
        }
10657
        case ast::NodeValue::ArrayLit(items) => {
10658
            for item in items {
10659
                if try addLinearPatternBindings(checker, env, item) {
10660
                    set hasReferences = true;
10661
                }
10662
            }
10663
        }
10664
        else => {}
10665
    }
10666
    return hasReferences;
10667
}
10668
10669
/// Check a lexical block and exact-use of locals introduced in it.
10670
unsafe fn checkLinearBlock 'arena 'checking (
10671
    checker: &mut LinearChecker 'arena 'checking,
10672
    env: &mut LinearEnv,
10673
    node: *ast::Node,
10674
) throws (ResolveError) where 'arena: 'checking {
10675
    let start = env.len;
10676
    let localStart = checker.localLen;
10677
    let case ast::NodeValue::Block(block) = node.value
10678
        else panic "checkLinearBlock: expected block";
10679
    for stmt in block.statements {
10680
        if env.terminated {
10681
            break;
10682
        }
10683
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
10684
    }
10685
    try finishLinearScope(checker, env, start);
10686
    set checker.localLen = localStart;
10687
}
10688
10689
/// Push a repeated-control-flow boundary.
10690
/// Initialize all loop state at this depth before increasing `loopDepth`.
10691
fn enterLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
10692
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
10693
    let depth = checker.loopDepth;
10694
    set checker.loopBackLoans[depth] = 0;
10695
    set checker.loopExitLoans[depth] = 0;
10696
    set checker.loopRegions[depth] = checker.regions;
10697
    set checker.loopMarks[depth] = env.len;
10698
    set checker.loopAvailable[depth] = env.available;
10699
    set checker.loopExitAvailable[depth] = env.available;
10700
    set checker.loopHasNaturalExit[depth] = false;
10701
    set checker.loopBreakSeen[depth] = false;
10702
    set checker.loopDepth += 1;
10703
}
10704
10705
/// Require a repeated body's outer bindings to match its entry state.
10706
fn checkLinearLoopBackEdge 'arena 'checking (
10707
    checker: &mut LinearChecker 'arena 'checking,
10708
    env: &LinearEnv,
10709
    node: *ast::Node,
10710
) throws (ResolveError) where 'arena: 'checking {
10711
    if env.terminated {
10712
        return;
10713
    }
10714
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
10715
    let depth = checker.loopDepth - 1;
10716
    set checker.loopBackLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
10717
    let mark = checker.loopMarks[depth];
10718
    let entryAvailable = checker.loopAvailable[depth];
10719
    for i in 0..mark {
10720
        let bit = (1 as u64) << (i as u64);
10721
        if (env.available & bit) <> (entryAvailable & bit) {
10722
            let sym = linearSymbol(env, i);
10723
            throw emitError(
10724
                checker.resolver,
10725
                node,
10726
                ErrorKind::LinearBranchMismatch(sym.name),
10727
            );
10728
        }
10729
    }
10730
}
10731
10732
/// Record the ownership state of a loop's condition-false exit.
10733
fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
10734
    assert checker.loopDepth > 0, "linear loop exit outside loop";
10735
    let depth = checker.loopDepth - 1;
10736
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
10737
    set checker.loopExitAvailable[depth] = env.available;
10738
    set checker.loopHasNaturalExit[depth] = true;
10739
}
10740
10741
/// Require a break exit to agree with every other exit from this loop.
10742
fn checkLinearLoopBreak 'arena 'checking (
10743
    checker: &mut LinearChecker 'arena 'checking,
10744
    env: &LinearEnv,
10745
    node: *ast::Node,
10746
) throws (ResolveError) where 'arena: 'checking {
10747
    assert checker.loopDepth > 0, "linear loop break outside loop";
10748
    let depth = checker.loopDepth - 1;
10749
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
10750
    let mark = checker.loopMarks[depth];
10751
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
10752
        let expected = checker.loopExitAvailable[depth];
10753
        for i in 0..mark {
10754
            let bit = (1 as u64) << (i as u64);
10755
            if (env.available & bit) <> (expected & bit) {
10756
                let sym = linearSymbol(env, i);
10757
                throw emitError(
10758
                    checker.resolver,
10759
                    node,
10760
                    ErrorKind::LinearBranchMismatch(sym.name),
10761
                );
10762
            }
10763
        }
10764
    } else {
10765
        set checker.loopExitAvailable[depth] = env.available;
10766
    }
10767
    set checker.loopBreakSeen[depth] = true;
10768
}
10769
10770
/// Pop a repeated-control-flow boundary.
10771
fn exitLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking) where 'arena: 'checking {
10772
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
10773
    set checker.loopDepth -= 1;
10774
}
10775
10776
/// Check a conditional and merge its ownership states.
10777
unsafe fn checkLinearIf 'arena 'checking (
10778
    checker: &mut LinearChecker 'arena 'checking,
10779
    env: &mut LinearEnv,
10780
    node: *ast::Node,
10781
    conditional: ast::If,
10782
) throws (ResolveError) where 'arena: 'checking {
10783
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
10784
    let base = *env;
10785
    let mut thenEnv = base;
10786
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
10787
    let mut elseEnv = base;
10788
    if let branch = conditional.elseBranch {
10789
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
10790
    }
10791
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
10792
}
10793
10794
/// Check an expression conditional and merge its ownership states.
10795
unsafe fn checkLinearCondExpr 'arena 'checking (
10796
    checker: &mut LinearChecker 'arena 'checking,
10797
    env: &mut LinearEnv,
10798
    node: *ast::Node,
10799
    conditional: ast::CondExpr,
10800
    usage: LinearUse,
10801
) throws (ResolveError) where 'arena: 'checking {
10802
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
10803
    let base = *env;
10804
    let mut thenEnv = base;
10805
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
10806
    let mut elseEnv = base;
10807
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
10808
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
10809
}
10810
10811
/// Pointer patterns borrow their subject; value patterns consume it.
10812
fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse {
10813
    if let ty = typeFor(self, subject) {
10814
        if let case Type::Pointer { .. } = ty {
10815
            return LinearUse::Borrow;
10816
        }
10817
    }
10818
    return LinearUse::Consume;
10819
}
10820
10821
/// Check a match expression, including ownership transferred into patterns.
10822
unsafe fn checkLinearMatch 'arena 'checking (
10823
    checker: &mut LinearChecker 'arena 'checking,
10824
    env: &mut LinearEnv,
10825
    node: *ast::Node,
10826
    matchExpr: ast::Match,
10827
) throws (ResolveError) where 'arena: 'checking {
10828
    try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject));
10829
    let base = *env;
10830
    let mut haveResult = false;
10831
    let mut result = base;
10832
    for prongNode in matchExpr.prongs {
10833
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
10834
            else panic "checkLinearMatch: expected prong";
10835
        let mut branch = base;
10836
        let bindingsStart = branch.len;
10837
        let loanStart = checker.loanLen;
10838
        match prong.arm {
10839
            case ast::ProngArm::Case(patterns) => {
10840
                for pattern in patterns {
10841
                    if try addLinearPatternBindings(checker, &mut branch, pattern) {
10842
                        try addPatternLoan(checker, matchExpr.subject);
10843
                    }
10844
                }
10845
            }
10846
            case ast::ProngArm::Binding(binding) => {
10847
                if try addLinearPatternBindings(checker, &mut branch, binding) {
10848
                    try addPatternLoan(checker, matchExpr.subject);
10849
                }
10850
            }
10851
            case ast::ProngArm::Else => {}
10852
        }
10853
        if prong.guard <> nil {
10854
            for i in bindingsStart..branch.len {
10855
                let sym = linearSymbol(&branch, i);
10856
                if sym.usage == BindingUse::Linear {
10857
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
10858
                }
10859
            }
10860
        }
10861
        if let guard = prong.guard {
10862
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
10863
        }
10864
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
10865
        try finishLinearScope(checker, &mut branch, bindingsStart);
10866
        set checker.loanLen = loanStart;
10867
        if haveResult {
10868
            let previous = result;
10869
        try joinLinearBranches(checker, &mut result, &previous, &branch, node);
10870
        } else {
10871
            set result = branch;
10872
            set haveResult = true;
10873
        }
10874
    }
10875
    if haveResult {
10876
        set *env = result;
10877
    }
10878
}
10879
10880
/// Check call-scoped loans and argument ownership transfers.
10881
unsafe fn checkLinearCall 'arena 'checking (
10882
    checker: &mut LinearChecker 'arena 'checking,
10883
    env: &mut LinearEnv,
10884
    node: *ast::Node,
10885
    call: ast::Call,
10886
) throws (ResolveError) where 'arena: 'checking {
10887
    let localStart = checker.localLen;
10888
    match checker.resolver.nodeData.entries[node.id].extra {
10889
        case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => {
10890
            let case ast::NodeValue::FieldAccess(access) = call.callee.value
10891
                else panic "slice mutation without receiver";
10892
            try checkPatternLoan(checker, access.parent);
10893
            try checkLocalLoans(checker, env, access.parent, true);
10894
        }
10895
        else => {}
10896
    }
10897
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
10898
    let mut fnInfo: ?*FnType = nil;
10899
    match checker.resolver.nodeData.entries[node.id].extra {
10900
        case NodeExtra::TraitMethodCall { traitInfo, methodIndex } =>
10901
            set fnInfo = traitInfo.methods[methodIndex].fnType,
10902
        case NodeExtra::MethodCall { method } => set fnInfo = method.fnType,
10903
        else => {
10904
            if let calleeTy = typeFor(checker.resolver, call.callee) {
10905
                if let case Type::Fn(info) = calleeTy {
10906
                    set fnInfo = info;
10907
                }
10908
            }
10909
        }
10910
    }
10911
    let info = fnInfo else {
10912
        for arg in call.args {
10913
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
10914
        }
10915
        return;
10916
    };
10917
    let mut arguments: [?CallArgument; MAX_FN_PARAMS + 1] = [nil; MAX_FN_PARAMS + 1];
10918
    let mut argumentsLen: u32 = 0;
10919
10920
    // Method function types exclude their implicit receiver. Account for it
10921
    // explicitly so owning receivers are consumed and reference receivers
10922
    // participate in call-scoped loan conflict checks.
10923
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
10924
        let mut receiverClass = types::PointerClass::Unsafe;
10925
        let mut receiverMutable = false;
10926
        let mut haveReceiver = false;
10927
        match checker.resolver.nodeData.entries[node.id].extra {
10928
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
10929
                let method = &traitInfo.methods[methodIndex];
10930
                set receiverClass = method.receiverClass;
10931
                set receiverMutable = method.mutable;
10932
                set haveReceiver = true;
10933
            }
10934
            case NodeExtra::MethodCall { method } => {
10935
                set receiverClass = method.receiverClass;
10936
                set receiverMutable = method.mutable;
10937
                set haveReceiver = true;
10938
            }
10939
            else => {}
10940
        }
10941
        if haveReceiver {
10942
            try checkLocalLoans(checker, env, access.parent,
10943
                receiverMutable or receiverClass == types::PointerClass::Owned);
10944
            if receiverMutable or receiverClass == types::PointerClass::Owned {
10945
                try checkPatternLoan(checker, access.parent);
10946
            }
10947
            if receiverClass <> types::PointerClass::Unsafe {
10948
                set arguments[argumentsLen] = CallArgument {
10949
                    node: access.parent,
10950
                    exclusive: receiverClass == types::PointerClass::Owned or receiverMutable,
10951
                };
10952
                set argumentsLen += 1;
10953
            }
10954
            if types::isReference(receiverClass) {
10955
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
10956
                if createsExplicitBorrow(access.parent) {
10957
                    try retainCallLoan(checker, access.parent, receiverMutable);
10958
                }
10959
            } else if receiverClass == types::PointerClass::Owned {
10960
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
10961
            }
10962
        }
10963
    }
10964
10965
    for arg, i in call.args {
10966
        let expected = *info.paramTypes[i];
10967
        let argExclusive = isExclusiveArgument(expected) or createsCellBorrow(arg);
10968
        if argExclusive {
10969
            try checkPatternLoan(checker, arg);
10970
        }
10971
        if not isUnsafePointerType(expected) {
10972
            for j in 0..argumentsLen {
10973
                let previous = arguments[j] else panic "checkLinearCall: missing active argument";
10974
                if previous.exclusive or argExclusive {
10975
                    if let name = callArgumentConflict(checker.resolver, previous.node, arg) {
10976
                        throw emitError(checker.resolver, arg, ErrorKind::BorrowConflict(name));
10977
                    }
10978
                }
10979
            }
10980
            set arguments[argumentsLen] = CallArgument { node: arg, exclusive: argExclusive };
10981
            set argumentsLen += 1;
10982
        }
10983
        try checkLocalLoans(checker, env, arg, argExclusive);
10984
        if isBorrowedReferenceParameter(expected) {
10985
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
10986
        } else {
10987
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
10988
        }
10989
        if isRefType(expected) and createsExplicitBorrow(arg) {
10990
            try retainCallLoan(checker, arg, argExclusive);
10991
        }
10992
    }
10993
    set checker.localLen = localStart;
10994
    if *info.returnType == Type::Never and info.throwList.len == 0 {
10995
        set env.terminated = true;
10996
    }
10997
}
10998
10999
/// Return the storage name when two call arguments can address the same place.
11000
unsafe fn callArgumentConflict 'arena (
11001
    self: &mut Resolver 'arena, left: *ast::Node, right: *ast::Node
11002
) -> ?*[u8] {
11003
    match left.value {
11004
        case ast::NodeValue::CondExpr(cond) => {
11005
            if let name = callArgumentConflict(self, cond.thenExpr, right) {
11006
                return name;
11007
            }
11008
            return callArgumentConflict(self, cond.elseExpr, right);
11009
        }
11010
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, cast.value, right),
11011
        case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, value, right),
11012
        else => {}
11013
    }
11014
    match right.value {
11015
        case ast::NodeValue::CondExpr(cond) => {
11016
            if let name = callArgumentConflict(self, left, cond.thenExpr) {
11017
                return name;
11018
            }
11019
            return callArgumentConflict(self, left, cond.elseExpr);
11020
        }
11021
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, left, cast.value),
11022
        case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, left, value),
11023
        else => {}
11024
    }
11025
    let leftPlace = borrowPlace(self, left);
11026
    let rightPlace = borrowPlace(self, right);
11027
    if placesOverlap(&leftPlace, &rightPlace) {
11028
        let root = rightPlace.root else panic "callArgumentConflict: overlap without root";
11029
        return root.name;
11030
    }
11031
    return nil;
11032
}
11033
11034
/// Return whether evaluating an argument creates an explicit address borrow.
11035
fn createsExplicitBorrow(node: *ast::Node) -> bool {
11036
    match node.value {
11037
        case ast::NodeValue::AddressOf(_) => return true,
11038
        case ast::NodeValue::As(cast) => return createsExplicitBorrow(cast.value),
11039
        case ast::NodeValue::RegionApply { value, .. } => return createsExplicitBorrow(value),
11040
        case ast::NodeValue::CondExpr(cond) =>
11041
            return createsExplicitBorrow(cond.thenExpr) or createsExplicitBorrow(cond.elseExpr),
11042
        else => return false,
11043
    }
11044
}
11045
11046
/// Protect explicit address arguments until their call begins.
11047
unsafe fn retainCallLoan 'arena 'checking (
11048
    checker: &mut LinearChecker 'arena 'checking, node: *ast::Node, exclusive: bool
11049
) throws (ResolveError) where 'arena: 'checking {
11050
    match node.value {
11051
        case ast::NodeValue::CondExpr(cond) => {
11052
            try retainCallLoan(checker, cond.thenExpr, exclusive);
11053
            try retainCallLoan(checker, cond.elseExpr, exclusive);
11054
            return;
11055
        }
11056
        case ast::NodeValue::As(cast) => {
11057
            try retainCallLoan(checker, cast.value, exclusive);
11058
            return;
11059
        }
11060
        case ast::NodeValue::RegionApply { value, .. } => {
11061
            try retainCallLoan(checker, value, exclusive);
11062
            return;
11063
        }
11064
        else => {}
11065
    }
11066
    let place = borrowPlace(checker.resolver, node);
11067
    if place.root == nil {
11068
        return;
11069
    }
11070
    if checker.localLen >= MAX_LINEAR_BINDINGS {
11071
        throw emitError(checker.resolver, node, ErrorKind::Internal);
11072
    }
11073
    set checker.locals[checker.localLen] = LocalLoan { binding: nil, place, exclusive };
11074
    set checker.localLen += 1;
11075
}
11076
11077
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
11078
unsafe fn checkLinearIfLet 'arena 'checking (
11079
    checker: &mut LinearChecker 'arena 'checking,
11080
    env: &mut LinearEnv,
11081
    node: *ast::Node,
11082
    conditional: ast::IfLet,
11083
) throws (ResolveError) where 'arena: 'checking {
11084
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
11085
        isLinear(subjectTy)
11086
    {
11087
        throw emitError(
11088
            checker.resolver,
11089
            conditional.pattern.scrutinee,
11090
            ErrorKind::LinearPartialMove,
11091
        );
11092
    }
11093
    try checkLinearNode(
11094
        checker,
11095
        env,
11096
        conditional.pattern.scrutinee,
11097
        patternSubjectUse(checker.resolver, conditional.pattern.scrutinee),
11098
    );
11099
    let base = *env;
11100
    let mut thenEnv = base;
11101
    let bindingsStart = thenEnv.len;
11102
    let loanStart = checker.loanLen;
11103
    if try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern) {
11104
        try addPatternLoan(checker, conditional.pattern.scrutinee);
11105
    }
11106
    if let guard = conditional.pattern.guard {
11107
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
11108
    }
11109
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
11110
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
11111
    set checker.loanLen = loanStart;
11112
    let mut elseEnv = base;
11113
    if let branch = conditional.elseBranch {
11114
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
11115
    }
11116
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
11117
}
11118
11119
/// Check a repeated region-loan flow until its loop-entry mask is stable.
11120
/// Each additional pass must add a bit from the bounded regional loan table.
11121
unsafe fn checkLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
11122
    throws (ResolveError) where 'arena: 'checking
11123
{
11124
    if let case ast::NodeValue::For(forStmt) = node.value {
11125
        try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
11126
    }
11127
    let base = *env;
11128
    let depth = checker.loopDepth;
11129
    let mut entryLoans = base.regionalLoans;
11130
    loop {
11131
        let mut pass = base;
11132
        set pass.regionalLoans = entryLoans;
11133
        try checkLinearLoopPass(checker, &mut pass, node);
11134
        let next = entryLoans | checker.loopBackLoans[depth];
11135
        if next == entryLoans {
11136
            set *env = pass;
11137
            return;
11138
        }
11139
        set entryLoans = next;
11140
    }
11141
}
11142
11143
/// Check one pass through a loop with the current loop-entry loan state.
11144
unsafe fn checkLinearLoopPass 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
11145
    throws (ResolveError) where 'arena: 'checking
11146
{
11147
    match node.value {
11148
        case ast::NodeValue::While(whileStmt) => {
11149
            enterLinearLoop(checker, env);
11150
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
11151
            let conditionExit = *env;
11152
            setLinearLoopNaturalExit(checker, &conditionExit);
11153
            let mut bodyEnv = conditionExit;
11154
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
11155
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
11156
            exitLinearLoop(checker);
11157
            set *env = conditionExit;
11158
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
11159
            if let elseBranch = whileStmt.elseBranch {
11160
                let mut elseEnv = conditionExit;
11161
                try checkLinearNode(
11162
                    checker,
11163
                    &mut elseEnv,
11164
                    elseBranch,
11165
                    LinearUse::Discard,
11166
                );
11167
                let exits = *env;
11168
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
11169
            }
11170
        }
11171
        case ast::NodeValue::WhileLet(whileStmt) => {
11172
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
11173
                isLinear(subjectTy)
11174
            {
11175
                throw emitError(
11176
                    checker.resolver,
11177
                    whileStmt.pattern.scrutinee,
11178
                    ErrorKind::LinearPartialMove,
11179
                );
11180
            }
11181
            let base = *env;
11182
            enterLinearLoop(checker, env);
11183
            let mut bodyEnv = base;
11184
            try checkLinearNode(
11185
                checker,
11186
                &mut bodyEnv,
11187
                whileStmt.pattern.scrutinee,
11188
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
11189
            );
11190
            let mut conditionExit = bodyEnv;
11191
            let start = bodyEnv.len;
11192
            let loanStart = checker.loanLen;
11193
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
11194
                try addPatternLoan(checker, whileStmt.pattern.scrutinee);
11195
            }
11196
            if let guard = whileStmt.pattern.guard {
11197
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
11198
                let mut guardExit = bodyEnv;
11199
                try finishLinearScope(checker, &mut guardExit, start);
11200
                let previous = conditionExit;
11201
                try joinLinearBranches(
11202
                    checker,
11203
                    &mut conditionExit,
11204
                    &previous,
11205
                    &guardExit,
11206
                    guard,
11207
                );
11208
            }
11209
            setLinearLoopNaturalExit(checker, &conditionExit);
11210
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
11211
            try finishLinearScope(checker, &mut bodyEnv, start);
11212
            set checker.loanLen = loanStart;
11213
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
11214
            exitLinearLoop(checker);
11215
            set *env = conditionExit;
11216
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
11217
            if let elseBranch = whileStmt.elseBranch {
11218
                let mut elseEnv = conditionExit;
11219
                try checkLinearNode(
11220
                    checker,
11221
                    &mut elseEnv,
11222
                    elseBranch,
11223
                    LinearUse::Discard,
11224
                );
11225
                let exits = *env;
11226
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
11227
            }
11228
        }
11229
        case ast::NodeValue::For(forStmt) => {
11230
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
11231
                if isLinear(iterableTy) {
11232
                    throw emitError(
11233
                        checker.resolver,
11234
                        forStmt.iterable,
11235
                        ErrorKind::LinearPartialMove,
11236
                    );
11237
                }
11238
            }
11239
            let base = *env;
11240
            enterLinearLoop(checker, env);
11241
            setLinearLoopNaturalExit(checker, &base);
11242
            let mut bodyEnv = base;
11243
            let start = bodyEnv.len;
11244
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
11245
            if let index = forStmt.index {
11246
                try addLinearBinding(checker, &mut bodyEnv, index);
11247
            }
11248
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
11249
            try finishLinearScope(checker, &mut bodyEnv, start);
11250
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
11251
            exitLinearLoop(checker);
11252
            set *env = base;
11253
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
11254
            if let elseBranch = forStmt.elseBranch {
11255
                let mut elseEnv = base;
11256
                try checkLinearNode(
11257
                    checker,
11258
                    &mut elseEnv,
11259
                    elseBranch,
11260
                    LinearUse::Discard,
11261
                );
11262
                let exits = *env;
11263
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
11264
            }
11265
        }
11266
        case ast::NodeValue::Loop { body } => {
11267
            let base = *env;
11268
            enterLinearLoop(checker, env);
11269
            let mut bodyEnv = base;
11270
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
11271
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
11272
            let depth = checker.loopDepth - 1;
11273
            let breakSeen = checker.loopBreakSeen[depth];
11274
            let exitAvailable = checker.loopExitAvailable[depth];
11275
            exitLinearLoop(checker);
11276
            set *env = base;
11277
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
11278
            if breakSeen {
11279
                set env.available = exitAvailable;
11280
            } else {
11281
                set env.terminated = true;
11282
            }
11283
        }
11284
        else => panic "checkLinearLoopPass: expected loop",
11285
    }
11286
}
11287
11288
/// Transfer a cell's owning handle and check each address operand once.
11289
unsafe fn checkCellAddressOwner 'arena 'checking (
11290
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
11291
    node: *ast::Node, owner: *ast::Node
11292
) throws (ResolveError) where 'arena: 'checking {
11293
    if node == owner {
11294
        try checkLinearNode(checker, env, node, LinearUse::Consume);
11295
        return;
11296
    }
11297
    match node.value {
11298
        case ast::NodeValue::Deref(target) => try checkCellAddressOwner(checker, env, target, owner),
11299
        case ast::NodeValue::FieldAccess(access) => try checkCellAddressOwner(checker, env, access.parent, owner),
11300
        case ast::NodeValue::Subscript { container, index } => {
11301
            try checkCellAddressOwner(checker, env, container, owner);
11302
            try checkLinearNode(checker, env, index, LinearUse::Consume);
11303
        }
11304
        else => panic "cell address owner must belong to its place",
11305
    }
11306
}
11307
11308
/// Check one expression or statement under an ownership-use context.
11309
unsafe fn checkLinearNode 'arena 'checking (
11310
    checker: &mut LinearChecker 'arena 'checking,
11311
    env: &mut LinearEnv,
11312
    node: *ast::Node,
11313
    usage: LinearUse,
11314
) throws (ResolveError) where 'arena: 'checking {
11315
    if env.terminated {
11316
        return;
11317
    }
11318
    if usage <> LinearUse::Locate {
11319
        match node.value {
11320
            case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_),
11321
                 ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. },
11322
                 ast::NodeValue::Deref(_) => {
11323
                let mut exclusive = usage == LinearUse::Place and not isCellDeref(checker.resolver, node);
11324
                if usage == LinearUse::Consume {
11325
                    if let ty = typeFor(checker.resolver, node) {
11326
                        set exclusive = isExclusiveArgument(ty);
11327
                    }
11328
                }
11329
                try checkLocalLoans(checker, env, node, exclusive);
11330
            }
11331
            else => {}
11332
        }
11333
    }
11334
    match node.value {
11335
        case ast::NodeValue::Ident(_) => {
11336
            if usage <> LinearUse::Place {
11337
                try checkLinearIdent(checker, env, node);
11338
            }
11339
            if usage == LinearUse::Consume {
11340
                if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) {
11341
                    try checkPatternLoan(checker, node);
11342
                }
11343
                try consumeLinearIdent(checker, env, node);
11344
            }
11345
        }
11346
        case ast::NodeValue::ExprStmt(expr) => {
11347
            if let exprTy = typeFor(checker.resolver, expr) {
11348
                if isLinear(exprTy) {
11349
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
11350
                }
11351
            }
11352
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
11353
        }
11354
        case ast::NodeValue::RegionBlock { bindings, body, .. } => {
11355
            let previousRegions = checker.regions;
11356
            let case NodeExtra::Regions(scope) = checker.resolver.nodeData.entries[node.id].extra else {
11357
                if checker.resolver.errors.len > 0 {
11358
                    return;
11359
                }
11360
                panic "checkLinearNode: missing region scope";
11361
            };
11362
            set checker.regions = scope;
11363
            let start = env.len;
11364
            let localStart = checker.localLen;
11365
            for bindingNode in bindings {
11366
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
11367
                    else panic "checkLinearNode: invalid borrow binding";
11368
                try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
11369
                if env.terminated {
11370
                    break;
11371
                }
11372
                try addLinearBinding(checker, env, bindingNode);
11373
                try addLocalLoan(checker, env, bindingNode, ast::borrowBinding(binding));
11374
            }
11375
            try checkLinearBlock(checker, env, body);
11376
            try finishLinearScope(checker, env, start);
11377
            set checker.localLen = localStart;
11378
            set checker.regions = previousRegions;
11379
            compactRegionalLoans(checker, env, previousRegions);
11380
        }
11381
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
11382
        case ast::NodeValue::Let(binding) => {
11383
            let mut isUndefined = false;
11384
            if let case ast::NodeValue::Undef = binding.value.value {
11385
                set isUndefined = true;
11386
            }
11387
            if isUndefined {
11388
                if let bindingTy = typeFor(checker.resolver, binding.ident);
11389
                    isLinear(bindingTy)
11390
                {
11391
                    throw emitError(
11392
                        checker.resolver,
11393
                        binding.value,
11394
                        ErrorKind::LinearUndefined,
11395
                    );
11396
                }
11397
            }
11398
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
11399
            if env.terminated {
11400
                return;
11401
            }
11402
            try addLinearBinding(checker, env, node);
11403
            try addLocalLoan(checker, env, node, binding);
11404
            if isUndefined {
11405
                markLinearBindingUnavailable(checker.resolver, env, node);
11406
            }
11407
        }
11408
        case ast::NodeValue::Assign(assign) => {
11409
            if not isCellDeref(checker.resolver, assign.left) {
11410
                try checkPatternLoan(checker, assign.left);
11411
            }
11412
            let mut target: ?u32 = nil;
11413
            let mut targetLinear = false;
11414
            if let leftTy = typeFor(checker.resolver, assign.left) {
11415
                if isMoveOnly(leftTy) {
11416
                    set targetLinear = isLinear(leftTy);
11417
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
11418
                        if let sym = symbolFor(checker.resolver, assign.left) {
11419
                            set target = findLinearBinding(env, sym.id);
11420
                        }
11421
                    }
11422
                    if targetLinear and target == nil {
11423
                        throw emitError(
11424
                            checker.resolver,
11425
                            assign.left,
11426
                            ErrorKind::LinearOverwrite,
11427
                        );
11428
                    }
11429
                }
11430
            }
11431
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
11432
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
11433
            if let index = target {
11434
                if targetLinear and linearBindingAvailable(env, index) {
11435
                    throw emitError(
11436
                        checker.resolver,
11437
                        assign.left,
11438
                        ErrorKind::LinearOverwrite,
11439
                    );
11440
                }
11441
                set env.available |= (1 as u64) << (index as u64);
11442
            }
11443
        }
11444
        case ast::NodeValue::RegionApply { value, .. } =>
11445
            try checkLinearNode(checker, env, value, usage),
11446
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
11447
        case ast::NodeValue::AddressOf(addr) => {
11448
            try checkLocalLoans(checker, env, addr.target, ast::isExclusiveAddress(addr));
11449
            if ast::isExclusiveAddress(addr) {
11450
                try checkPatternLoan(checker, addr.target);
11451
            }
11452
            let mut owner: ?*ast::Node = nil;
11453
            if addr.kind == ast::AddressKind::Cell {
11454
                if let ty = typeFor(checker.resolver, node) {
11455
                    if let case Type::Cell { class: types::PointerClass::Owned, .. } = ty {
11456
                        set owner = addressOwner(checker.resolver, addr.target);
11457
                    }
11458
                }
11459
            }
11460
            if let source = owner {
11461
                try checkCellAddressOwner(checker, env, addr.target, source);
11462
            } else {
11463
                try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
11464
            }
11465
            try addRegionalLoan(checker, env, node, addr);
11466
        }
11467
        case ast::NodeValue::Deref(target) => {
11468
            if let resultTy = typeFor(checker.resolver, node) {
11469
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
11470
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
11471
                }
11472
            }
11473
            try checkLinearNode(checker, env, target, LinearUse::Locate);
11474
        }
11475
        case ast::NodeValue::FieldAccess(access) => {
11476
            if let resultTy = typeFor(checker.resolver, node) {
11477
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
11478
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
11479
                }
11480
            }
11481
            try checkLinearNode(checker, env, access.parent, LinearUse::Locate);
11482
        }
11483
        case ast::NodeValue::ScopeAccess(_) => {}
11484
        case ast::NodeValue::Subscript { container, index } => {
11485
            if let resultTy = typeFor(checker.resolver, node) {
11486
                if isMoveOnly(resultTy) and usage == LinearUse::Consume {
11487
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
11488
                }
11489
            }
11490
            try checkLinearNode(checker, env, container, LinearUse::Locate);
11491
            try checkLinearNode(checker, env, index, LinearUse::Consume);
11492
        }
11493
        case ast::NodeValue::RecordLit(lit) => {
11494
            for fieldNode in lit.fields {
11495
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
11496
                    else panic "checkLinearNode: expected field";
11497
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
11498
            }
11499
        }
11500
        case ast::NodeValue::ArrayLit(items) => {
11501
            for item in items {
11502
                try checkLinearNode(checker, env, item, LinearUse::Consume);
11503
            }
11504
        }
11505
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
11506
            if let itemTy = typeFor(checker.resolver, repeat.item) {
11507
                if not isCopy(itemTy) {
11508
                    throw emitError(
11509
                        checker.resolver,
11510
                        repeat.item,
11511
                        ErrorKind::LinearDiscard,
11512
                    );
11513
                }
11514
            }
11515
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
11516
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
11517
        }
11518
        case ast::NodeValue::BinOp(op) => {
11519
            if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or {
11520
                try checkLinearNode(checker, env, op.left, LinearUse::Consume);
11521
                let skipped = *env;
11522
                let mut evaluated = skipped;
11523
                try checkLinearNode(checker, &mut evaluated, op.right, LinearUse::Consume);
11524
                try joinLinearBranches(checker, env, &skipped, &evaluated, node);
11525
                return;
11526
            }
11527
            let mut operandUse = LinearUse::Consume;
11528
            match op.op {
11529
                case ast::BinaryOp::Eq, ast::BinaryOp::Ne,
11530
                     ast::BinaryOp::Lt, ast::BinaryOp::Gt,
11531
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
11532
                    set operandUse = LinearUse::Observe,
11533
                else => {}
11534
            }
11535
            try checkLinearNode(checker, env, op.left, operandUse);
11536
            try checkLinearNode(checker, env, op.right, operandUse);
11537
        }
11538
        case ast::NodeValue::UnOp(op) => {
11539
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
11540
        }
11541
        case ast::NodeValue::As(expr) => {
11542
            let mut castUse = usage;
11543
            if let targetTy = typeFor(checker.resolver, node) {
11544
                if let case Type::Cell { .. } = targetTy {
11545
                    set castUse = LinearUse::Consume;
11546
                }
11547
            }
11548
            if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) {
11549
                set castUse = LinearUse::Observe;
11550
            }
11551
            try checkLinearNode(checker, env, expr.value, castUse);
11552
        }
11553
        case ast::NodeValue::Range(range) => {
11554
            if let start = range.start {
11555
                try checkLinearNode(checker, env, start, LinearUse::Consume);
11556
            }
11557
            if let end = range.end {
11558
                try checkLinearNode(checker, env, end, LinearUse::Consume);
11559
            }
11560
        }
11561
        case ast::NodeValue::BuiltinCall { args, .. } => {
11562
            for arg in args {
11563
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
11564
            }
11565
        }
11566
        case ast::NodeValue::If(conditional) => {
11567
            try checkLinearIf(checker, env, node, conditional);
11568
        }
11569
        case ast::NodeValue::CondExpr(conditional) => {
11570
            try checkLinearCondExpr(checker, env, node, conditional, usage);
11571
        }
11572
        case ast::NodeValue::IfLet(conditional) => {
11573
            try checkLinearIfLet(checker, env, node, conditional);
11574
        }
11575
        case ast::NodeValue::LetElse(binding) => {
11576
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
11577
                isLinear(subjectTy)
11578
            {
11579
                throw emitError(
11580
                    checker.resolver,
11581
                    binding.pattern.scrutinee,
11582
                    ErrorKind::LinearPartialMove,
11583
                );
11584
            }
11585
            try checkLinearNode(
11586
                checker,
11587
                env,
11588
                binding.pattern.scrutinee,
11589
                patternSubjectUse(checker.resolver, binding.pattern.scrutinee),
11590
            );
11591
            let base = *env;
11592
            let mut guardedEnv = base;
11593
            if let guard = binding.pattern.guard {
11594
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
11595
            }
11596
            let mut successEnv = guardedEnv;
11597
            try addLinearPatternBindings(
11598
                checker,
11599
                &mut successEnv,
11600
                binding.pattern.pattern,
11601
            );
11602
            let mut fallbackEnv = base;
11603
            try checkLinearNode(
11604
                checker,
11605
                &mut fallbackEnv,
11606
                binding.elseBranch,
11607
                LinearUse::Consume,
11608
            );
11609
            if binding.pattern.guard <> nil {
11610
                let mut guardFallbackEnv = guardedEnv;
11611
                try checkLinearNode(
11612
                    checker,
11613
                    &mut guardFallbackEnv,
11614
                    binding.elseBranch,
11615
                    LinearUse::Consume,
11616
                );
11617
                let previous = fallbackEnv;
11618
                try joinLinearBranches(
11619
                    checker,
11620
                    &mut fallbackEnv,
11621
                    &previous,
11622
                    &guardFallbackEnv,
11623
                    binding.elseBranch,
11624
                );
11625
            }
11626
            if let case ast::PatternKind::Binding = binding.pattern.kind {
11627
                try addLinearPatternBindings(
11628
                    checker,
11629
                    &mut fallbackEnv,
11630
                    binding.pattern.pattern,
11631
                );
11632
            }
11633
            try joinLinearBranches(checker, env, &successEnv, &fallbackEnv, node);
11634
        }
11635
        case ast::NodeValue::Match(matchExpr) => {
11636
            try checkLinearMatch(checker, env, node, matchExpr);
11637
        }
11638
        case ast::NodeValue::Try(tryExpr) => {
11639
            try checkLinearNode(checker, env, tryExpr.expr, usage);
11640
            let success = *env;
11641
            if let resultTy = typeFor(checker.resolver, tryExpr.expr); resultTy == Type::Never {
11642
                if not tryExpr.returnsOptional and (tryExpr.catches.len > 0 or tryExpr.shouldPanic) {
11643
                    set env.terminated = true;
11644
                }
11645
            }
11646
            for catchNode in tryExpr.catches {
11647
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
11648
                    else panic "checkLinearNode: expected catch";
11649
                let mut branch = success;
11650
                let start = branch.len;
11651
                if let binding = catchClause.binding {
11652
                    try addLinearBinding(checker, &mut branch, binding);
11653
                }
11654
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
11655
                try finishLinearScope(checker, &mut branch, start);
11656
                let previous = *env;
11657
                try joinLinearBranches(checker, env, &previous, &branch, node);
11658
            }
11659
        }
11660
        case ast::NodeValue::While(_), ast::NodeValue::WhileLet(_),
11661
             ast::NodeValue::For(_), ast::NodeValue::Loop { .. } =>
11662
            try checkLinearLoop(checker, env, node),
11663
        case ast::NodeValue::Break => {
11664
            assert checker.loopDepth > 0, "linear loop control outside loop";
11665
            let start = checker.loopMarks[checker.loopDepth - 1];
11666
            try finishLinearScope(checker, env, start);
11667
            try checkLinearLoopBreak(checker, env, node);
11668
            set env.terminated = true;
11669
        }
11670
        case ast::NodeValue::Continue => {
11671
            assert checker.loopDepth > 0, "linear loop control outside loop";
11672
            let start = checker.loopMarks[checker.loopDepth - 1];
11673
            try finishLinearScope(checker, env, start);
11674
            try checkLinearLoopBackEdge(checker, env, node);
11675
            set env.terminated = true;
11676
        }
11677
        case ast::NodeValue::Return { value } => {
11678
            if let expr = value {
11679
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
11680
            }
11681
            try finishLinearExit(checker, env);
11682
        }
11683
        case ast::NodeValue::Throw { expr } => {
11684
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
11685
            try finishLinearExit(checker, env);
11686
        }
11687
        case ast::NodeValue::Panic { message } => {
11688
            if let expr = message {
11689
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
11690
            }
11691
            set env.terminated = true;
11692
        }
11693
        case ast::NodeValue::Assert { condition, message } => {
11694
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
11695
            if let expr = message {
11696
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
11697
            }
11698
        }
11699
        else => {}
11700
    }
11701
}
11702
11703
/// Check exact-use ownership for one resolved function.
11704
unsafe fn checkLinearFn 'arena (
11705
    self: &mut Resolver 'arena,
11706
    receiver: ?*ast::Node,
11707
    params: *[*ast::Node],
11708
    body: *ast::Node,
11709
) throws (ResolveError) {
11710
    try resolveNominalApplications(self, body);
11711
    let regions = self.regionScope;
11712
    let resolved: 'checking = &mut *self where 'arena: 'checking in {
11713
        let mut checker = linearChecker(resolved, regions);
11714
        let mut env = linearEnv();
11715
        if let receiverNode = receiver {
11716
            try addLinearBinding(&mut checker, &mut env, receiverNode);
11717
        }
11718
        for paramNode in params {
11719
            let case ast::NodeValue::FnParam(_) = paramNode.value
11720
                else panic "checkLinearFn: expected parameter";
11721
            try addLinearBinding(&mut checker, &mut env, paramNode);
11722
        }
11723
        try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
11724
        try finishLinearScope(&mut checker, &mut env, 0);
11725
    }
11726
}
11727
11728
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
11729
unsafe fn resolveModuleDefs 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
11730
    for stmt in block.statements {
11731
        try resolveNominalApplications(self, stmt);
11732
        try visitDef(self, stmt);
11733
        try resolveNominalApplications(self, stmt);
11734
    }
11735
}
11736
11737
/// Resolve all packages.
11738
/// Module entries retain their identity throughout resolution and diagnostics.
11739
export unsafe fn resolve 'arena (self: &mut Resolver 'arena, graph: &module::ModuleGraph, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
11740
    assert graph.entriesLen <= self.moduleEntries.len, "resolve: module registry capacity exceeded";
11741
    for i in 0..self.moduleEntries.len {
11742
        set self.moduleEntries[i] = module::get(graph, i as u16);
11743
    }
11744
11745
    // 1. Bind all package roots to enable cross-package references.
11746
    for i in 0..packages.len {
11747
        let pkg = packages[i];
11748
        // Enter a new scope for the module.
11749
        let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry);
11750
        // Bind the package root module name in the global package scope.
11751
        let scope = self.pkgScope;
11752
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope);
11753
11754
        exitModuleScope(self, enter);
11755
    }
11756
    // 2. Resolve each package's contents.
11757
    for i in 0..packages.len {
11758
        let pkg = packages[i];
11759
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
11760
        if not success(&diags) {
11761
            return diags;
11762
        }
11763
    }
11764
    return diagnostics(self);
11765
}
11766
11767
/// Resolve a package.
11768
unsafe fn resolvePackage 'arena (self: &mut Resolver 'arena, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
11769
    let rootId = rootEntry.id;
11770
    let scope = self.moduleScopes[rootId as u32]
11771
        else panic "resolvePackage: module scope not found";
11772
11773
    // Set up the module scope for this package.
11774
    set self.scope = scope;
11775
    set self.currentMod = rootId;
11776
11777
    let case ast::NodeValue::Block(block) = node.value
11778
        else panic "resolvePackage: expected block for module root";
11779
11780
    // Module graph analysis phase: bind all module name symbols and scopes.
11781
    try resolveModuleGraph(self, &block) catch {
11782
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11783
        return diagnostics(self);
11784
    };
11785
11786
    // Declaration phase: bind all names and analyze top-level declarations.
11787
    try resolveModuleDecls(self, &block) catch {
11788
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11789
    };
11790
    if self.errors.len > 0 {
11791
        return diagnostics(self);
11792
    }
11793
11794
    // Definition phase: analyze function bodies and sub-module definitions.
11795
    try resolveModuleDefs(self, &block) catch {
11796
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
11797
    };
11798
    setNodeType(self, node, Type::Void);
11799
11800
    return diagnostics(self);
11801
}