lib/std/lang/resolver.rad 511.1 KiB raw
1
//! Radiance semantic analyzer and type resolver.
2
//!
3
//! This module performs scope construction, symbol binding, and identifier
4
//! resolution on top of the AST produced by the parser.
5
6
export mod printer;
7
8
/// Unit tests for the resolver.
9
@test mod tests;
10
11
/// Tests for module-owned record representations.
12
@test mod opaqueTests;
13
14
// TODO: Move to raw vectors to reduce list duplication?
15
// TODO: When a function declaration fails to typecheck, it should still "exist".
16
// TODO: `ensureNominalResolved` should just run when you call `typeFor`.
17
// TODO: Have different types for positional vs. named field records.
18
19
use std::mem;
20
use std::io;
21
use std::lang::alloc;
22
use std::lang::types;
23
use std::lang::ast;
24
use std::lang::parser;
25
use std::lang::module;
26
27
/// Maximum number of diagnostics recorded.
28
export constant MAX_ERRORS: u32 = 64;
29
30
/// Power-of-two bucket count for interned type lookup chains.
31
constant TYPE_BUCKETS: u32 = 1024;
32
/// Power-of-two bucket count for exact nominal application lookup chains.
33
constant APPLICATION_BUCKETS: u32 = 1024;
34
/// Odd multiplier that combines cache key components.
35
constant CACHE_HASH_PRIME: u32 = 16777619;
36
37
/// Synthetic function name used when wrapping a bare expression for analysis.
38
export constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__";
39
/// Synthetic function name used when wrapping a block for analysis.
40
export constant ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__";
41
42
/// Maximum number of symbols stored within a module scope.
43
export constant MAX_MODULE_SYMBOLS: u32 = 528;
44
/// Maximum number of symbols stored within a local scope.
45
export constant MAX_LOCAL_SYMBOLS: u32 = 32;
46
/// Maximum function parameters.
47
export constant MAX_FN_PARAMS: u32 = 8;
48
/// Maximum function thrown types.
49
export constant MAX_FN_THROWS: u32 = 8;
50
/// Maximum number of variants in a union.
51
/// Nb. This should not be raised above `255`,
52
/// as tags are stored using 8-bits only.
53
export constant MAX_UNION_VARIANTS: u32 = 128;
54
/// Maximum nesting of loops.
55
export constant MAX_LOOP_DEPTH: u32 = 16;
56
/// Maximum trait instances.
57
export constant MAX_INSTANCES: u32 = 128;
58
/// Maximum standalone methods (across all types).
59
export constant MAX_METHODS: u32 = 256;
60
/// Maximum number of linear bindings active in one function.
61
constant MAX_LINEAR_BINDINGS: u32 = 32;
62
/// Maximum full-region projections active in nested lexical regions.
63
constant MAX_REGIONAL_LOANS: u32 = 32;
64
/// Maximum inline field depth used to prove borrow separation.
65
constant MAX_BORROW_FIELDS: u32 = 16;
66
/// Maximum nesting depth tracked for loops.
67
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
68
69
/// Trait definition stored in the resolver.
70
export record TraitType: Copy {
71
    /// Trait name.
72
    name: *[u8],
73
    /// Module that declares the trait.
74
    moduleId: u16,
75
    /// Method signatures, including from supertraits.
76
    methods: *unsafe mut [TraitMethod],
77
    /// Supertraits that must also be implemented.
78
    supertraits: *unsafe mut [*unsafe TraitType],
79
}
80
81
/// A single method signature within a trait.
82
export record TraitMethod: Copy {
83
    /// Method name.
84
    name: *[u8],
85
    /// Function type for the method, excluding the receiver.
86
    fnType: *FnType,
87
    /// Whether the receiver is mutable.
88
    mutable: bool,
89
    /// Pointer-like class used by the receiver.
90
    receiverClass: types::PointerClass,
91
    /// V-table slot index.
92
    index: u32,
93
}
94
95
/// An entry in the trait instance registry.
96
export record InstanceEntry: Copy {
97
    /// Trait type descriptor.
98
    traitType: *unsafe TraitType,
99
    /// Concrete type that implements the trait.
100
    concreteType: Type,
101
    /// Name of the concrete type.
102
    concreteTypeName: *[u8],
103
    /// Module where this instance was declared.
104
    moduleId: u16,
105
    /// Method symbols for each trait method, in declaration order.
106
    methods: *unsafe mut [*unsafe mut Symbol],
107
}
108
109
/// An entry in the method registry.
110
export record MethodEntry: Copy {
111
    /// Module where the method is defined.
112
    moduleId: u16,
113
    /// Concrete type that owns the method.
114
    concreteType: Type,
115
    /// Name of the concrete type.
116
    concreteTypeName: *[u8],
117
    /// Method name.
118
    name: *[u8],
119
    /// Function type excluding the receiver.
120
    fnType: *FnType,
121
    /// Whether the receiver is mutable.
122
    mutable: bool,
123
    /// Pointer-like class used by the receiver.
124
    receiverClass: types::PointerClass,
125
    /// Resolver-local identity of the method symbol.
126
    symbolId: u32,
127
    /// Function type including the receiver, used for emitted calls.
128
    fullFnType: *FnType,
129
}
130
131
/// Identifier for the synthetic `len` field.
132
export constant LEN_FIELD: *[u8] = "len";
133
/// Identifier for the synthetic `ptr` field.
134
export constant PTR_FIELD: *[u8] = "ptr";
135
/// Identifier for the synthetic `cap` field.
136
export constant CAP_FIELD: *[u8] = "cap";
137
138
/// Maximum `u16` value.
139
constant U16_MAX: u16 = 0xFFFF;
140
/// Maximum `u8` value.
141
constant U8_MAX: u16 = 0xFF;
142
143
/// Minimum `i8` value.
144
constant I8_MIN: i32 = -128;
145
/// Maximum `i8` value.
146
constant I8_MAX: i32 = 127;
147
/// Minimum `i16` value.
148
constant I16_MIN: i32 = -32768;
149
/// Maximum `i16` value.
150
constant I16_MAX: i32 = 32767;
151
152
/// Minimum `i32` value.
153
constant I32_MIN: i32 = -2147483648;
154
/// Maximum `i32` value.
155
constant I32_MAX: i32 = 2147483647;
156
/// Minimum `i64` value: -(2^63).
157
constant I64_MIN: i64 = -9223372036854775808;
158
/// Maximum `i64` value: 2^63 - 1.
159
constant I64_MAX: i64 = 9223372036854775807;
160
161
/// Size of a pointer in bytes.
162
export constant PTR_SIZE: u32 = 8;
163
164
/// Information about a record or tuple field.
165
export record RecordField: Copy {
166
    /// Field name, `nil` for positional fields.
167
    name: ?*[u8],
168
    /// Field type.
169
    fieldType: Type,
170
    /// Byte offset from the start of the record.
171
    offset: i32,
172
}
173
174
/// Information about a union variant.
175
record UnionVariant: Copy {
176
    name: *[u8],
177
    valueType: Type,
178
    symbol: *unsafe mut Symbol,
179
}
180
181
/// Array type payload.
182
export record ArrayType: Copy {
183
    item: *Type,
184
    length: u32,
185
}
186
187
/// Record nominal type.
188
export record RecordType: Copy {
189
    /// Module that can access the representation, or `nil` for public fields.
190
    privateModule: ?u16,
191
    /// Region parameters of the source declaration.
192
    regions: ?*RegionScope,
193
    /// Exact region arguments, if this is an applied type.
194
    application: ?*unsafe mut NominalApplication,
195
    fields: *unsafe [RecordField],
196
    labeled: bool,
197
    /// Shared layout of the source declaration.
198
    layout: *Layout,
199
    /// Whether the declaration explicitly carries the `Once` marker.
200
    declaredLinear: bool,
201
    /// Whether the declaration explicitly carries the `Copy` marker.
202
    declaredCopy: bool,
203
}
204
205
/// Union nominal type.
206
export record UnionType: Copy {
207
    /// Region parameters of the source declaration.
208
    regions: ?*RegionScope,
209
    /// Exact region arguments, if this is an applied type.
210
    application: ?*unsafe mut NominalApplication,
211
    variants: *unsafe [UnionVariant],
212
    /// Shared layout of the source declaration.
213
    layout: *Layout,
214
    /// Cached payload offset within the union aggregate.
215
    valOffset: u32,
216
    /// If all variants have void payloads.
217
    isAllVoid: bool,
218
    /// Whether the declaration explicitly carries the `Once` marker.
219
    declaredLinear: bool,
220
    /// Whether the declaration explicitly carries the `Copy` marker.
221
    declaredCopy: bool,
222
}
223
224
/// Metadata for user-defined types.
225
export union NominalType: Copy {
226
    /// Placeholder for a type that hasn't been fully resolved yet.
227
    /// Stores the declaration node for lazy resolution.
228
    Placeholder(*ast::Node),
229
    /// Declaration whose value layout is under analysis.
230
    Resolving(*ast::Node),
231
    /// Applied type whose field or variant view is not yet resolved.
232
    Application(*unsafe mut NominalApplication),
233
    Record(RecordType),
234
    Union(UnionType),
235
}
236
237
/// Coercion plan, when coercion from one type to another.
238
export union Coercion: Copy {
239
    /// No coercion, eg. `T -> T`.
240
    Identity,
241
    /// Eg. `u8 -> i32`. Stores both source and target types for lowering.
242
    NumericCast { from: Type, to: Type },
243
    /// Eg. `T -> ?T`. Stores the inner value type.
244
    OptionalLift(Type),
245
    /// Wrap return value in success variant of result type.
246
    ResultWrap,
247
    /// Coerce a concrete pointer to a trait object.
248
    TraitObject {
249
        /// Trait type information.
250
        traitInfo: *unsafe TraitType,
251
        /// Instance entry for v-table lookup.
252
        inst: *unsafe InstanceEntry,
253
    },
254
}
255
256
/// Result of resolving a module path.
257
record ResolvedModule: Copy {
258
    /// Module entry in the graph.
259
    entry: *module::ModuleEntry,
260
    /// Scope containing the module's declarations.
261
    scope: *unsafe mut Scope,
262
}
263
264
/// Type layout.
265
export record Layout: Copy {
266
    /// Size in bytes.
267
    size: u32,
268
    /// Alignment in bytes.
269
    alignment: u32,
270
}
271
272
/// Computed union layout parameters.
273
record UnionLayoutInfo: Copy {
274
    layout: Layout,
275
    valOffset: u32,
276
    isAllVoid: bool,
277
}
278
279
/// Pre-computed metadata for slice range expressions.
280
/// Used by the lowerer.
281
export record SliceRangeInfo: Copy {
282
    /// Element type of the resulting slice.
283
    itemType: *Type,
284
    /// Whether the resulting slice is mutable.
285
    mutable: bool,
286
    /// Static capacity if container is an array.
287
    capacity: ?u32,
288
}
289
290
/// Pre-computed metadata for `for` loop iteration.
291
/// Used by the lowerer to avoid re-analyzing the iterable type.
292
export union ForLoopInfo: Copy {
293
    /// Iterating over a range expression (e.g., `for i in 0..n`).
294
    Range {
295
        valType: *Type,
296
        range: ast::Range,
297
        bindingName: ?*[u8],
298
        indexName: ?*[u8]
299
    },
300
    /// Iterating over an array or slice. For arrays, the length field is set.
301
    Collection {
302
        elemType: *Type,
303
        length: ?u32,
304
        bindingName: ?*[u8],
305
        indexName: ?*[u8]
306
    },
307
}
308
309
/// Resolved function signature details.
310
export record FnType: Copy {
311
    /// Symbolic regions declared by the source function.
312
    regions: ?*RegionScope,
313
    /// Parameter types in call order.
314
    paramTypes: *[*Type],
315
    /// Return value type.
316
    returnType: *Type,
317
    /// Error types that the function can throw.
318
    throwList: *[*Type],
319
    /// Whether calling this function requires an unsafe context.
320
    isUnsafe: bool,
321
}
322
323
/// Describes a type computed during semantic analysis.
324
export union Type: Copy {
325
    /// A type that couldn't be decided.
326
    Unknown,
327
    /// Types only used during inference.
328
    Nil, Undefined, Int,
329
    /// Primitive types.
330
    Void, Opaque, Never, Bool,
331
    /// Integer types.
332
    U8, U16, U32, U64, I8, I16, I32, I64,
333
    /// Shared cell pointer with controlled payload access.
334
    Cell {
335
        /// Storage lifetime and ownership class.
336
        class: types::PointerClass,
337
        /// Optional compile-time permission region.
338
        permission: ?*unsafe types::Region,
339
        /// Payload type, preserved by all writes.
340
        payload: *Type,
341
    },
342
    /// Affine allocation interface retained by a lexical region.
343
    Session(*unsafe types::Region),
344
    /// Range types, eg. `start..end`.
345
    Range {
346
        start: ?*Type,
347
        end: ?*Type,
348
    },
349
    /// Owning pointer-like address.
350
    Pointer {
351
        class: types::PointerClass,
352
        target: *Type,
353
        mutable: bool,
354
    },
355
    /// Owning slice.
356
    Slice {
357
        class: types::PointerClass,
358
        item: *Type,
359
        mutable: bool,
360
    },
361
    /// Eg. `[i32; 32]`.
362
    Array(ArrayType),
363
    /// Eg. `?T`.
364
    Optional(*Type),
365
    /// Eg. `fn id(i32) -> i32`.
366
    Fn(*FnType),
367
    /// Named, ie. user-defined types, includes union variants.
368
    Nominal(*unsafe NominalType),
369
    /// Owning trait object. An erased type with v-table.
370
    TraitObject {
371
        /// Ownership and safety class.
372
        class: types::PointerClass,
373
        /// Trait definition.
374
        traitInfo: *unsafe TraitType,
375
        /// Whether the pointer is mutable.
376
        mutable: bool,
377
    },
378
}
379
380
/// Structured diagnostic payload for type mismatches.
381
export record TypeMismatch: Copy {
382
    expected: Type,
383
    actual: Type,
384
}
385
386
/// Structured diagnostic payload for invalid `as` casts.
387
export record InvalidAsCast: Copy {
388
    from: Type,
389
    to: Type,
390
}
391
392
/// Diagnostic payload for argument count mismatches.
393
export record CountMismatch: Copy {
394
    expected: u32,
395
    actual: u32,
396
}
397
398
/// Detailed payload attached to a symbol, specialized per symbol kind.
399
export union SymbolData: Copy {
400
    /// Payload describing mutable bindings like variables or functions.
401
    Value {
402
        /// Whether the binding permits mutation.
403
        mutable: bool,
404
        /// Custom alignment requirement, or 0 for default.
405
        alignment: u32,
406
        /// Resolved type associated with the value.
407
        type: Type,
408
        /// Whether the variable's address is taken anywhere (via `&` or `&mut`).
409
        /// Used by the lowerer to allocate a stack slot eagerly.
410
        addressTaken: bool,
411
    },
412
    /// Payload describing constants.
413
    Constant {
414
        /// Resolved type associated with the value.
415
        type: Type,
416
        /// Constant value, if any.
417
        value: ?ConstValue,
418
    },
419
    /// Payload describing union variants and the union type they instantiate.
420
    Variant {
421
        /// Variant payload type.
422
        type: Type,
423
        /// Union declaration.
424
        decl: *ast::Node,
425
        /// Variant ordinal in declaration order.
426
        ordinal: u32,
427
        /// Variant index within the union.
428
        index: u32,
429
    },
430
    /// Module reference.
431
    Module {
432
        /// Module entry in the graph.
433
        entry: *module::ModuleEntry,
434
        /// Module scope.
435
        scope: *unsafe mut Scope,
436
    },
437
    /// Payload describing type symbols with their resolved type.
438
    Type(*unsafe mut NominalType),
439
    /// Trait symbol.
440
    Trait(*unsafe mut TraitType),
441
}
442
443
/// Resolved symbol allocated during semantic analysis.
444
export record Symbol: Copy {
445
    /// Unique identity within the resolver that created this symbol.
446
    id: u32,
447
    /// Symbol name in source code.
448
    name: *[u8],
449
    /// Data associated with the symbol.
450
    data: SymbolData,
451
    /// Bitset of attributes applied to the declaration.
452
    attrs: u32,
453
    /// AST node that introduced the symbol.
454
    node: *ast::Node,
455
    /// Module ID this symbol belongs to. Only for module-level symbols.
456
    moduleId: ?u16,
457
}
458
459
/// Integer constant payload.
460
export record ConstInt: Copy {
461
    /// Absolute magnitude of the value.
462
    magnitude: u64,
463
    /// Bit width of the integer.
464
    bits: u8,
465
    /// Whether the integer is signed.
466
    signed: bool,
467
    /// Whether the value is negative (only valid when `signed` is true).
468
    negative: bool,
469
}
470
471
/// Constant value recorded for literal nodes.
472
export union ConstValue: Copy {
473
    Bool(bool),
474
    Char(u8),
475
    String(*[u8]),
476
    Int(ConstInt),
477
}
478
479
/// Integer range metadata for primitive integer types.
480
union IntegerRange: Copy {
481
    Signed {
482
        bits: u8,
483
        min: i64,
484
        max: i64,
485
        lim: u64,
486
    },
487
    Unsigned {
488
        bits: u8,
489
        max: u64,
490
    },
491
}
492
493
/// Diagnostic emitted by the analyzer.
494
export record Error: Copy {
495
    /// Error category.
496
    kind: ErrorKind,
497
    /// Node associated with the error, if known.
498
    node: ?*ast::Node,
499
    /// Module ID where this error occurred.
500
    moduleId: u16,
501
}
502
503
/// High-level classification for semantic diagnostics.
504
export union ErrorKind: Copy {
505
    /// Identifier declared more than once in the same scope.
506
    DuplicateBinding(*[u8]),
507
    /// Identifier referenced before it was declared.
508
    UnresolvedSymbol(*[u8]),
509
    /// Attempted to assign to an immutable binding.
510
    ImmutableBinding,
511
    /// Slice append requires a valid allocator record and callback.
512
    InvalidSliceAllocator,
513
    /// Expected a compile-time constant expression.
514
    ConstExprRequired,
515
    /// Symbol arena exhausted while binding identifiers.
516
    SymbolOverflow,
517
    /// Expression has the wrong type.
518
    TypeMismatch(TypeMismatch),
519
    /// Numeric literal does not fit within the required range.
520
    NumericLiteralOverflow,
521
    /// Record literal omitted a required field.
522
    RecordFieldMissing(*[u8]),
523
    /// Record representation is private to another module.
524
    OpaqueRecordAccess,
525
    /// Record literal referenced a field that does not exist.
526
    RecordFieldUnknown(*[u8]),
527
    /// Brace syntax used on unlabeled record.
528
    RecordFieldStyleMismatch,
529
    /// Record literal supplied the wrong number of fields.
530
    RecordFieldCountMismatch(CountMismatch),
531
    /// Record literal fields not in declaration order.
532
    RecordFieldOutOfOrder { field: *[u8], prev: *[u8] },
533
    /// Function call supplied the wrong number of arguments.
534
    FnArgCountMismatch(CountMismatch),
535
    /// Function throws list has the wrong number of types.
536
    FnThrowCountMismatch(CountMismatch),
537
    /// Expected an identifier node.
538
    ExpectedIdentifier,
539
    /// Expected any optional type.
540
    ExpectedOptional,
541
    /// Expected a numeric type.
542
    ExpectedNumeric,
543
    /// Expected a pointer type.
544
    ExpectedPointer,
545
    /// Expected a record type.
546
    ExpectedRecord,
547
    /// Expected an array or slice value.
548
    ExpectedIndexable,
549
    /// Expected an iterable (array, slice, or range) for a `for` loop.
550
    ExpectedIterable,
551
    /// Invalid `as` cast between the provided types.
552
    InvalidAsCast(InvalidAsCast),
553
    /// Invalid alignment value specified.
554
    InvalidAlignmentValue(u32),
555
    /// Invalid module path.
556
    InvalidModulePath,
557
    /// Invalid identifier.
558
    InvalidIdentifier(*ast::Node),
559
    /// Placeholder used where a value expression is required.
560
    PlaceholderExpression,
561
    /// Invalid scope access.
562
    InvalidScopeAccess,
563
    /// Referenced an unknown array field.
564
    ArrayFieldUnknown(*[u8]),
565
    /// Referenced an unknown slice field.
566
    SliceFieldUnknown(*[u8]),
567
    /// Array slicing without taking an address.
568
    SliceRequiresAddress,
569
    /// Slice bounds exceed array length.
570
    SliceRangeOutOfBounds,
571
    /// Unexpected `return` statement.
572
    UnexpectedReturn,
573
    /// Unexpected module name.
574
    UnexpectedModuleName,
575
    /// Unexpected node.
576
    UnexpectedNode(*ast::Node),
577
    /// Function with non-void return type falls through without returning.
578
    FnMissingReturn,
579
    /// Function is missing a body.
580
    FnMissingBody,
581
    /// Function body is not expected.
582
    FnUnexpectedBody,
583
    /// Intrinsic function must not have a body.
584
    IntrinsicUnexpectedBody,
585
    /// Encountered loop control outside of a loop construct.
586
    InvalidLoopControl,
587
    /// `try` used when the enclosing function does not declare throws.
588
    TryRequiresThrows,
589
    /// `try` used to propagate an error not declared by the enclosing function.
590
    TryIncompatibleError,
591
    /// `throw` used when the enclosing function does not declare throws.
592
    ThrowRequiresThrows,
593
    /// `throw` used with an error type not declared by the enclosing function.
594
    ThrowIncompatibleError,
595
    /// `try` applied to an expression that cannot throw.
596
    TryNonThrowing,
597
    /// Inferred catch binding used with multi-error callee.
598
    TryCatchMultiError,
599
    /// Duplicate error type in typed catch clauses.
600
    TryCatchDuplicateType,
601
    /// Distinct error types have the same tag after region erasure.
602
    AmbiguousRegionalError,
603
    /// Typed catch clauses do not cover all error types.
604
    TryCatchNonExhaustive,
605
    /// Called a fallible function without using `try`.
606
    MissingTry,
607
    /// Cannot use opaque type in this context.
608
    OpaqueTypeNotAllowed,
609
    /// Cannot dereference pointer to opaque type.
610
    OpaqueTypeDeref,
611
    /// Cannot perform pointer arithmetic on opaque pointer.
612
    OpaquePointerArithmetic,
613
    /// Cannot infer type from context.
614
    CannotInferType,
615
    /// Cannot assign a void value to a variable.
616
    CannotAssignVoid,
617
    /// `default` attribute used on a non-function declaration.
618
    DefaultAttrOnlyOnFn,
619
    /// Union variant requires a payload but none was provided.
620
    UnionVariantPayloadMissing(*[u8]),
621
    /// Union variant does not expect a payload but one was provided.
622
    UnionVariantPayloadUnexpected(*[u8]),
623
    /// `match` on a union omits a variant without a `default` case.
624
    UnionMatchNonExhaustive(*[u8]),
625
    /// `match` on an optional is missing a value case.
626
    OptionalMatchMissingValue,
627
    /// `match` on an optional is missing a nil case.
628
    OptionalMatchMissingNil,
629
    /// `match` on a bool is missing a case (true or false).
630
    BoolMatchMissing(bool),
631
    /// `match` on a non-union type is missing a catch-all.
632
    MatchNonExhaustive,
633
    /// `match` has more than one catch-all prongs.
634
    DuplicateCatchAll,
635
    /// `match` has a prong after an unguarded catch-all.
636
    CatchAllMustBeLast,
637
    /// `match` has a duplicate case pattern.
638
    DuplicateMatchPattern,
639
    /// `match` has an unreachable `else`: all cases are already handled.
640
    UnreachableElse,
641
    /// Builtin called with wrong number of arguments.
642
    BuiltinArgCountMismatch(CountMismatch),
643
    /// Instance method receiver mutability does not match the trait declaration.
644
    ReceiverMutabilityMismatch,
645
    /// Duplicate instance declaration for the same (trait, type) pair.
646
    DuplicateInstance,
647
    /// Instance declaration is missing a required trait method.
648
    MissingTraitMethod(*[u8]),
649
    /// Trait name used as a value expression.
650
    UnexpectedTraitName,
651
    /// Trait method receiver does not point to the declaring trait.
652
    TraitReceiverMismatch,
653
    /// Trait declaration and instance disagree about unsafe call requirements.
654
    TraitMethodSafetyMismatch,
655
    /// Function declaration has too many parameters.
656
    FnParamOverflow(CountMismatch),
657
    /// Function declaration has too many throws.
658
    FnThrowOverflow(CountMismatch),
659
    /// Trait declaration has too many methods.
660
    TraitMethodOverflow(CountMismatch),
661
    /// Instance declaration is missing a required supertrait instance.
662
    MissingSupertraitInstance(*[u8]),
663
    /// An affine binding was used after it moved.
664
    AffineUseAfterMove(*[u8]),
665
    /// Linear binding was consumed more than once.
666
    LinearUseAfterConsume(*[u8]),
667
    /// Linear binding remains available at an exit.
668
    LinearNotConsumed(*[u8]),
669
    /// A case-pattern `let-else` fallback must terminate control flow.
670
    LinearLetElseMustTerminate,
671
    /// Branches disagree about a linear binding's state.
672
    LinearBranchMismatch(*[u8]),
673
    /// A linear field cannot be moved independently.
674
    LinearPartialMove,
675
    /// A linear value cannot be discarded.
676
    LinearDiscard,
677
    /// Assignment would overwrite a live linear value.
678
    LinearOverwrite,
679
    /// `undefined` cannot initialize a linear type.
680
    LinearUndefined,
681
    /// A `Copy` declaration contains a non-copy field or variant.
682
    CopyContainsNonCopy,
683
    /// A declaration carries both `Copy` and `Once`.
684
    ConflictingOwnershipMarkers,
685
    /// A region name is not visible in this declaration or block.
686
    UnknownRegion(*[u8]),
687
    /// A region application has the wrong argument count.
688
    RegionArgumentCount(CountMismatch),
689
    /// A region parameter has no consistent argument from checked references.
690
    RegionInference(*[u8]),
691
    /// A region argument does not satisfy its declared parent relation.
692
    RegionParent(*[u8]),
693
    /// A region parent relation contains a cycle.
694
    RegionCycle(*[u8]),
695
    /// A value retains a region that has left lexical scope.
696
    RegionEscape(*[u8]),
697
    /// A session requires one exclusive borrow of an allocation trait implementer.
698
    InvalidSessionSource,
699
    /// Allocation requires a value that can be discarded without destruction.
700
    InvalidAllocationValue,
701
    /// Cell payload does not satisfy storage and ownership requirements.
702
    InvalidCellPayload,
703
    /// Cell payload access lacks matching lexical permission authority.
704
    CellPermissionRequired(*[u8]),
705
    /// The allocated value has an invalid or overflowing layout.
706
    InvalidAllocationLayout,
707
    /// A compiler-known allocation trait method has an invalid signature.
708
    InvalidAllocationRuntime,
709
    /// The function has too many distinct full-region projections.
710
    RegionalLoanOverflow,
711
    /// A nominal value layout contains itself.
712
    RecursiveType,
713
    /// A reference appears in a storable or escaping position.
714
    InvalidRefPosition,
715
    /// A reference local requires a fixed binding to existing storage.
716
    RefBinding,
717
    /// Call arguments contain overlapping incompatible loans.
718
    BorrowConflict(*[u8]),
719
    /// Unsafe operation outside an unsafe context.
720
    UnsafeOperation,
721
    /// An unsafe call requires an unsafe context.
722
    UnsafeCall,
723
    /// Internal error.
724
    Internal,
725
}
726
727
/// Diagnostics returned by the analyzer.
728
export record Diagnostics: Copy {
729
    /// Immutable errors captured at the end of an analysis operation.
730
    errors: *[Error],
731
}
732
733
/// Mutable diagnostic storage owned by a resolver.
734
record DiagnosticBuffer {
735
    /// Backing entries. Only the prefix below `len` is initialized.
736
    entries: *mut [Error],
737
    /// Number of recorded errors.
738
    len: u32,
739
}
740
741
/// Call context.
742
union CallCtx: Copy {
743
    /// Normal function call.
744
    Normal,
745
    /// Fallible function call, ie. `try f()`.
746
    Try,
747
}
748
749
/// Result of resolving a record literal's type name.
750
record ResolvedRecordLitType: Copy {
751
    /// The record nominal type to use for field checking.
752
    recordType: *unsafe NominalType,
753
    /// The result type of the literal (record type or union type for variants).
754
    resultType: Type,
755
}
756
757
/// Result of checking for a `super` path prefix.
758
record SuperAccessResult: Copy {
759
    scope: *unsafe mut Scope,
760
    child: *ast::Node,
761
}
762
763
/// Initialization operation performed after session storage reservation.
764
export union SessionAllocationKind: Copy {
765
    /// Initialize one object from a value.
766
    New,
767
    /// Copy plain Copy elements from a slice.
768
    Copy,
769
    /// Fill a slice with a plain Copy value.
770
    Fill,
771
}
772
773
/// Typed session allocation and its checked runtime reservation function.
774
export record SessionAllocation: Copy {
775
    /// Initialization operation.
776
    kind: SessionAllocationKind,
777
    /// Initialized element type.
778
    item: *Type,
779
    /// Allocation trait used by the session source.
780
    traitInfo: *unsafe TraitType,
781
    /// Reservation method slot in the allocation trait.
782
    methodIndex: u32,
783
}
784
785
/// Node-specific resolver metadata.
786
export union NodeExtra: Copy {
787
    /// No extra data for this node.
788
    None,
789
    /// Region identities owned by a source declaration.
790
    Regions(*RegionScope),
791
    /// Resolved field index for record literal fields.
792
    RecordField { index: u32 },
793
    /// Slice range metadata for subscript expressions with ranges.
794
    SliceRange(SliceRangeInfo),
795
    /// Cached union variant metadata for patterns/constructors.
796
    UnionVariant { ordinal: u32, tag: u32 },
797
    /// Match prong metadata.
798
    MatchProng { catchAll: bool },
799
    /// Match expression metadata.
800
    Match { isConst: bool },
801
    /// For-loop iteration metadata.
802
    ForLoop(ForLoopInfo),
803
    /// Trait method call metadata.
804
    TraitMethodCall {
805
        /// Trait definition.
806
        traitInfo: *unsafe TraitType,
807
        /// Method index in the v-table.
808
        methodIndex: u32,
809
    },
810
    /// Standalone method call metadata.
811
    MethodCall { method: *unsafe MethodEntry },
812
    /// Typed allocation through a session interface.
813
    SessionAllocation(SessionAllocation),
814
    /// Slice `.append(val, allocator)` method call.
815
    SliceAppend { elemType: *Type },
816
    /// Slice `.delete(index)` method call.
817
    SliceDelete { elemType: *Type },
818
}
819
820
/// Symbol identity and storage associated with a resolved AST node.
821
export record ResolvedSymbol: Copy {
822
    /// Identity within the resolver that owns the node metadata.
823
    id: u32,
824
    /// Symbol storage used by type resolution and lowering.
825
    symbol: *unsafe mut Symbol,
826
}
827
828
/// Combined resolver metadata for a single AST node.
829
export record NodeData: Copy {
830
    /// Number of local bindings and internal iteration variables in this function.
831
    localCount: u32,
832
    /// Resolved type for this node.
833
    ty: Type,
834
    /// Coercion plan applied to this node.
835
    coercion: Coercion,
836
    /// Symbol identity and storage associated with this node.
837
    binding: ?ResolvedSymbol,
838
    /// Constant value for literal nodes.
839
    constValue: ?ConstValue,
840
    /// Lexical scope owned by this node.
841
    scope: ?*unsafe mut Scope,
842
    /// Node-specific extra data.
843
    extra: NodeExtra,
844
}
845
846
/// Table storing all resolver metadata indexed by node ID.
847
record NodeDataTable {
848
    /// Semantic data indexed by AST node ID.
849
    entries: *mut [NodeData],
850
}
851
852
/// Lexical scope.
853
export record Scope: Copy {
854
    /// Owning AST node, or `nil` for the root scope.
855
    owner: ?*ast::Node,
856
    /// Parent/enclosing scope.
857
    parent: ?*unsafe mut Scope,
858
    /// Module ID if this is a module scope.
859
    moduleId: ?u16,
860
    /// Symbols introduced inside the scope, allocated from the arena.
861
    symbols: *unsafe mut [*unsafe mut Symbol],
862
    /// Number of live symbols.
863
    symbolsLen: u32,
864
}
865
866
/// An object used by the enter and exit functions for module scopes.
867
record ModuleScope: Copy {
868
    /// Module root node.
869
    root: *ast::Node,
870
    /// Module entry in graph.
871
    entry: *module::ModuleEntry,
872
    /// The newly entered scope.
873
    newScope: *unsafe mut Scope,
874
    /// The previous scope.
875
    prevScope: *unsafe mut Scope,
876
    /// The previous module.
877
    prevMod: u16,
878
}
879
880
/// Loop context for tracking control flow within loops.
881
record LoopCtx: Copy {
882
    /// Whether a reachable break was encountered in this loop.
883
    /// This is used to determine whether a loop diverges.
884
    hasBreak: bool,
885
}
886
887
/// Configuration for semantic analysis.
888
export record Config: Copy {
889
    /// Whether we're building in test mode.
890
    buildTest: bool,
891
}
892
893
/// How pattern bindings are created during match.
894
export union MatchBy: Copy {
895
    /// Match by value.
896
    Value,
897
    /// Match by immutable reference.
898
    Ref(types::PointerClass),
899
    /// Match by mutable reference.
900
    MutRef,
901
}
902
903
/// State of a match statement being resolved.
904
// TODO: This is only used because of the maximum function param limitation.
905
record MatchState: Copy {
906
    /// Is the match catch-all?
907
    catchAll: bool,
908
    /// Is the match constant?
909
    isConst: bool
910
}
911
912
/// Result of unwrapping a type for pattern matching.
913
export record MatchSubject: Copy {
914
    /// The effective type to match against.
915
    effectiveTy: Type,
916
    /// How bindings should be created.
917
    by: MatchBy,
918
}
919
920
/// How an expression uses a linear result.
921
union LinearUse: Copy {
922
    /// Consume the value and end its availability.
923
    Consume,
924
    /// Read the value without consuming it.
925
    Observe,
926
    /// Borrow the value through a reference.
927
    Borrow,
928
    /// Discard an unused expression result.
929
    Discard,
930
    /// Use the value as an assignment target.
931
    Place,
932
    /// Evaluate a place prefix after checking the complete place.
933
    Locate,
934
}
935
936
/// Region role sought during compile-time type traversal.
937
union RegionTypeRole: Copy {
938
    /// Compile-time identity associated with a cell payload.
939
    CellPermission,
940
    /// Storage reference that can carry a regional borrow.
941
    Reference,
942
    /// Interior storage that can retain a reference after a call returns.
943
    Retention,
944
    /// Region dependencies that must cover a destination.
945
    StorageValidation,
946
    /// Named storage retained by a value.
947
    StoragePresence,
948
}
949
950
/// Consumption rule for a tracked move-only binding.
951
union BindingUse: Copy {
952
    /// The binding can be consumed at most once.
953
    Affine,
954
    /// The binding must be consumed exactly once.
955
    Linear,
956
}
957
958
/// Resolved binding metadata retained for ownership checks and diagnostics.
959
record TrackedSymbol: Copy {
960
    /// Resolver-local symbol identity.
961
    id: u32,
962
    /// Source name used in ownership diagnostics.
963
    name: *[u8],
964
    /// Declaration used to locate an unconsumed binding.
965
    node: *ast::Node,
966
    /// Consumption rule fixed before ownership analysis.
967
    usage: BindingUse,
968
}
969
970
/// Per-control-flow-path ownership state.
971
/// Active binding slots below `len` must contain metadata.
972
record LinearEnv: Copy {
973
    /// Active full-region loans, indexed by the checker's regional loan table.
974
    regionalLoans: u64,
975
    /// Initialized slots for resolved binding metadata.
976
    symbols: [?TrackedSymbol; MAX_LINEAR_BINDINGS],
977
    /// Bit set for each binding that remains available.
978
    available: u64,
979
    /// Number of active binding slots in `symbols`.
980
    len: u32,
981
    /// Whether this control-flow path has terminated.
982
    terminated: bool,
983
}
984
985
/// A storage root and its statically distinct record fields.
986
record BorrowPlace: Copy {
987
    /// Symbol that owns or supplies the storage.
988
    root: ?*unsafe mut Symbol,
989
    /// Field indices before the first uncertain projection.
990
    fields: [u32; MAX_BORROW_FIELDS],
991
    /// Number of initialized field indices.
992
    len: u32,
993
    /// Whether further projections can identify distinct storage.
994
    precise: bool,
995
}
996
/// A reference binding that protects its source for one lexical scope.
997
record LocalLoan: Copy {
998
    /// Local symbol that provides access, or nil for a pending call argument.
999
    binding: ?*unsafe mut Symbol,
1000
    /// Storage retained by the reference.
1001
    place: BorrowPlace,
1002
    /// Whether other reads of the source are excluded.
1003
    exclusive: bool,
1004
    /// Permission identity protected independently of the source place.
1005
    permission: ?*unsafe types::Region,
1006
    /// Named region that owns associated cell storage, when one exists.
1007
    storage: ?*unsafe types::Region,
1008
}
1009
1010
/// Argument metadata retained during call-scoped conflict checks.
1011
record CallArgument: Copy {
1012
    /// Receiver or explicit argument expression.
1013
    node: *ast::Node,
1014
    /// Whether overlapping argument access is excluded.
1015
    exclusive: bool,
1016
}
1017
/// Function-local exact-use checker state.
1018
/// Read loop arrays only at indices below `loopDepth`.
1019
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
1020
record LinearChecker: 'arena + 'checking where 'arena: 'checking {
1021
    /// Resolver that owns the symbols and diagnostics.
1022
    resolver: &'checking mut Resolver 'arena,
1023
    /// Regional projections discovered in this function.
1024
    regional: [?RegionalLoan; MAX_REGIONAL_LOANS],
1025
    /// Number of active regional loan entries.
1026
    regionalLen: u32,
1027
    /// Named regions active at the current source location.
1028
    regions: ?*RegionScope,
1029
    /// Regional loans carried to each loop's next iteration.
1030
    loopBackLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
1031
    /// Regional loans carried to each loop's exits.
1032
    loopExitLoans: [u64; MAX_LINEAR_LOOP_DEPTH],
1033
    /// Regions active at each loop's entry and exit.
1034
    loopRegions: [?*RegionScope; MAX_LINEAR_LOOP_DEPTH],
1035
    /// Source places protected by active pattern references.
1036
    loans: [BorrowPlace; MAX_LINEAR_BINDINGS],
1037
    /// Number of active entries in `loans`.
1038
    loanLen: u32,
1039
    /// Reference locals in active lexical scopes.
1040
    locals: [LocalLoan; MAX_LINEAR_BINDINGS],
1041
    /// Number of active local loans.
1042
    localLen: u32,
1043
    /// Permission identities controlled by active lexical authority bindings.
1044
    authorityPermissions: [?*unsafe types::Region; MAX_LINEAR_BINDINGS],
1045
    /// Bindings that provide each lexical permission authority.
1046
    authorityBindings: [?*unsafe mut Symbol; MAX_LINEAR_BINDINGS],
1047
    /// Whether each lexical authority permits exclusive payload access.
1048
    authorityExclusive: [bool; MAX_LINEAR_BINDINGS],
1049
    /// Number of initialized authority entries.
1050
    authorityLen: u32,
1051
    /// Associated payload address currently validated by a region header.
1052
    payloadAddress: ?*ast::Node,
1053
    /// Matching authority reborrow currently being checked.
1054
    witnessPermission: ?*unsafe types::Region,
1055
    /// Binding count at entry to each active loop.
1056
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
1057
    /// Available bindings at entry to each active loop.
1058
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
1059
    /// Available bindings shared by the exits from each active loop.
1060
    loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
1061
    /// Whether each active loop can exit without `break`.
1062
    loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH],
1063
    /// Whether each active loop contains a reachable `break`.
1064
    loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH],
1065
    /// Number of active loops.
1066
    loopDepth: u32,
1067
}
1068
1069
/// Unwrap a pointer type for pattern matching.
1070
export fn unwrapMatchSubject(ty: Type) -> MatchSubject {
1071
    if let case Type::Pointer { class, target, mutable } = ty {
1072
        let mut bindingClass = types::PointerClass::Ref;
1073
        if let case types::PointerClass::Region(_) = class {
1074
            set bindingClass = class;
1075
        }
1076
        let by = MatchBy::MutRef if mutable else MatchBy::Ref(bindingClass);
1077
        return MatchSubject { effectiveTy: *target, by };
1078
    }
1079
    return MatchSubject { effectiveTy: ty, by: MatchBy::Value };
1080
}
1081
1082
/// Source nodes that define the identities in one region environment.
1083
export union RegionDeclarations: Copy {
1084
    /// Declaration parameters, including any non-region constraints.
1085
    Parameters(*[*ast::Node]),
1086
    /// Single region introduced by a lexical block.
1087
    Block(*ast::Node),
1088
}
1089
1090
/// Region names introduced by a declaration or lexical block.
1091
export record RegionScope: Copy {
1092
    /// Immutable source declarations that supply region identities.
1093
    declarations: RegionDeclarations,
1094
    /// Entries in declaration order.
1095
    entries: *unsafe [*unsafe mut types::Region],
1096
    /// Enclosing lexical region environment.
1097
    parent: ?*RegionScope,
1098
}
1099
1100
/// Region arguments for one source declaration.
1101
record RegionSubstitution: Copy {
1102
    /// Declared parameters in source order.
1103
    parameters: *RegionScope,
1104
    /// Inferred or explicit arguments. Every entry must be set before substitution.
1105
    arguments: *unsafe mut [?*unsafe types::Region],
1106
}
1107
1108
/// One interned application of a nominal declaration to exact region arguments.
1109
export record NominalApplication: Copy {
1110
    /// Canonical source declaration identity.
1111
    base: *unsafe NominalType,
1112
    /// Source parameters in declaration order.
1113
    parameters: *RegionScope,
1114
    /// Region arguments in parameter order.
1115
    arguments: *unsafe [*unsafe types::Region],
1116
    /// Stable descriptor for the substituted field or variant view.
1117
    view: *unsafe mut NominalType,
1118
    /// Resolver-private generation of the most recent compile-time traversal.
1119
    traversalGeneration: u32,
1120
    /// Resolver-private region roles visited during that traversal.
1121
    traversalRoles: u8,
1122
    /// Next application in the resolver cache.
1123
    next: ?*unsafe mut NominalApplication,
1124
    /// Next application in the same lookup bucket.
1125
    bucketNext: ?*unsafe mut NominalApplication,
1126
}
1127
1128
/// Global resolver state.
1129
export record Resolver: 'arena {
1130
    /// Number of symbol identities allocated by this resolver.
1131
    symbolCount: u32,
1132
    /// Active region names for source type checking.
1133
    regionScope: ?*RegionScope,
1134
    /// Interned applications of nominal region parameters.
1135
    applications: ?*unsafe mut NominalApplication,
1136
    /// First entry in the fully resolved suffix of the application list.
1137
    completedApplications: ?*unsafe mut NominalApplication,
1138
    /// Exact application lookup chains, backed by the resolver arena.
1139
    applicationBuckets: *unsafe mut [?*unsafe mut NominalApplication],
1140
    /// Monotonic identity for cycle-safe compile-time type traversals.
1141
    nominalTraversalGeneration: u32,
1142
    /// Cell payload checks that require complete nominal layouts.
1143
    cellChecks: *mut [CellCheck],
1144
    /// Current scope.
1145
    scope: *unsafe mut Scope,
1146
    /// Package scope containing package roots and top-level symbols.
1147
    pkgScope: *unsafe mut Scope,
1148
    /// Stack of loop contexts for nested loops.
1149
    loopStack: [LoopCtx; MAX_LOOP_DEPTH],
1150
    /// Current loop depth, indexes into loop stack.
1151
    loopDepth: u32,
1152
    /// Signature of the function currently being analyzed.
1153
    currentFn: ?FnType,
1154
    /// Declaration that owns the active function body and its local bindings.
1155
    currentFnNode: ?*ast::Node,
1156
    /// Current module being analyzed.
1157
    currentMod: u16,
1158
    /// Whether the current lexical context permits unsafe operations.
1159
    inUnsafeContext: bool,
1160
    /// Configuration for semantic analysis.
1161
    config: Config,
1162
    /// Caller-owned arena, valid for this resolver and all emitted metadata.
1163
    arena: &'arena mut alloc::Arena,
1164
    /// Combined semantic metadata table indexed by node ID.
1165
    nodeData: NodeDataTable,
1166
    /// Lookup chains for interned types.
1167
    types: *unsafe mut [?*TypeNode],
1168
    /// Diagnostics recorded so far.
1169
    errors: DiagnosticBuffer,
1170
    /// Stable module identities indexed by module ID.
1171
    moduleEntries: [?*module::ModuleEntry; module::MAX_MODULES],
1172
    /// Parsed roots captured while module update authority is available.
1173
    moduleRoots: [?*ast::Node; module::MAX_MODULES],
1174
    /// Cache of module scopes indexed by module ID.
1175
    moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES],
1176
    /// Trait instance registry.
1177
    instances: [InstanceEntry; MAX_INSTANCES],
1178
    /// Number of registered instances.
1179
    instancesLen: u32,
1180
    /// Standalone method registry.
1181
    methods: [MethodEntry; MAX_METHODS],
1182
    /// Number of registered standalone methods.
1183
    methodsLen: u32,
1184
}
1185
1186
/// Deferred cell payload validation that requires a complete nominal layout.
1187
record CellCheck: Copy {
1188
    /// Source signature used for diagnostics.
1189
    node: *ast::Node,
1190
    /// Payload whose layout and ownership must be checked.
1191
    payload: Type,
1192
    /// Permission association that makes affine payloads eligible.
1193
    permission: ?*unsafe types::Region,
1194
    /// Region environment at the source signature.
1195
    regions: ?*RegionScope,
1196
    /// Module that owns the source signature.
1197
    moduleId: u16,
1198
}
1199
1200
/// Internal error sentinel thrown when analysis cannot proceed.
1201
export union ResolveError: Copy {
1202
    Failure,
1203
}
1204
1205
/// Node in a type interning lookup chain.
1206
record TypeNode: Copy {
1207
    /// Exact interned type value.
1208
    ty: Type,
1209
    /// Next type in the same lookup bucket.
1210
    next: ?*TypeNode,
1211
}
1212
1213
/// Look up a region name in a lexical environment.
1214
unsafe fn findRegion(scope: ?*RegionScope, name: *[u8]) -> ?*unsafe mut types::Region {
1215
    let mut current = scope;
1216
    while let env = current {
1217
        for region in env.entries {
1218
            if mem::eq(region.name, name) {
1219
                return region;
1220
            }
1221
        }
1222
        set current = env.parent;
1223
    }
1224
    return nil;
1225
}
1226
1227
/// Resolve a source region name without using its spelling as an identity.
1228
unsafe fn resolveRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe types::Region
1229
    throws (ResolveError)
1230
{
1231
    let case ast::NodeValue::Region { name, .. } = node.value
1232
        else panic "resolveRegion: invalid region node";
1233
    let region = findRegion(self.regionScope, name)
1234
        else throw emitError(self, node, ErrorKind::UnknownRegion(name));
1235
    return region;
1236
}
1237
1238
/// Bind all region parameters before resolving their parent relations.
1239
unsafe fn bindRegions 'arena (self: &mut Resolver 'arena, owner: *ast::Node, nodes: *[*ast::Node]) -> ?*RegionScope
1240
    throws (ResolveError)
1241
{
1242
    if let case NodeExtra::Regions(scope) = self.nodeData.entries[owner.id].extra {
1243
        return scope;
1244
    }
1245
    let mut count: u32 = 0;
1246
    for node in nodes {
1247
        if let case ast::NodeValue::Region { .. } = node.value {
1248
            set count += 1;
1249
        }
1250
    }
1251
    if count == 0 {
1252
        return nil;
1253
    }
1254
    let entries = try! alloc::allocRawSlice(
1255
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), count
1256
    ) as *unsafe mut [*unsafe mut types::Region];
1257
    let mut index: u32 = 0;
1258
    for node in nodes {
1259
        let case ast::NodeValue::Region { name, .. } = node.value else continue;
1260
        for i in 0..index {
1261
            if mem::eq(entries[i].name, name) {
1262
                throw emitError(self, node, ErrorKind::DuplicateBinding(name));
1263
            }
1264
        }
1265
        let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
1266
            as *unsafe mut types::Region;
1267
        set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil };
1268
        set entries[index] = region;
1269
        set index += 1;
1270
    }
1271
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
1272
    set *scope = RegionScope {
1273
        declarations: RegionDeclarations::Parameters(nodes),
1274
        entries,
1275
        parent: nil,
1276
    };
1277
    let frozen: *RegionScope = scope;
1278
    set index = 0;
1279
    for node in nodes {
1280
        let case ast::NodeValue::Region { parent, .. } = node.value else continue;
1281
        if let parentNode = parent {
1282
            let case ast::NodeValue::Region { name, .. } = parentNode.value
1283
                else panic "bindRegions: invalid parent node";
1284
            let target = findRegion(frozen, name)
1285
                else throw emitError(self, parentNode, ErrorKind::UnknownRegion(name));
1286
            if types::regionContains(entries[index], target) {
1287
                throw emitError(self, parentNode, ErrorKind::RegionCycle(entries[index].name));
1288
            }
1289
            set entries[index].parent = target;
1290
        }
1291
        set index += 1;
1292
    }
1293
    set self.nodeData.entries[owner.id].extra = NodeExtra::Regions(frozen);
1294
    return frozen;
1295
}
1296
1297
/// Mix aligned metadata addresses into a lookup key.
1298
fn addressHash(address: u64) -> u32 {
1299
    return ((address >> 3) as u32) ^ ((address >> 35) as u32);
1300
}
1301
1302
/// Hash the active fields of a pointer class.
1303
unsafe fn classHash(class: types::PointerClass) -> u32 {
1304
    match class {
1305
        case types::PointerClass::Owned => return 1,
1306
        case types::PointerClass::Ref => return 2,
1307
        case types::PointerClass::Unsafe => return 3,
1308
        case types::PointerClass::Region(region) => return addressHash(region as u64),
1309
    }
1310
}
1311
1312
/// Hash active type fields so equal values select the same lookup chain.
1313
unsafe fn typeHash(ty: Type) -> u32 {
1314
    match ty {
1315
        case Type::Cell { class, permission, payload } => {
1316
            let mut hash = addressHash(payload as u64) ^ (classHash(class) * CACHE_HASH_PRIME);
1317
            if let region = permission {
1318
                set hash = (hash ^ addressHash(region as u64)) * CACHE_HASH_PRIME;
1319
            }
1320
            return hash;
1321
        }
1322
        case Type::Pointer { class, target, mutable } =>
1323
            return addressHash(target as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1324
        case Type::Slice { class, item, mutable } =>
1325
            return addressHash(item as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1326
        case Type::TraitObject { class, traitInfo, mutable } =>
1327
            return addressHash(traitInfo as u64) ^ (classHash(class) * CACHE_HASH_PRIME) ^ (1 if mutable else 0),
1328
        case Type::Session(region) => return addressHash(region as u64),
1329
        case Type::Optional(inner) => return addressHash(inner as u64),
1330
        case Type::Fn(info) => return addressHash(info as u64),
1331
        case Type::Nominal(info) => return addressHash(info as u64),
1332
        case Type::Array(array) => return addressHash(array.item as u64) ^ (array.length * CACHE_HASH_PRIME),
1333
        case Type::Range { start, end } => {
1334
            let mut hash: u32 = 0;
1335
            if let item = start {
1336
                set hash = addressHash(item as u64);
1337
            }
1338
            if let item = end {
1339
                set hash = hash ^ (addressHash(item as u64) * CACHE_HASH_PRIME);
1340
            }
1341
            return hash;
1342
        }
1343
        else => return 0,
1344
    }
1345
}
1346
1347
/// Allocate and intern a type in the arena, returning a pointer for deduplication.
1348
export unsafe fn allocType 'arena (self: &mut Resolver 'arena, ty: Type) -> *Type {
1349
    // Search existing types for a match.
1350
    let bucket = typeHash(ty) & (TYPE_BUCKETS - 1);
1351
    let mut cursor = self.types[bucket];
1352
    while let node = cursor {
1353
        if node.ty == ty {
1354
            return &node.ty;
1355
        }
1356
        set cursor = node.next;
1357
    }
1358
    // Allocate a new type node from the arena.
1359
    let node = try! alloc::alloc(
1360
        &mut *self.arena, @sizeOf(TypeNode), @alignOf(TypeNode)
1361
    ) as *mut TypeNode;
1362
1363
    set *node = TypeNode { ty, next: self.types[bucket] };
1364
    let frozen: *TypeNode = node;
1365
    set self.types[bucket] = frozen;
1366
1367
    return &frozen.ty;
1368
}
1369
1370
/// Allocate a nominal type descriptor and return a pointer to it.
1371
unsafe fn allocNominalType 'arena (self: &mut Resolver 'arena, info: NominalType) -> *unsafe mut NominalType {
1372
    // Nb. We don't attempt to de-duplicate nominal type entries,
1373
    // since they don't carry node information and we create
1374
    // placeholder entries when binding symbols.
1375
    let entry = try! alloc::allocRaw(
1376
        self.arena, @sizeOf(NominalType), @alignOf(NominalType)
1377
    ) as *unsafe mut NominalType;
1378
1379
    set *entry = info;
1380
1381
    return entry;
1382
}
1383
1384
/// Allocate the single runtime layout for a nominal declaration.
1385
unsafe fn allocLayout 'arena (self: &mut Resolver 'arena, value: Layout) -> *Layout {
1386
    let layout = try! alloc::alloc(&mut *self.arena, @sizeOf(Layout), @alignOf(Layout)) as *mut Layout;
1387
    set *layout = value;
1388
    return layout;
1389
}
1390
1391
/// Get the exact arguments of an applied nominal descriptor.
1392
export fn nominalApplication(info: &NominalType) -> ?*unsafe mut NominalApplication {
1393
    match *info {
1394
        case NominalType::Application(applied) => return applied,
1395
        case NominalType::Record(body) => return body.application,
1396
        case NominalType::Union(body) => return body.application,
1397
        else => return nil,
1398
    }
1399
}
1400
1401
/// Return the private visit bit for one compile-time region role.
1402
fn regionTypeRoleBit(role: RegionTypeRole) -> u8 {
1403
    match role {
1404
        case RegionTypeRole::CellPermission => return 1,
1405
        case RegionTypeRole::Reference => return 2,
1406
        case RegionTypeRole::Retention => return 4,
1407
        case RegionTypeRole::StorageValidation => return 8,
1408
        case RegionTypeRole::StoragePresence => return 16,
1409
    }
1410
}
1411
1412
/// Start a cycle-safe nominal traversal without allocating per-call scratch.
1413
unsafe fn nextNominalTraversalGeneration 'arena (self: &mut Resolver 'arena) -> u32 {
1414
    if self.nominalTraversalGeneration == parser::U32_MAX {
1415
        let mut cursor = self.applications;
1416
        while let applied = cursor {
1417
            set applied.traversalGeneration = 0;
1418
            set applied.traversalRoles = 0;
1419
            set cursor = applied.next;
1420
        }
1421
        set self.nominalTraversalGeneration = 0;
1422
    }
1423
    set self.nominalTraversalGeneration += 1;
1424
    return self.nominalTraversalGeneration;
1425
}
1426
1427
/// Mark an applied nominal visited once for a role in one traversal generation.
1428
unsafe fn visitNominalApplication(
1429
    applied: *unsafe mut NominalApplication,
1430
    generation: u32,
1431
    role: RegionTypeRole,
1432
) -> bool {
1433
    if applied.traversalGeneration <> generation {
1434
        set applied.traversalGeneration = generation;
1435
        set applied.traversalRoles = 0;
1436
    }
1437
    let bit = regionTypeRoleBit(role);
1438
    if (applied.traversalRoles & bit) <> 0 {
1439
        return false;
1440
    }
1441
    set applied.traversalRoles |= bit;
1442
    return true;
1443
}
1444
1445
/// Get source region parameters without forcing a recursive type's layout.
1446
unsafe fn nominalParameters 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType) -> ?*RegionScope
1447
    throws (ResolveError)
1448
{
1449
    match *info {
1450
        case NominalType::Placeholder(node) => return try declarationRegions(self, node),
1451
        case NominalType::Resolving(node) => return try declarationRegions(self, node),
1452
        case NominalType::Application(applied) => return applied.parameters,
1453
        case NominalType::Record(body) => return body.regions,
1454
        case NominalType::Union(body) => return body.regions,
1455
    }
1456
}
1457
1458
/// Bind the regions declared by a nominal source node.
1459
unsafe fn declarationRegions 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*RegionScope
1460
    throws (ResolveError)
1461
{
1462
    match node.value {
1463
        case ast::NodeValue::RecordDecl(decl) => return try bindRegions(self, node, decl.regions),
1464
        case ast::NodeValue::UnionDecl(decl) => return try bindRegions(self, node, decl.regions),
1465
        else => panic "declarationRegions: expected nominal declaration",
1466
    }
1467
}
1468
1469
/// Use a hinted application only for the same unapplied nominal declaration.
1470
unsafe fn hintedNominal(info: *unsafe NominalType, hint: Type) -> *unsafe NominalType {
1471
    if nominalApplication(info) <> nil {
1472
        return info;
1473
    }
1474
    let mut target = hint;
1475
    if let case Type::Optional(inner) = target {
1476
        set target = *inner;
1477
    }
1478
    if let case Type::Nominal(other) = target {
1479
        if let applied = nominalApplication(other); applied.base == info {
1480
            return other;
1481
        }
1482
    }
1483
    return info;
1484
}
1485
1486
/// Require explicit arguments for a parameterized nominal type.
1487
unsafe fn requireNominalArguments 'arena (self: &mut Resolver 'arena, info: *unsafe NominalType, site: *ast::Node)
1488
    throws (ResolveError)
1489
{
1490
    if nominalApplication(info) <> nil {
1491
        return;
1492
    }
1493
    if let parameters = try nominalParameters(self, info) {
1494
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
1495
            expected: parameters.entries.len, actual: 0,
1496
        }));
1497
    }
1498
}
1499
1500
/// Intern an exact nominal application before resolving its recursive members.
1501
unsafe fn internNominalApplication 'arena (
1502
    self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution
1503
) -> *unsafe mut NominalType {
1504
    let mut hash = addressHash(base as u64);
1505
    for argument in map.arguments {
1506
        let region = argument else panic "internNominalApplication: incomplete map";
1507
        set hash = (hash ^ region.id) * CACHE_HASH_PRIME;
1508
    }
1509
    let bucket = hash & (APPLICATION_BUCKETS - 1);
1510
    let mut cursor = self.applicationBuckets[bucket];
1511
    while let applied = cursor {
1512
        if applied.base == base {
1513
            let mut same = true;
1514
            for argument, i in applied.arguments {
1515
                let other = map.arguments[i] else panic "internNominalApplication: missing argument";
1516
                if argument.id <> other.id {
1517
                    set same = false;
1518
                    break;
1519
                }
1520
            }
1521
            if same {
1522
                return applied.view;
1523
            }
1524
        }
1525
        set cursor = applied.bucketNext;
1526
    }
1527
    let arguments = try! alloc::allocRawSlice(
1528
        self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len
1529
    ) as *unsafe mut [*unsafe types::Region];
1530
    for argument, i in map.arguments {
1531
        let region = argument else panic "internNominalApplication: incomplete map";
1532
        set arguments[i] = region;
1533
    }
1534
    let entry = try! alloc::allocRaw(
1535
        self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication)
1536
    ) as *unsafe mut NominalApplication;
1537
    let view = allocNominalType(self, NominalType::Application(entry));
1538
    set *entry = NominalApplication {
1539
        base, parameters: map.parameters, arguments, view,
1540
        traversalGeneration: 0, traversalRoles: 0,
1541
        next: self.applications, bucketNext: self.applicationBuckets[bucket],
1542
    };
1543
    set self.applications = entry;
1544
    set self.applicationBuckets[bucket] = entry;
1545
    return view;
1546
}
1547
1548
/// Check explicit region arguments and intern the applied nominal type.
1549
unsafe fn applyNominalRegions 'arena (
1550
    self: &mut Resolver 'arena, base: *unsafe NominalType, regions: *[*ast::Node], site: *ast::Node
1551
) -> *unsafe mut NominalType throws (ResolveError) {
1552
    if nominalApplication(base) <> nil {
1553
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: 0, actual: regions.len }));
1554
    }
1555
    let parameters = try nominalParameters(self, base);
1556
    let mut count: u32 = 0;
1557
    if let scope = parameters {
1558
        set count = scope.entries.len;
1559
    }
1560
    if count <> regions.len {
1561
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
1562
    }
1563
    let scope = parameters else panic "applyNominalRegions: empty application";
1564
    let map = regionSubstitution(self, scope);
1565
    for region, i in regions {
1566
        set map.arguments[i] = try resolveRegion(self, region);
1567
    }
1568
    try validateRegionArguments(self, &map, nil, nil, site);
1569
    return internNominalApplication(self, base, &map);
1570
}
1571
1572
/// Complete nominal views stored inline within an applied type.
1573
/// Pointer, slice, and cell targets have independent storage layouts.
1574
unsafe fn resolveInlineTypeViews 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node)
1575
    throws (ResolveError)
1576
{
1577
    match ty {
1578
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1579
        case Type::Array(array) => try resolveInlineTypeViews(self, *array.item, site),
1580
        case Type::Optional(inner) => try resolveInlineTypeViews(self, *inner, site),
1581
        else => {
1582
        },
1583
    }
1584
}
1585
1586
/// Resolve a substituted member view with the source declaration's shared layout.
1587
unsafe fn resolveNominalApplication 'arena (self: &mut Resolver 'arena, applied: *unsafe mut NominalApplication, site: *ast::Node)
1588
    throws (ResolveError)
1589
{
1590
    try ensureNominalResolved(self, applied.base, site);
1591
    let map = regionSubstitution(self, applied.parameters);
1592
    for argument, i in applied.arguments {
1593
        set map.arguments[i] = argument;
1594
    }
1595
    let allocator = alloc::arenaAllocator(self.arena);
1596
    match *applied.base {
1597
        case NominalType::Record(body) => {
1598
            let mut fields: *mut [RecordField] = &mut [];
1599
            for field in body.fields {
1600
                let fieldType = substituteRegions(self, &map, field.fieldType);
1601
                try resolveInlineTypeViews(self, fieldType, site);
1602
                fields.append(RecordField {
1603
                    name: field.name,
1604
                    fieldType,
1605
                    offset: field.offset,
1606
                }, allocator);
1607
            }
1608
            set *applied.view = NominalType::Record(RecordType {
1609
                privateModule: body.privateModule,
1610
                regions: body.regions,
1611
                application: applied,
1612
                fields: (&fields[..]) as *unsafe [RecordField],
1613
                labeled: body.labeled,
1614
                layout: body.layout,
1615
                declaredLinear: body.declaredLinear,
1616
                declaredCopy: body.declaredCopy,
1617
            });
1618
        }
1619
        case NominalType::Union(body) => {
1620
            let mut variants: *mut [UnionVariant] = &mut [];
1621
            for variant in body.variants {
1622
                let valueType = substituteRegions(self, &map, variant.valueType);
1623
                try resolveInlineTypeViews(self, valueType, site);
1624
                variants.append(UnionVariant {
1625
                    name: variant.name,
1626
                    valueType,
1627
                    symbol: variant.symbol,
1628
                }, allocator);
1629
            }
1630
            set *applied.view = NominalType::Union(UnionType {
1631
                regions: body.regions,
1632
                application: applied,
1633
                variants: (&variants[..]) as *unsafe [UnionVariant],
1634
                layout: body.layout,
1635
                valOffset: body.valOffset,
1636
                isAllVoid: body.isAllVoid,
1637
                declaredLinear: body.declaredLinear,
1638
                declaredCopy: body.declaredCopy,
1639
            });
1640
        }
1641
        else => panic "resolveNominalApplication: unresolved base",
1642
    }
1643
}
1644
1645
/// Complete all applied member views before semantic metadata reaches lowering.
1646
unsafe fn resolveNominalApplications 'arena (self: &mut Resolver 'arena, site: *ast::Node) throws (ResolveError) {
1647
    let mut end = self.completedApplications;
1648
    loop {
1649
        let first = self.applications;
1650
        let mut cursor = first;
1651
        while cursor <> end {
1652
            let applied = cursor else panic "resolveNominalApplications: invalid frontier";
1653
            try ensureNominalResolved(self, applied.view, site);
1654
            set cursor = applied.next;
1655
        }
1656
        if self.applications == first {
1657
            set self.completedApplications = first;
1658
            break;
1659
        }
1660
        set end = first;
1661
    }
1662
    let previousRegions = self.regionScope;
1663
    let previousModule = self.currentMod;
1664
    let mut index: u32 = 0;
1665
    while index < self.cellChecks.len {
1666
        let check = self.cellChecks[index];
1667
        set self.regionScope = check.regions;
1668
        set self.currentMod = check.moduleId;
1669
        try validateCellPayload(self, check.node, check.payload, check.permission) catch error {
1670
            set self.regionScope = previousRegions;
1671
            set self.currentMod = previousModule;
1672
            throw error;
1673
        };
1674
        set index += 1;
1675
    }
1676
    set self.cellChecks.len = 0;
1677
    set self.regionScope = previousRegions;
1678
    set self.currentMod = previousModule;
1679
}
1680
1681
1682
/// Allocate a function type descriptor and return a pointer to it.
1683
unsafe fn allocFnType 'arena (self: &mut Resolver 'arena, info: FnType) -> *FnType {
1684
    let entry = try! alloc::alloc(
1685
        &mut *self.arena, @sizeOf(FnType), @alignOf(FnType)
1686
    ) as *mut FnType;
1687
1688
    set *entry = info;
1689
1690
    return entry;
1691
}
1692
1693
/// Returns an error, if any, associated with the given node.
1694
fn errorForNode 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Error {
1695
    for i in 0..self.errors.len {
1696
        let err = self.errors.entries[i];
1697
        if err.node == node {
1698
            return err;
1699
        }
1700
    }
1701
    return nil;
1702
}
1703
1704
/// Storage buffers used by the analyzer.
1705
export record ResolverStorage {
1706
    /// Node semantic metadata indexed by node ID.
1707
    nodeData: *mut [NodeData],
1708
    /// Package scope.
1709
    pkgScope: *unsafe mut Scope,
1710
    /// Error storage.
1711
    errors: *mut [Error],
1712
}
1713
1714
/// Input for resolving a single package.
1715
export record Pkg: Copy {
1716
    /// Root module entry.
1717
    rootEntry: *module::ModuleEntry,
1718
    /// Root AST node.
1719
    rootAst: *ast::Node,
1720
}
1721
1722
/// Construct a resolver with module context and backing storage.
1723
/// The arena owner and backing bytes must retain stable addresses during use.
1724
/// Arena reclamation can occur only after all metadata uses end.
1725
export unsafe fn resolver 'arena (
1726
    arena: &'arena mut alloc::Arena,
1727
    storage: ResolverStorage,
1728
    config: Config
1729
) -> Resolver 'arena {
1730
    let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
1731
    let applicationBuckets = try! alloc::allocRawSlice(
1732
        arena, @sizeOf(?*unsafe mut NominalApplication), @alignOf(?*unsafe mut NominalApplication), APPLICATION_BUCKETS
1733
    ) as *unsafe mut [?*unsafe mut NominalApplication];
1734
    for i in 0..applicationBuckets.len {
1735
        set applicationBuckets[i] = nil;
1736
    }
1737
    let types = try! alloc::allocRawSlice(
1738
        arena, @sizeOf(?*TypeNode), @alignOf(?*TypeNode), TYPE_BUCKETS
1739
    ) as *unsafe mut [?*TypeNode];
1740
    for i in 0..types.len {
1741
        set types[i] = nil;
1742
    }
1743
    let symbols = try! alloc::allocRawSlice(
1744
        arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), MAX_MODULE_SYMBOLS
1745
    ) as *unsafe mut [*unsafe mut Symbol];
1746
1747
    // Initialize the root scope.
1748
    // TODO: Set this up when declaring `PKG_SCOPE`, not here.
1749
    set *pkgScope = Scope {
1750
        owner: nil,
1751
        parent: nil,
1752
        moduleId: nil,
1753
        symbols,
1754
        symbolsLen: 0,
1755
    };
1756
1757
    // Clear all node semantic metadata to sentinel values.
1758
    // TODO: Use array repeat literal?
1759
    for i in 0..nodeData.len {
1760
        set nodeData[i] = NodeData {
1761
            localCount: 0,
1762
            ty: Type::Unknown,
1763
            coercion: Coercion::Identity,
1764
            binding: nil,
1765
            constValue: nil,
1766
            scope: nil,
1767
            extra: NodeExtra::None,
1768
        };
1769
    }
1770
1771
    let mut moduleScopes: [?*unsafe mut Scope; module::MAX_MODULES] = undefined;
1772
    // TODO: Simplify.
1773
    for i in 0..moduleScopes.len {
1774
        set moduleScopes[i] = nil;
1775
    }
1776
    return Resolver 'arena {
1777
        symbolCount: 0,
1778
        regionScope: nil,
1779
        applications: nil,
1780
        completedApplications: nil,
1781
        applicationBuckets,
1782
        nominalTraversalGeneration: 0,
1783
        cellChecks: &mut [],
1784
        scope: pkgScope,
1785
        pkgScope: pkgScope,
1786
        loopStack: [LoopCtx { hasBreak: false }; MAX_LOOP_DEPTH],
1787
        loopDepth: 0,
1788
        currentFn: nil,
1789
        currentFnNode: nil,
1790
        currentMod: 0,
1791
        inUnsafeContext: false,
1792
        config,
1793
        arena,
1794
        nodeData: NodeDataTable { entries: nodeData },
1795
        types,
1796
        errors: DiagnosticBuffer { entries: errors, len: 0 },
1797
        moduleEntries: [nil; module::MAX_MODULES],
1798
        moduleRoots: [nil; module::MAX_MODULES],
1799
        moduleScopes,
1800
        instances: undefined,
1801
        instancesLen: 0,
1802
        methods: undefined,
1803
        methodsLen: 0,
1804
    };
1805
}
1806
1807
/// Capture the current errors in an immutable arena allocation.
1808
/// The allocation must remain valid while the diagnostics are used.
1809
export unsafe fn diagnostics 'arena (self: &mut Resolver 'arena) -> Diagnostics {
1810
    let count = self.errors.len;
1811
    let entries = try! alloc::allocSlice(
1812
        self.arena, @sizeOf(Error), @alignOf(Error), count
1813
    ) as *mut [Error];
1814
    for i in 0..self.errors.len {
1815
        set entries[i] = self.errors.entries[i];
1816
    }
1817
    return Diagnostics { errors: entries };
1818
}
1819
1820
/// Return `true` if there are no errors in the diagnostics.
1821
export fn success(diag: &Diagnostics) -> bool {
1822
    return diag.errors.len == 0;
1823
}
1824
1825
/// Retrieve an error diagnostic by index, if present.
1826
export fn errorAt(errs: &[Error], index: u32) -> ?Error {
1827
    if index >= errs.len {
1828
        return nil;
1829
    }
1830
    return errs[index];
1831
}
1832
1833
/// Record an error diagnostic and return an error sentinel suitable for throwing.
1834
fn emitError 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, kind: ErrorKind) -> ResolveError {
1835
    // If our error list is full, just return an error without recording it.
1836
    if self.errors.len >= self.errors.entries.len {
1837
        return ResolveError::Failure;
1838
    }
1839
    // Don't record more than one error per node.
1840
    if let n = node; errorForNode(self, n) <> nil {
1841
        return ResolveError::Failure;
1842
    }
1843
    let idx = self.errors.len;
1844
    set self.errors.entries[idx] = Error { kind, node, moduleId: self.currentMod };
1845
    set self.errors.len = idx + 1;
1846
1847
    return ResolveError::Failure;
1848
}
1849
1850
/// Like [`emitError`], but for type mismatches specifically.
1851
fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1852
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1853
}
1854
1855
/// Allocate a scope object with the given symbol capacity.
1856
unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1857
    // Check for an existing scope for this node, and don't allocate a new
1858
    // one in that case.
1859
    if let scope = scopeFor(self, owner) {
1860
        return scope;
1861
    }
1862
    assert owner.id < self.nodeData.entries.len, "allocScope: node ID out of bounds";
1863
    let p = try! alloc::allocRaw(self.arena, @sizeOf(Scope), @alignOf(Scope));
1864
    let entry = p as *unsafe mut Scope;
1865
1866
    // Allocate symbols from the arena.
1867
    let symbols = try! alloc::allocRawSlice(
1868
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), capacity
1869
    ) as *unsafe mut [*unsafe mut Symbol];
1870
1871
    set *entry = Scope { owner, parent: nil, moduleId: nil, symbols, symbolsLen: 0 };
1872
    set self.nodeData.entries[owner.id].scope = entry;
1873
1874
    return entry;
1875
}
1876
1877
/// Enter a new local scope that is the child of the current scope.
1878
/// This creates a parent/child relationship that means that lookups in the
1879
/// child scope can recurse upwards.
1880
export unsafe fn enterScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node) -> *unsafe Scope {
1881
    let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS);
1882
    set scope.parent = self.scope;
1883
    set self.scope = scope;
1884
    return scope;
1885
}
1886
1887
/// Enter a module scope. Returns an object that can be used to exit the scope.
1888
export unsafe fn enterModuleScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope {
1889
    let prevScope = self.scope;
1890
    let prevMod = self.currentMod;
1891
    let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS);
1892
1893
    set self.scope = scope;
1894
    set self.scope.moduleId = module.id;
1895
    set self.currentMod = module.id;
1896
    // TODO: Allow any unsigned integer to index an array.
1897
    set self.moduleScopes[module.id as u32] = scope;
1898
1899
    return ModuleScope { root: owner, entry: module, newScope: scope, prevScope, prevMod };
1900
}
1901
1902
/// Enter a sub-module. Changes the current scope into that of the sub-module.
1903
unsafe fn enterSubModule 'arena (self: &mut Resolver 'arena, name: *[u8], node: *ast::Node) -> ModuleScope throws (ResolveError) {
1904
    let modEntry = findChildModule(self, name, self.currentMod)
1905
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
1906
    let modRoot = self.moduleRoots[modEntry.id as u32]
1907
        else panic "enterSubModule: analyzing module that wasn't parsed";
1908
1909
    return enterModuleScope(self, modRoot, modEntry);
1910
}
1911
1912
/// Exit a module scope, given the object returned by `enterModuleScope`.
1913
export fn exitModuleScope 'arena (self: &mut Resolver 'arena, entry: ModuleScope) {
1914
    set self.scope = entry.prevScope;
1915
    set self.currentMod = entry.prevMod;
1916
}
1917
1918
/// Exit the most recent scope.
1919
export unsafe fn exitScope 'arena (self: &mut Resolver 'arena) {
1920
    let parent = self.scope.parent else {
1921
        // TODO: This should be a panic, but one of the tests hits this
1922
        // clause, which might be a bug in the generator.
1923
        return;
1924
    };
1925
    set self.scope = parent;
1926
}
1927
1928
/// Initialize a loop context before making it active.
1929
fn enterLoop 'arena (self: &mut Resolver 'arena) {
1930
    assert self.loopDepth < MAX_LOOP_DEPTH, "enterLoop: loop nesting depth exceeded";
1931
    set self.loopStack[self.loopDepth] = LoopCtx { hasBreak: false };
1932
    set self.loopDepth += 1;
1933
}
1934
1935
/// End the active loop context and return its control-flow type.
1936
fn exitLoop 'arena (self: &mut Resolver 'arena) -> Type {
1937
    assert self.loopDepth > 0, "exitLoop: loop depth underflow";
1938
    // Pop and check if break was encountered.
1939
    set self.loopDepth -= 1;
1940
    if self.loopStack[self.loopDepth].hasBreak {
1941
        return Type::Void;
1942
    }
1943
    return Type::Never;
1944
}
1945
1946
/// Visit the body of a loop while tracking nesting depth.
1947
unsafe fn visitLoop 'arena (self: &mut Resolver 'arena, body: *ast::Node) -> Type
1948
    throws (ResolveError)
1949
{
1950
    enterLoop(self);
1951
    try infer(self, body) catch {
1952
        exitLoop(self);
1953
        throw ResolveError::Failure;
1954
    };
1955
    return exitLoop(self);
1956
}
1957
1958
/// Require that loop control statements appear inside a loop.
1959
/// Record breaks and assign the control statement's diverging type.
1960
fn resolveLoopControl 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
1961
    if self.loopDepth == 0 {
1962
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1963
    }
1964
    match node.value {
1965
        case ast::NodeValue::Break => {
1966
            // Mark that the current loop has a reachable break.
1967
            set self.loopStack[self.loopDepth - 1].hasBreak = true;
1968
        }
1969
        case ast::NodeValue::Continue => {}
1970
        else => panic "resolveLoopControl: expected loop control statement",
1971
    }
1972
    return setNodeType(self, node, Type::Never);
1973
}
1974
1975
/// Bind a loop pattern to the provided type.
1976
unsafe fn bindForLoopPattern 'arena (self: &mut Resolver 'arena, pattern: *ast::Node, ty: Type, mutable: bool)
1977
    throws (ResolveError)
1978
{
1979
    match pattern.value {
1980
        case ast::NodeValue::Placeholder, ast::NodeValue::Ident(_) => {
1981
            let _ = try bindValueIdent(self, pattern, pattern, ty, mutable, 0, 0);
1982
        }
1983
        else => {
1984
            let actualTy = try checkAssignable(self, pattern, ty);
1985
            setNodeType(self, pattern, actualTy);
1986
        }
1987
    }
1988
}
1989
1990
/// Set the expected return type for a new function body.
1991
unsafe fn enterFn 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: &FnType) {
1992
    assert self.currentFn == nil, "enterFn: already in a function";
1993
    set self.currentFn = *ty;
1994
    set self.currentFnNode = node;
1995
    enterScope(self, node);
1996
}
1997
1998
/// Clear the expected return type when leaving a function body.
1999
unsafe fn exitFn 'arena (self: &mut Resolver 'arena) {
2000
    if self.currentFn == nil {
2001
        // TODO: This should be a panic, but one of the tests hits this
2002
        // clause, which might be a bug in the generator.
2003
        return;
2004
    }
2005
    set self.currentFn = nil;
2006
    set self.currentFnNode = nil;
2007
    exitScope(self);
2008
}
2009
2010
/// Extract the identifier text from a node.
2011
fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8]
2012
    throws (ResolveError)
2013
{
2014
    let case ast::NodeValue::Ident(name) = node.value
2015
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
2016
    return name;
2017
}
2018
2019
/// Associate a resolved symbol with an AST node.
2020
unsafe fn setNodeSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node, symbol: *unsafe mut Symbol) {
2021
    if let existingSym = self.nodeData.entries[node.id].binding {
2022
        panic "setNodeSymbol: a symbol is already associated with this node";
2023
    }
2024
    set self.nodeData.entries[node.id].binding = ResolvedSymbol { id: symbol.id, symbol };
2025
}
2026
2027
/// Associate a resolved type with an AST node and return it.
2028
fn setNodeType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type) -> Type {
2029
    if ty == Type::Unknown {
2030
        // In this case, we simply don't associate a type.
2031
        return ty;
2032
    }
2033
    set self.nodeData.entries[node.id].ty = ty;
2034
2035
    return ty;
2036
}
2037
2038
/// Unify the types of two branches for control flow. Returns `never` only if
2039
/// both branches diverge, otherwise returns `void`. If the else branch is
2040
/// absent, we assume it doesn't diverge.
2041
fn unifyBranches(left: Type, right: ?Type) -> Type {
2042
    if left == Type::Never {
2043
        if let ty = right; ty == Type::Never {
2044
            return Type::Never;
2045
        }
2046
    }
2047
    return Type::Void;
2048
}
2049
2050
/// Associate a coercion plan with an AST node.
2051
fn setNodeCoercion 'arena (self: &mut Resolver 'arena, node: *ast::Node, coercion: Coercion) -> Coercion {
2052
    if coercion == Coercion::Identity {
2053
        return coercion;
2054
    }
2055
    set self.nodeData.entries[node.id].coercion = coercion;
2056
2057
    return coercion;
2058
}
2059
2060
/// Associate a constant value with an AST node.
2061
fn setNodeConstValue 'arena (self: &mut Resolver 'arena, node: *ast::Node, value: ConstValue) {
2062
    set self.nodeData.entries[node.id].constValue = value;
2063
}
2064
2065
/// Associate a record field index with a record literal field node.
2066
fn setRecordFieldIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node, index: u32) {
2067
    set self.nodeData.entries[node.id].extra = NodeExtra::RecordField { index };
2068
}
2069
2070
/// Associate slice range metadata with a subscript expression.
2071
fn setSliceRangeInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: SliceRangeInfo) {
2072
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceRange(info);
2073
}
2074
2075
/// Associate union variant metadata with a pattern or constructor node.
2076
fn setVariantInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, ordinal: u32, tag: u32) {
2077
    set self.nodeData.entries[node.id].extra = NodeExtra::UnionVariant { ordinal, tag };
2078
}
2079
2080
/// Associate trait method call metadata with a call node.
2081
fn setTraitMethodCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, traitInfo: *unsafe TraitType, methodIndex: u32) {
2082
    set self.nodeData.entries[node.id].extra = NodeExtra::TraitMethodCall { traitInfo, methodIndex };
2083
}
2084
2085
/// Associate for-loop metadata with a for-loop node.
2086
fn setForLoopInfo 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: ForLoopInfo) {
2087
    set self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info);
2088
}
2089
2090
/// Retrieve the constant value associated with a node, if any.
2091
export fn constValueEntry 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ConstValue {
2092
    return self.nodeData.entries[node.id].constValue;
2093
}
2094
2095
/// Get the resolved record field index for a record literal field node.
2096
export fn recordFieldIndexFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?u32 {
2097
    if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra {
2098
        return index;
2099
    }
2100
    return nil;
2101
}
2102
2103
/// Get the range metadata for a slice borrow or range assignment.
2104
export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo {
2105
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
2106
        return info;
2107
    }
2108
    return nil;
2109
}
2110
2111
/// Get the for-loop metadata for a for-loop node.
2112
export fn forLoopInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?ForLoopInfo {
2113
    if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra {
2114
        return info;
2115
    }
2116
    return nil;
2117
}
2118
2119
/// Associate match prong metadata with a match prong node.
2120
fn setProngCatchAll 'arena (self: &mut Resolver 'arena, node: *ast::Node, catchAll: bool) {
2121
    set self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll };
2122
}
2123
2124
/// Check if a prong is catch-all.
2125
export fn isProngCatchAll 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
2126
    if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra {
2127
        return catchAll;
2128
    }
2129
    return false;
2130
}
2131
2132
/// Set match metadata.
2133
fn setMatchConst 'arena (self: &mut Resolver 'arena, node: *ast::Node, isConst: bool) {
2134
    set self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst };
2135
}
2136
2137
/// Check if a match has all constant patterns.
2138
export fn isMatchConst 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
2139
    if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra {
2140
        return isConst;
2141
    }
2142
    return false;
2143
}
2144
2145
/// Get the resolver metadata for a node.
2146
export fn nodeData 'arena (self: &Resolver 'arena, node: *ast::Node) -> NodeData {
2147
    return self.nodeData.entries[node.id];
2148
}
2149
2150
/// Get the type for a node, or `nil` if unknown.
2151
export fn typeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Type {
2152
    let ty = self.nodeData.entries[node.id].ty;
2153
    if ty == Type::Unknown {
2154
        return nil;
2155
    }
2156
    return ty;
2157
}
2158
2159
/// Get the scope associated with a node.
2160
export fn scopeFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope {
2161
    return self.nodeData.entries[node.id].scope;
2162
}
2163
2164
/// Get the symbol bound to a node.
2165
export fn symbolFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
2166
    let binding = self.nodeData.entries[node.id].binding else return nil;
2167
    return binding.symbol;
2168
}
2169
2170
/// Get the coercion plan associated with a node, if any.
2171
export fn coercionFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?Coercion {
2172
    let c = self.nodeData.entries[node.id].coercion;
2173
    if c == Coercion::Identity {
2174
        return nil;
2175
    }
2176
    return c;
2177
}
2178
2179
/// Get the module ID for a symbol by walking up its scope chain.
2180
export unsafe fn moduleIdForSymbol 'arena (self: &Resolver 'arena, sym: *unsafe Symbol) -> ?u16 {
2181
    // For module-level symbols, return the cached module ID.
2182
    if let id = sym.moduleId {
2183
        return id;
2184
    }
2185
    // For module symbols, return the module ID directly.
2186
    if let case SymbolData::Module { entry, .. } = sym.data {
2187
        return entry.id;
2188
    }
2189
    // If this node has its own scope (functions, types, etc.), walk up from there.
2190
    if let scope = self.nodeData.entries[sym.node.id].scope {
2191
        return findModuleForScope(scope);
2192
    }
2193
    return nil;
2194
}
2195
2196
/// Get the binding node for a variant pattern.
2197
/// Returns the argument node if this is a variant constructor with a non-placeholder binding.
2198
export unsafe fn variantPatternBinding 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> ?*ast::Node {
2199
    let case ast::NodeValue::Call(call) = pattern.value
2200
        else return nil;
2201
    let sym = symbolFor(self, call.callee)
2202
        else return nil;
2203
    let case SymbolData::Variant { .. } = sym.data
2204
        else return nil;
2205
2206
    if call.args.len == 0 {
2207
        return nil;
2208
    }
2209
    let arg = call.args[0];
2210
2211
    if let case ast::NodeValue::Placeholder = arg.value {
2212
        return nil;
2213
    }
2214
    return arg;
2215
}
2216
2217
/// Allocate a new symbol, and return a reference to it.
2218
unsafe fn allocSymbol 'arena (self: &mut Resolver 'arena, data: SymbolData, name: *[u8], node: *ast::Node, attrs: u32) -> *unsafe mut Symbol {
2219
    let sym = try! alloc::allocRaw(self.arena, @sizeOf(Symbol), @alignOf(Symbol)) as *unsafe mut Symbol;
2220
    assert self.symbolCount < parser::U32_MAX, "allocSymbol: symbol identity overflow";
2221
    let id = self.symbolCount;
2222
    set self.symbolCount += 1;
2223
    set *sym = Symbol { id, name, data, attrs, node, moduleId: nil };
2224
2225
    return sym;
2226
}
2227
2228
/// Check that a type is boolean, otherwise throw an error.
2229
unsafe fn checkBoolean 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2230
    return try checkEqual(self, node, Type::Bool);
2231
}
2232
2233
/// Check that a type is numeric, otherwise throw an error.
2234
unsafe fn checkNumeric 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
2235
    let ty = try infer(self, node);
2236
    if not isNumericType(ty) {
2237
        throw emitError(self, node, ErrorKind::ExpectedNumeric);
2238
    }
2239
    return ty;
2240
}
2241
2242
/// Check if a type is a numeric type.
2243
fn isNumericType(ty: Type) -> bool {
2244
    match ty {
2245
        case Type::U8, Type::U16, Type::U32, Type::U64,
2246
             Type::I8, Type::I16, Type::I32, Type::I64,
2247
             Type::Int => return true,
2248
        else => return false,
2249
    }
2250
}
2251
2252
/// Check if a type is an unsigned integer type.
2253
export fn isUnsignedIntegerType(ty: Type) -> bool {
2254
    match ty {
2255
        case Type::U8, Type::U16, Type::U32, Type::U64 => return true,
2256
        else => return false,
2257
    }
2258
}
2259
2260
/// Return the maximum of two u32 values.
2261
fn max(a: u32, b: u32) -> u32 {
2262
    if a > b {
2263
        return a;
2264
    }
2265
    return b;
2266
}
2267
2268
/// Get the layout of a type.
2269
export unsafe fn getTypeLayout(ty: Type) -> Layout {
2270
    return typeLayout(ty);
2271
}
2272
2273
/// Traverse owned type links and compute array, optional, or fixed layouts.
2274
fn typeLayout(ty: Type) -> Layout {
2275
    match ty {
2276
        case Type::Array(arr) => return getArrayLayout(typeLayout(*arr.item), arr.length),
2277
        case Type::Optional(inner) => {
2278
            // Nullable types use null pointer optimization -- no tag byte needed.
2279
            if isNullableType(*inner) {
2280
                return typeLayout(*inner);
2281
            }
2282
            return getOptionalAggregateLayout(typeLayout(*inner));
2283
        }
2284
        case Type::Nominal(info) => {
2285
            unsafe {
2286
                return getNominalLayout(*info);
2287
            }
2288
        },
2289
        else => return fixedTypeLayout(ty),
2290
    }
2291
}
2292
2293
/// Get a layout that does not depend on nested type or nominal metadata.
2294
fn fixedTypeLayout(ty: Type) -> Layout {
2295
    match ty {
2296
        case Type::Pointer { .. } => return Layout {
2297
            size: PTR_SIZE, alignment: PTR_SIZE
2298
        },
2299
        case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) =>
2300
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
2301
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
2302
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
2303
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
2304
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
2305
        case Type::Int => return Layout { size: 8, alignment: 8 },
2306
        case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 },
2307
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
2308
        case Type::Cell { .. } => return Layout {
2309
            size: PTR_SIZE, alignment: PTR_SIZE
2310
        },
2311
        else => {
2312
            panic "fixedTypeLayout: the given type has no fixed layout";
2313
        }
2314
    }
2315
}
2316
2317
/// Get the layout of a type or value.
2318
export unsafe fn getLayout 'arena (self: &Resolver 'arena, node: *ast::Node, ty: Type) -> Layout {
2319
    let mut layout = getTypeLayout(ty);
2320
    // Check for symbol-specific alignment override.
2321
    if let sym = symbolFor(self, node) {
2322
        if let case SymbolData::Value { alignment, .. } = sym.data {
2323
            if alignment > 0 {
2324
                set layout.alignment = alignment;
2325
            }
2326
        }
2327
    }
2328
    return layout;
2329
}
2330
2331
/// Get an array layout from its element layout and length.
2332
export fn getArrayLayout(item: Layout, length: u32) -> Layout {
2333
    return Layout {
2334
        size: item.size * length,
2335
        alignment: item.alignment,
2336
    };
2337
}
2338
2339
/// Get an optional aggregate layout from its payload layout.
2340
export fn getOptionalAggregateLayout(innerLayout: Layout) -> Layout {
2341
    let valOffset = getOptionalValOffset(innerLayout);
2342
    let alignment = max(innerLayout.alignment, 1);
2343
2344
    return Layout {
2345
        size: mem::alignUp(valOffset + innerLayout.size, alignment),
2346
        alignment,
2347
    };
2348
}
2349
2350
/// Get the payload offset within an optional aggregate.
2351
export fn getOptionalValOffset(inner: Layout) -> u32 {
2352
    return mem::alignUp(1, inner.alignment);
2353
}
2354
2355
/// Check if a type is optional.
2356
export fn isOptionalType(ty: Type) -> bool {
2357
    match ty {
2358
        case Type::Optional(_) => return true,
2359
        else => return false,
2360
    }
2361
}
2362
2363
/// Check if a type uses null pointer optimization.
2364
/// This applies to optional pointers `?*T` and optional slices `?*[T]`,
2365
/// where `nil` is represented as a null data pointer with no tag byte.
2366
export fn isOptionalPointer(ty: Type) -> bool {
2367
    if let case Type::Optional(inner) = ty {
2368
        return isNullableType(*inner);
2369
    }
2370
    return false;
2371
}
2372
2373
/// Check if a type uses the optional aggregate representation.
2374
export fn isOptionalAggregate(ty: Type) -> bool {
2375
    if let case Type::Optional(inner) = ty {
2376
        return not isNullableType(*inner);
2377
    }
2378
    return false;
2379
}
2380
2381
/// Check if a type can use null to represent `nil`.
2382
/// Pointers and slices have a data pointer that is never null when valid.
2383
export fn isNullableType(ty: Type) -> bool {
2384
    match ty {
2385
        case Type::Pointer { .. }, Type::Slice { .. } => return true,
2386
        else => return false,
2387
    }
2388
}
2389
2390
/// Get the layout of a nominal type.
2391
export fn getNominalLayout(info: NominalType) -> Layout {
2392
    match info {
2393
        case NominalType::Placeholder(_), NominalType::Resolving(_), NominalType::Application(_) => {
2394
            panic "getNominalLayout: unresolved type";
2395
        }
2396
        case NominalType::Record(recordType) => {
2397
            return *recordType.layout;
2398
        }
2399
        case NominalType::Union(unionType) => {
2400
            return *unionType.layout;
2401
        }
2402
    }
2403
}
2404
2405
/// Get the layout of a result aggregate with a tag and the larger payload.
2406
export unsafe fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout {
2407
    return resultLayout(payload, throwList);
2408
}
2409
2410
/// Compute tagged result storage while borrowing its error type table.
2411
fn resultLayout(payload: Type, throwList: &[*Type]) -> Layout {
2412
    let payloadLayout = typeLayout(payload);
2413
    let mut maxSize = payloadLayout.size;
2414
    let mut maxAlign = payloadLayout.alignment;
2415
2416
    for errType in throwList {
2417
        let errLayout = typeLayout(*errType);
2418
        set maxSize = max(maxSize, errLayout.size);
2419
        set maxAlign = max(maxAlign, errLayout.alignment);
2420
    }
2421
    return Layout {
2422
        size: PTR_SIZE + maxSize,
2423
        alignment: max(PTR_SIZE, maxAlign),
2424
    };
2425
}
2426
2427
/// Compute the layout for a union given its resolved variants.
2428
fn computeUnionLayout(variants: &[UnionVariant]) -> UnionLayoutInfo {
2429
    let tagSize: u32 = 1;
2430
    let mut maxVarSize: u32 = 0;
2431
    let mut maxVarAlign: u32 = 1;
2432
    let mut isAllVoid: bool = true;
2433
2434
    for variant in variants {
2435
        if variant.valueType <> Type::Void {
2436
            set isAllVoid = false;
2437
            let payloadLayout = typeLayout(variant.valueType);
2438
            set maxVarSize = max(maxVarSize, payloadLayout.size);
2439
            set maxVarAlign = max(maxVarAlign, payloadLayout.alignment);
2440
        }
2441
    }
2442
    let unionAlignment: u32 = max(1, maxVarAlign);
2443
    let unionValOffset: u32 = mem::alignUp(tagSize, maxVarAlign);
2444
    let unionLayout = Layout {
2445
        size: mem::alignUp(unionValOffset + maxVarSize, unionAlignment),
2446
        alignment: unionAlignment,
2447
    };
2448
    return UnionLayoutInfo { layout: unionLayout, valOffset: unionValOffset, isAllVoid };
2449
}
2450
2451
/// Compute the discriminant tag for a variant, advancing the iota counter.
2452
/// If the variant has an explicit `= N` value, uses that; otherwise uses iota.
2453
fn variantTag(variantDecl: ast::UnionDeclVariant, iota: &mut u32) -> u32 {
2454
    let mut tag: u32 = *iota;
2455
    if let valueNode = variantDecl.value {
2456
        let case ast::NodeValue::Number(lit) = valueNode.value
2457
            else panic "variantTag: expected number literal";
2458
        set tag = lit.magnitude as u32;
2459
    }
2460
    set *iota = tag + 1;
2461
    return tag;
2462
}
2463
2464
/// Check if a type is a union without payloads.
2465
export unsafe fn isVoidUnion(ty: Type) -> bool {
2466
    let case Type::Nominal(NominalType::Union(unionType)) = ty
2467
        else return false;
2468
    return unionType.isAllVoid;
2469
}
2470
2471
/// Check if a type should be treated as an address-like value.
2472
fn isAddressType(ty: Type) -> bool {
2473
    if isNullableType(ty) {
2474
        return true;
2475
    }
2476
    match ty {
2477
        case Type::Fn(_) => return true,
2478
        else => return false,
2479
    }
2480
}
2481
2482
/// Return the representable range for an integer type.
2483
fn integerRange(ty: Type) -> ?IntegerRange {
2484
    match ty {
2485
        case Type::I8 => return IntegerRange::Signed {
2486
            bits: 8,
2487
            min: I8_MIN as i64,
2488
            max: I8_MAX as i64,
2489
            lim: (I8_MAX as u64) + 1,
2490
        },
2491
        case Type::I16 => return IntegerRange::Signed {
2492
            bits: 16,
2493
            min: I16_MIN as i64,
2494
            max: I16_MAX as i64,
2495
            lim: (I16_MAX as u64) + 1,
2496
        },
2497
        case Type::I32 => return IntegerRange::Signed {
2498
            bits: 32,
2499
            min: I32_MIN as i64,
2500
            max: I32_MAX as i64,
2501
            lim: (I32_MAX as u64) + 1,
2502
        },
2503
        case Type::I64, Type::Int => return IntegerRange::Signed {
2504
            bits: 64,
2505
            min: I64_MIN,
2506
            max: I64_MAX,
2507
            lim: (I64_MAX as u64) + 1,
2508
        },
2509
        case Type::U8 => return IntegerRange::Unsigned { bits: 8, max: U8_MAX as u64 },
2510
        case Type::U16 => return IntegerRange::Unsigned { bits: 16, max: U16_MAX as u64 },
2511
        case Type::U32 => return IntegerRange::Unsigned { bits: 32, max: parser::U32_MAX as u64 },
2512
        case Type::U64 => return IntegerRange::Unsigned { bits: 64, max: parser::U64_MAX },
2513
        else => return nil,
2514
    }
2515
}
2516
2517
/// Validate that an integer constant fits within the target type's range.
2518
fn validateConstIntRange(value: ConstValue, target: Type) -> bool {
2519
    let range = integerRange(target)
2520
        else panic "validateConstIntRange: expected integer type";
2521
    let case ConstValue::Int(int) = value
2522
        else panic "validateConstIntRange: expected integer constant";
2523
2524
    match range {
2525
        case IntegerRange::Signed { lim, .. } => {
2526
            if int.negative {
2527
                if int.magnitude > lim {
2528
                    return false;
2529
                }
2530
                return true;
2531
            }
2532
            if int.magnitude > lim - 1 {
2533
                return false;
2534
            }
2535
            return true;
2536
        }
2537
        case IntegerRange::Unsigned { max, .. } => {
2538
            if int.negative or int.magnitude > max {
2539
                return false;
2540
            }
2541
            return true;
2542
        }
2543
    }
2544
}
2545
2546
/// Ensure all nested nominal types in a type are resolved.
2547
unsafe fn ensureTypeResolved 'arena (self: &mut Resolver 'arena, ty: Type, site: *ast::Node) throws (ResolveError) {
2548
    match ty {
2549
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
2550
        // Pointer, slice, and cell layouts do not depend on their element layout.
2551
        case Type::Pointer { .. }, Type::Slice { .. }, Type::Cell { .. } => {
2552
        },
2553
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
2554
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
2555
        else => {},
2556
    }
2557
}
2558
2559
/// Ensure a nominal type has its body resolved.
2560
unsafe fn ensureNominalResolved 'arena (self: &mut Resolver 'arena, tyInfo: *unsafe NominalType, site: *ast::Node)
2561
    throws (ResolveError)
2562
{
2563
    if let case NominalType::Application(applied) = *tyInfo {
2564
        try resolveNominalApplication(self, applied, site);
2565
        return;
2566
    }
2567
    if let case NominalType::Resolving(_) = *tyInfo {
2568
        throw emitError(self, site, ErrorKind::RecursiveType);
2569
    }
2570
    if let case NominalType::Placeholder(declNode) = *tyInfo {
2571
        // When resolving on-demand (e.g. from a child module), switch to the
2572
        // declaring module's scope so field type lookups find the right symbols.
2573
        let prevScope = self.scope;
2574
        let prevMod = self.currentMod;
2575
2576
        if let sym = symbolFor(self, declNode) {
2577
            if let mid = sym.moduleId {
2578
                if (mid as u32) < self.moduleScopes.len {
2579
                    if let ms = self.moduleScopes[mid as u32] {
2580
                        set self.scope = ms;
2581
                        set self.currentMod = mid;
2582
                    }
2583
                }
2584
            }
2585
        }
2586
2587
        match declNode.value {
2588
            case ast::NodeValue::RecordDecl(decl) => {
2589
                try resolveRecordBody(self, declNode, decl) catch error {
2590
                    set self.scope = prevScope;
2591
                    set self.currentMod = prevMod;
2592
                    throw error;
2593
                };
2594
            }
2595
            case ast::NodeValue::UnionDecl(decl) => {
2596
                try resolveUnionBody(self, declNode, decl) catch error {
2597
                    set self.scope = prevScope;
2598
                    set self.currentMod = prevMod;
2599
                    throw error;
2600
                };
2601
            }
2602
            else => {},
2603
        }
2604
        set self.scope = prevScope;
2605
        set self.currentMod = prevMod;
2606
    }
2607
}
2608
2609
/// Check if all elements in a node list are assignable to the target type.
2610
unsafe fn isListAssignable 'arena (self: &mut Resolver 'arena, targetType: Type, items: *[*ast::Node]) -> bool {
2611
    for itemNode in items {
2612
        let elemTy = typeFor(self, itemNode)
2613
            else return false;
2614
        if let _ = isAssignable(self, targetType, elemTy, itemNode) {
2615
            // Do nothing.
2616
        } else {
2617
            return false;
2618
        }
2619
    }
2620
    return true;
2621
}
2622
2623
/// Return whether pointer classes are compatible in the current safety context.
2624
fn pointerClassesAssignable(
2625
    to: types::PointerClass,
2626
    from: types::PointerClass,
2627
    inUnsafeContext: bool,
2628
) -> bool {
2629
    return to == from or (
2630
        to == types::PointerClass::Ref
2631
        and (types::isReference(from) or from == types::PointerClass::Owned
2632
            or (from == types::PointerClass::Unsafe and inUnsafeContext))
2633
    );
2634
}
2635
2636
/// Limit an exclusive value's implicit borrow to its owner's borrow.
2637
unsafe fn assignableValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: Type) -> Type {
2638
    match source {
2639
        case Type::Pointer { class, target, mutable: true } => {
2640
            let usable = pointerAddressClass(self, node, class, true);
2641
            return Type::Pointer { class: usable, target, mutable: true };
2642
        }
2643
        case Type::Slice { class, item, mutable: true } => {
2644
            let usable = pointerAddressClass(self, node, class, true);
2645
            return Type::Slice { class: usable, item, mutable: true };
2646
        }
2647
        case Type::TraitObject { class, traitInfo, mutable: true } => {
2648
            let usable = pointerAddressClass(self, node, class, true);
2649
            return Type::TraitObject { class: usable, traitInfo, mutable: true };
2650
        }
2651
        case Type::Optional(inner) => {
2652
            let value = assignableValueType(self, node, *inner);
2653
            if typesEqual(value, *inner) {
2654
                return source;
2655
            }
2656
            return Type::Optional(allocType(self, value));
2657
        }
2658
        else => return source,
2659
    }
2660
}
2661
2662
/// Check if the `from` type is assignable to the `to` type, and return a
2663
/// coercion plan if so.
2664
/// Referenced storage requires equal element types. Function values may gain
2665
/// an unsafe call requirement.
2666
unsafe fn isAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, rval: *ast::Node) -> ?Coercion {
2667
    let from = assignableValueType(self, rval, source);
2668
    if to == Type::Unknown or from == Type::Unknown {
2669
        return nil;
2670
    }
2671
    if from == Type::Undefined {
2672
        if containsRegion(to) {
2673
            return nil;
2674
        }
2675
        if to == Type::Never {
2676
            return nil;
2677
        }
2678
        // TODO: Don't let `undefined` be used in place of functions and other
2679
        // non-data types.
2680
        return Coercion::Identity;
2681
    }
2682
    // The "never" type can always be assigned, since the code path is never
2683
    // executed.
2684
    if from == Type::Never {
2685
        return Coercion::Identity;
2686
    }
2687
    if to == from {
2688
        return Coercion::Identity;
2689
    }
2690
    if let case Type::Cell { class, permission, payload } = to {
2691
        let case Type::Cell {
2692
            class: sourceClass, permission: sourcePermission, payload: sourcePayload,
2693
        } = from else return nil;
2694
        let mut storageAssignable = pointerClassesAssignable(
2695
            class, sourceClass, self.inUnsafeContext
2696
        );
2697
        if let case types::PointerClass::Region(targetRegion) = class {
2698
            if let case types::PointerClass::Region(sourceRegion) = sourceClass {
2699
                set storageAssignable = types::regionContains(sourceRegion, targetRegion);
2700
            }
2701
        }
2702
        if permission == sourcePermission and storageAssignable
2703
            and typesEqual(*payload, *sourcePayload)
2704
        {
2705
            return Coercion::Identity;
2706
        }
2707
        return nil;
2708
    }
2709
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
2710
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
2711
            else return nil;
2712
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) {
2713
            return nil;
2714
        }
2715
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
2716
        if *lhsTarget == Type::Opaque {
2717
            if lhsMutable and not rhsMutable {
2718
                return nil;
2719
            }
2720
            return Coercion::Identity;
2721
        }
2722
        if lhsMutable and not rhsMutable {
2723
            return nil;
2724
        }
2725
        if typesEqual(*lhsTarget, *rhsTarget) {
2726
            return Coercion::Identity;
2727
        }
2728
        return nil;
2729
    }
2730
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
2731
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
2732
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2733
                or (lhsMutable and not rhsMutable)
2734
            {
2735
                return nil;
2736
            }
2737
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
2738
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
2739
            }
2740
        }
2741
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
2742
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2743
                or lhsTraitInfo <> rhsTraitInfo
2744
            {
2745
                return nil;
2746
            }
2747
            if lhsMutable and not rhsMutable {
2748
                return nil;
2749
            }
2750
            return Coercion::Identity;
2751
        }
2752
        return nil;
2753
    }
2754
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
2755
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
2756
            else return nil;
2757
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
2758
            or (lhsMutable and not rhsMutable)
2759
        {
2760
            return nil;
2761
        }
2762
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
2763
        if *lhsItem == Type::Opaque {
2764
            return Coercion::Identity;
2765
        }
2766
        if typesEqual(*lhsItem, *rhsItem) {
2767
            return Coercion::Identity;
2768
        }
2769
        return nil;
2770
    }
2771
    match to {
2772
        case Type::Array(lhs) => {
2773
            let case Type::Array(rhs) = from
2774
                else return nil;
2775
2776
            if lhs.length <> rhs.length {
2777
                return nil;
2778
            }
2779
            // For array literals, check each element individually for
2780
            // assignability.
2781
            match rval.value {
2782
                case ast::NodeValue::ArrayLit(items) => {
2783
                    if rhs.length == 0 and lhs.length == 0 {
2784
                        return Coercion::Identity;
2785
                    }
2786
                    // TODO: This won't work, because we should be setting coercions
2787
                    // for every list item, but we don't. It's best to not have an
2788
                    // `isAssignable` function and just have one that records coercions.
2789
                    if isListAssignable(self, *lhs.item, items) {
2790
                        return Coercion::Identity;
2791
                    }
2792
                    return nil;
2793
                }
2794
                case ast::NodeValue::ArrayRepeatLit(repeat) => {
2795
                    return isAssignable(self, *lhs.item, *rhs.item, repeat.item);
2796
                }
2797
                else => {
2798
                    if typesEqual(*lhs.item, *rhs.item) {
2799
                        return Coercion::Identity;
2800
                    }
2801
                    return nil;
2802
                }
2803
            }
2804
        }
2805
2806
        case Type::Optional(inner) => {
2807
            if from == Type::Nil {
2808
                return Coercion::OptionalLift(to);
2809
            }
2810
            if let _ = isAssignable(self, *inner, from, rval) {
2811
                return Coercion::OptionalLift(to);
2812
            }
2813
            if let case Type::Optional(fromInner) = from {
2814
                return isAssignable(self, *inner, *fromInner, rval);
2815
            }
2816
            return nil;
2817
        }
2818
2819
        case Type::Fn(toInfo) => {
2820
            // Allow function type structural matching.
2821
            if let case Type::Fn(fromInfo) = from {
2822
                if fnTypeEqual(toInfo, fromInfo) or (
2823
                    toInfo.isUnsafe and not fromInfo.isUnsafe
2824
                    and fnSignatureEqual(toInfo, fromInfo)
2825
                ) {
2826
                    return Coercion::Identity;
2827
                }
2828
            }
2829
            return nil;
2830
        }
2831
        else => {
2832
            if isNumericType(to) and isNumericType(from) {
2833
                // Perform range validation at compile time if possible.
2834
                // For unsuffixed integer expressions (`Type::Int`), only
2835
                // validate literals directly written by the programmer.
2836
                // Folded results (e.g. `0 - 65`) may not fit the target
2837
                // type but are valid wrapping arithmetic at runtime.
2838
                if let value = constValueEntry(self, rval) {
2839
                    if from <> Type::Int or isIntegerLiteralExpr(rval) {
2840
                        if validateConstIntRange(value, to) {
2841
                            return Coercion::Identity;
2842
                        }
2843
                        return nil;
2844
                    }
2845
                    // Folded constant expression (e.g. `1 + 2`): if the
2846
                    // result fits the target, use identity. Otherwise allow
2847
                    // wrapping via numeric cast.
2848
                    if validateConstIntRange(value, to) {
2849
                        return Coercion::Identity;
2850
                    }
2851
                }
2852
                // Allow unsuffixed integer expressions to be inferred from context.
2853
                if from == Type::Int {
2854
                    return Coercion::NumericCast { from, to };
2855
                }
2856
                // Non-constant numeric values require an explicit cast.
2857
                return nil;
2858
            }
2859
        }
2860
    }
2861
    return nil;
2862
}
2863
2864
/// Check if two function type descriptors are structurally equivalent.
2865
fn fnTypeEqual(a: &FnType, b: &FnType) -> bool {
2866
    if a.isUnsafe <> b.isUnsafe {
2867
        return false;
2868
    }
2869
    return fnSignatureEqual(a, b);
2870
}
2871
2872
/// Compare parameter, return, and error types of functions.
2873
fn fnSignatureEqual(a: &FnType, b: &FnType) -> bool {
2874
    if a.regions <> b.regions {
2875
        return false;
2876
    }
2877
    if a.paramTypes.len <> b.paramTypes.len {
2878
        return false;
2879
    }
2880
    if a.throwList.len <> b.throwList.len {
2881
        return false;
2882
    }
2883
    if not typesEqual(*a.returnType, *b.returnType) {
2884
        return false;
2885
    }
2886
    for i in 0..a.paramTypes.len {
2887
        if not typesEqual(*a.paramTypes[i], *b.paramTypes[i]) {
2888
            return false;
2889
        }
2890
    }
2891
    for i in 0..a.throwList.len {
2892
        if not typesEqual(*a.throwList[i], *b.throwList[i]) {
2893
            return false;
2894
        }
2895
    }
2896
    return true;
2897
}
2898
2899
/// Check if two types are structurally equal.
2900
export fn typesEqual(a: Type, b: Type) -> bool {
2901
    // Nominal and trait types compare by descriptor identity.
2902
    if a == b {
2903
        return true;
2904
    }
2905
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
2906
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
2907
            else return false;
2908
        return aClass == bClass and aMutable == bMutable
2909
            and typesEqual(*aTarget, *bTarget);
2910
    }
2911
    if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a {
2912
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
2913
            else return false;
2914
        return aClass == bClass and aMutable == bMutable
2915
            and typesEqual(*aItem, *bItem);
2916
    }
2917
    match a {
2918
        case Type::Cell { class, permission, payload } => {
2919
            let case Type::Cell {
2920
                class: otherClass, permission: otherPermission, payload: other,
2921
            } = b else return false;
2922
            return class == otherClass and permission == otherPermission
2923
                and typesEqual(*payload, *other);
2924
        }
2925
        case Type::Array(aa) => {
2926
            let case Type::Array(ab) = b else return false;
2927
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
2928
        }
2929
        case Type::Optional(oa) => {
2930
            let case Type::Optional(ob) = b else return false;
2931
            return typesEqual(*oa, *ob);
2932
        }
2933
        case Type::Fn(fa) => {
2934
            let case Type::Fn(fb) = b else return false;
2935
            return fnTypeEqual(fa, fb);
2936
        }
2937
        else => return false,
2938
    }
2939
}
2940
2941
/// Compare types after lexical region arguments are erased.
2942
/// Nominal types retain their source declaration identity.
2943
export unsafe fn erasedTypesEqual(a: Type, b: Type) -> bool {
2944
    if typesEqual(a, b) {
2945
        return true;
2946
    }
2947
    match a {
2948
        case Type::Cell { class, permission, payload } => {
2949
            let case Type::Cell {
2950
                class: otherClass, permission: otherPermission, payload: other,
2951
            } = b else return false;
2952
            if (permission == nil) <> (otherPermission == nil) {
2953
                return false;
2954
            }
2955
            return erasedClassesEqual(class, otherClass) and erasedTypesEqual(*payload, *other);
2956
        }
2957
        case Type::Session(_) => {
2958
            let case Type::Session(_) = b else return false;
2959
            return true;
2960
        }
2961
        case Type::Nominal(left) => {
2962
            let case Type::Nominal(right) = b else return false;
2963
            let mut leftBase = left;
2964
            let mut rightBase = right;
2965
            if let app = nominalApplication(left) {
2966
                set leftBase = app.base;
2967
            }
2968
            if let app = nominalApplication(right) {
2969
                set rightBase = app.base;
2970
            }
2971
            return leftBase == rightBase;
2972
        }
2973
        case Type::Pointer { class, target, mutable } => {
2974
            let case Type::Pointer { class: otherClass, target: other, mutable: otherMutable } = b
2975
                else return false;
2976
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2977
                and erasedTypesEqual(*target, *other);
2978
        }
2979
        case Type::Slice { class, item, mutable } => {
2980
            let case Type::Slice { class: otherClass, item: other, mutable: otherMutable } = b
2981
                else return false;
2982
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable
2983
                and erasedTypesEqual(*item, *other);
2984
        }
2985
        case Type::TraitObject { class, traitInfo, mutable } => {
2986
            let case Type::TraitObject { class: otherClass, traitInfo: other, mutable: otherMutable } = b
2987
                else return false;
2988
            return erasedClassesEqual(class, otherClass) and mutable == otherMutable and traitInfo == other;
2989
        }
2990
        case Type::Array(left) => {
2991
            let case Type::Array(right) = b else return false;
2992
            return left.length == right.length and erasedTypesEqual(*left.item, *right.item);
2993
        }
2994
        case Type::Optional(left) => {
2995
            let case Type::Optional(right) = b else return false;
2996
            return erasedTypesEqual(*left, *right);
2997
        }
2998
        case Type::Fn(left) => {
2999
            let case Type::Fn(right) = b else return false;
3000
            if left.isUnsafe <> right.isUnsafe or left.paramTypes.len <> right.paramTypes.len
3001
                or left.throwList.len <> right.throwList.len {
3002
                return false;
3003
            }
3004
            for ty, i in left.paramTypes {
3005
                if not erasedTypesEqual(*ty, *right.paramTypes[i]) {
3006
                    return false;
3007
                }
3008
            }
3009
            for ty, i in left.throwList {
3010
                if not erasedTypesEqual(*ty, *right.throwList[i]) {
3011
                    return false;
3012
                }
3013
            }
3014
            return erasedTypesEqual(*left.returnType, *right.returnType);
3015
        }
3016
        else => return false,
3017
    }
3018
}
3019
3020
/// Compare pointer classes without lexical region identities.
3021
fn erasedClassesEqual(a: types::PointerClass, b: types::PointerClass) -> bool {
3022
    return a == b or (types::isReference(a) and types::isReference(b));
3023
}
3024
3025
/// Require distinct runtime tags for errors with different source types.
3026
unsafe fn validateErrorTag 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type, errors: *[*Type]) throws (ResolveError) {
3027
    for other in errors {
3028
        if not typesEqual(ty, *other) and erasedTypesEqual(ty, *other) {
3029
            throw emitError(self, node, ErrorKind::AmbiguousRegionalError);
3030
        }
3031
    }
3032
}
3033
3034
/// Return whether `ty` is a direct reference.
3035
export fn isRefType(ty: Type) -> bool {
3036
    match ty {
3037
        case Type::Cell { class, .. } => return types::isReference(class),
3038
        case Type::Pointer { class, .. } => return types::isReference(class),
3039
        case Type::Slice { class, .. } => return types::isReference(class),
3040
        case Type::TraitObject { class, .. } => return types::isReference(class),
3041
        else => return false,
3042
    }
3043
}
3044
3045
/// Get the region of a direct named reference.
3046
fn referenceRegion(ty: Type) -> ?*unsafe types::Region {
3047
    let mut class = types::PointerClass::Ref;
3048
    match ty {
3049
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
3050
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
3051
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
3052
        case Type::TraitObject { class: objectClass, .. } => set class = objectClass,
3053
        else => return nil,
3054
    }
3055
    if let case types::PointerClass::Region(region) = class {
3056
        return region;
3057
    }
3058
    return nil;
3059
}
3060
3061
/// Require every free region in a value type to remain in lexical scope.
3062
unsafe fn validateRegionDependencies 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
3063
    throws (ResolveError)
3064
{
3065
    try validateRegionStorage(self, node, ty, nil);
3066
}
3067
3068
/// Check a dependency against storage lifetime or current lexical visibility.
3069
unsafe fn regionCoversStorage(
3070
    scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region
3071
) -> bool {
3072
    if let region = destination {
3073
        return types::regionContains(dependency, region);
3074
    }
3075
    return regionInScope(scope, dependency.id);
3076
}
3077
3078
/// Require stored references to cover the lifetime of checked destination storage.
3079
unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type)
3080
    throws (ResolveError)
3081
{
3082
    if let case types::PointerClass::Region(region) = addressStorageClass(self, place) {
3083
        try validateRegionStorage(self, value, ty, region);
3084
    }
3085
}
3086
3087
/// Require all type dependencies to cover the destination or active lexical scope.
3088
/// Permission identities are visibility dependencies rather than storage lifetimes.
3089
unsafe fn validateRegionStorage 'arena (
3090
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, destination: ?*unsafe types::Region
3091
)
3092
    throws (ResolveError)
3093
{
3094
    let generation = nextNominalTraversalGeneration(self);
3095
    try validateRegionStorageType(self, node, ty, destination, generation);
3096
}
3097
3098
/// Walk one value type once per applied nominal descriptor.
3099
unsafe fn validateRegionStorageType 'arena (
3100
    self: &mut Resolver 'arena,
3101
    node: *ast::Node,
3102
    ty: Type,
3103
    destination: ?*unsafe types::Region,
3104
    generation: u32,
3105
)
3106
    throws (ResolveError)
3107
{
3108
    if let case Type::Session(region) = ty {
3109
        if not regionCoversStorage(self.regionScope, region, destination) {
3110
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
3111
        }
3112
    }
3113
    if let region = referenceRegion(ty) {
3114
        if not regionCoversStorage(self.regionScope, region, destination) {
3115
            throw emitError(self, node, ErrorKind::RegionEscape(region.name));
3116
        }
3117
    }
3118
    match ty {
3119
        case Type::Pointer { target, .. } =>
3120
            try validateRegionStorageType(
3121
                self, node, *target, destination, generation,
3122
            ),
3123
        case Type::Slice { item, .. } =>
3124
            try validateRegionStorageType(
3125
                self, node, *item, destination, generation,
3126
            ),
3127
        case Type::Cell { permission, payload, .. } => {
3128
            if let region = permission; not regionInScope(self.regionScope, region.id) {
3129
                throw emitError(self, node, ErrorKind::RegionEscape(region.name));
3130
            }
3131
            try validateRegionStorageType(
3132
                self, node, *payload, destination, generation,
3133
            );
3134
        }
3135
        case Type::Array(array) =>
3136
            try validateRegionStorageType(
3137
                self, node, *array.item, destination, generation,
3138
            ),
3139
        case Type::Optional(inner) =>
3140
            try validateRegionStorageType(
3141
                self, node, *inner, destination, generation,
3142
            ),
3143
        case Type::Nominal(info) => {
3144
            let applied = nominalApplication(info) else return;
3145
            for region in applied.arguments {
3146
                if not regionInScope(self.regionScope, region.id) {
3147
                    throw emitError(self, node, ErrorKind::RegionEscape(region.name));
3148
                }
3149
            }
3150
            if not visitNominalApplication(
3151
                applied, generation, RegionTypeRole::StorageValidation,
3152
            ) {
3153
                return;
3154
            }
3155
            match *info {
3156
                case NominalType::Record(recordType) => {
3157
                    for field in recordType.fields {
3158
                        try validateRegionStorageType(
3159
                            self, node, field.fieldType, destination, generation,
3160
                        );
3161
                    }
3162
                }
3163
                case NominalType::Union(unionType) => {
3164
                    for variant in unionType.variants {
3165
                        try validateRegionStorageType(
3166
                            self, node, variant.valueType, destination, generation,
3167
                        );
3168
                    }
3169
                }
3170
                // Unresolved applications have no member view to inspect yet.
3171
                case NominalType::Placeholder(_), NominalType::Resolving(_),
3172
                     NominalType::Application(_) => return,
3173
            }
3174
        }
3175
        case Type::Fn(info) => {
3176
            if info.regions <> nil {
3177
                return;
3178
            }
3179
            for parameter in info.paramTypes {
3180
                try validateRegionStorageType(
3181
                    self, node, *parameter, destination, generation,
3182
                );
3183
            }
3184
            for error in info.throwList {
3185
                try validateRegionStorageType(
3186
                    self, node, *error, destination, generation,
3187
                );
3188
            }
3189
            try validateRegionStorageType(
3190
                self, node, *info.returnType, destination, generation,
3191
            );
3192
        }
3193
        else => {}
3194
    }
3195
}
3196
3197
/// Return whether a value type carries a named storage lifetime.
3198
/// Cell permission identities authorize access and do not constrain storage.
3199
unsafe fn containsStorageRegion 'arena (
3200
    self: &mut Resolver 'arena, node: *ast::Node, root: Type
3201
) -> bool throws (ResolveError) {
3202
    let generation = nextNominalTraversalGeneration(self);
3203
    return try typeContainsStorageRegion(self, node, root, generation);
3204
}
3205
3206
/// Search one value type once per applied nominal descriptor.
3207
unsafe fn typeContainsStorageRegion 'arena (
3208
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type, generation: u32
3209
) -> bool throws (ResolveError) {
3210
    match ty {
3211
        case Type::Cell { class, payload, .. } => {
3212
            if let case types::PointerClass::Region(_) = class {
3213
                return true;
3214
            }
3215
            return try typeContainsStorageRegion(self, node, *payload, generation);
3216
        }
3217
        case Type::Session(_) => return true,
3218
        case Type::Pointer { class, target, .. } => {
3219
            if let case types::PointerClass::Region(_) = class {
3220
                return true;
3221
            }
3222
            return try typeContainsStorageRegion(self, node, *target, generation);
3223
        }
3224
        case Type::Slice { class, item, .. } => {
3225
            if let case types::PointerClass::Region(_) = class {
3226
                return true;
3227
            }
3228
            return try typeContainsStorageRegion(self, node, *item, generation);
3229
        }
3230
        case Type::TraitObject { class, .. } => {
3231
            if let case types::PointerClass::Region(_) = class {
3232
                return true;
3233
            }
3234
            return false;
3235
        }
3236
        case Type::Array(array) =>
3237
            return try typeContainsStorageRegion(self, node, *array.item, generation),
3238
        case Type::Optional(inner) =>
3239
            return try typeContainsStorageRegion(self, node, *inner, generation),
3240
        case Type::Range { start, end } => {
3241
            if let startType = start;
3242
                try typeContainsStorageRegion(self, node, *startType, generation)
3243
            {
3244
                return true;
3245
            }
3246
            if let endType = end {
3247
                return try typeContainsStorageRegion(self, node, *endType, generation);
3248
            }
3249
            return false;
3250
        }
3251
        case Type::Nominal(info) => {
3252
            try ensureNominalResolved(self, info, node);
3253
            let applied = nominalApplication(info) else return false;
3254
            if not visitNominalApplication(
3255
                applied, generation, RegionTypeRole::StoragePresence,
3256
            ) {
3257
                return false;
3258
            }
3259
            match *info {
3260
                case NominalType::Record(recordType) => {
3261
                    for field in recordType.fields {
3262
                        if try typeContainsStorageRegion(
3263
                            self, node, field.fieldType, generation,
3264
                        ) {
3265
                            return true;
3266
                        }
3267
                    }
3268
                }
3269
                case NominalType::Union(unionType) => {
3270
                    for variant in unionType.variants {
3271
                        if try typeContainsStorageRegion(
3272
                            self, node, variant.valueType, generation,
3273
                        ) {
3274
                            return true;
3275
                        }
3276
                    }
3277
                }
3278
                case NominalType::Placeholder(_), NominalType::Resolving(_),
3279
                     NominalType::Application(_) =>
3280
                    panic "typeContainsStorageRegion: unresolved nominal type",
3281
            }
3282
            return false;
3283
        }
3284
        case Type::Fn(info) => {
3285
            if info.regions <> nil {
3286
                return false;
3287
            }
3288
            for parameter in info.paramTypes {
3289
                if try typeContainsStorageRegion(
3290
                    self, node, *parameter, generation,
3291
                ) {
3292
                    return true;
3293
                }
3294
            }
3295
            for error in info.throwList {
3296
                if try typeContainsStorageRegion(self, node, *error, generation) {
3297
                    return true;
3298
                }
3299
            }
3300
            return try typeContainsStorageRegion(
3301
                self, node, *info.returnType, generation,
3302
            );
3303
        }
3304
        else => return false,
3305
    }
3306
}
3307
3308
/// Return whether a type has an explicit region dependency.
3309
unsafe fn containsRegion(ty: Type) -> bool {
3310
    match ty {
3311
        case Type::Cell { class, permission, payload } => {
3312
            if permission <> nil {
3313
                return true;
3314
            }
3315
            if let case types::PointerClass::Region(_) = class {
3316
                return true;
3317
            }
3318
            return containsRegion(*payload);
3319
        }
3320
        case Type::Session(_) => return true,
3321
        case Type::Pointer { class, target, .. } => {
3322
            if let case types::PointerClass::Region(_) = class {
3323
                return true;
3324
            }
3325
            return containsRegion(*target);
3326
        }
3327
        case Type::Slice { class, item, .. } => {
3328
            if let case types::PointerClass::Region(_) = class {
3329
                return true;
3330
            }
3331
            return containsRegion(*item);
3332
        }
3333
        case Type::TraitObject { class, .. } => {
3334
            if let case types::PointerClass::Region(_) = class {
3335
                return true;
3336
            }
3337
            return false;
3338
        }
3339
        case Type::Array(array) => return containsRegion(*array.item),
3340
        case Type::Optional(inner) => return containsRegion(*inner),
3341
        case Type::Fn(info) => {
3342
            if info.regions <> nil or containsRegion(*info.returnType) {
3343
                return true;
3344
            }
3345
            for param in info.paramTypes {
3346
                if containsRegion(*param) {
3347
                    return true;
3348
                }
3349
            }
3350
            for error in info.throwList {
3351
                if containsRegion(*error) {
3352
                    return true;
3353
                }
3354
            }
3355
            return false;
3356
        }
3357
        case Type::Nominal(info) => return nominalApplication(info) <> nil,
3358
        else => return false,
3359
    }
3360
}
3361
3362
/// Return whether a stored type contains a reference without a named region.
3363
fn containsUnscopedRef(ty: Type) -> bool {
3364
    if isRefType(ty) and referenceRegion(ty) == nil {
3365
        return true;
3366
    }
3367
    if let case Type::Pointer { target, .. } = ty {
3368
        return containsUnscopedRef(*target);
3369
    }
3370
    if let case Type::Slice { item, .. } = ty {
3371
        return containsUnscopedRef(*item);
3372
    }
3373
    match ty {
3374
        case Type::Cell { payload, .. } => return containsUnscopedRef(*payload),
3375
        case Type::Array(array) => return containsUnscopedRef(*array.item),
3376
        case Type::Optional(inner) => return containsUnscopedRef(*inner),
3377
        case Type::Fn(info) => return info.regions <> nil,
3378
        // Nominal declarations validate their own fields and variants.
3379
        // Treating them as leaves also terminates recursive pointer types.
3380
        case Type::Nominal(_) => return false,
3381
        else => return false,
3382
    }
3383
}
3384
3385
/// Return whether `ty` may be duplicated implicitly.
3386
export unsafe fn isCopy(ty: Type) -> bool {
3387
    match ty {
3388
        case Type::Session(_) => return false,
3389
        case Type::Pointer { class, mutable, .. } =>
3390
            return class == types::PointerClass::Unsafe or not mutable,
3391
        case Type::Slice { class, mutable, .. } =>
3392
            return class == types::PointerClass::Unsafe or not mutable,
3393
        case Type::TraitObject { class, mutable, .. } =>
3394
            return class == types::PointerClass::Unsafe or not mutable,
3395
        case Type::Array(array) => return isCopy(*array.item),
3396
        case Type::Optional(inner) => return isCopy(*inner),
3397
        case Type::Nominal(NominalType::Record(recInfo)) => return recInfo.declaredCopy,
3398
        case Type::Nominal(NominalType::Union(unionType)) => return unionType.declaredCopy,
3399
        case Type::Nominal(NominalType::Application(applied)) => return isCopy(Type::Nominal(applied.base)),
3400
        case Type::Nominal(NominalType::Placeholder(_)), Type::Nominal(NominalType::Resolving(_)) => return false,
3401
        else => return true,
3402
    }
3403
}
3404
3405
/// Return whether a type must be consumed exactly once.
3406
export unsafe fn isLinear(ty: Type) -> bool {
3407
    match ty {
3408
        case Type::Nominal(NominalType::Application(applied)) => return isLinear(Type::Nominal(applied.base)),
3409
        case Type::Array(array) => return isLinear(*array.item),
3410
        case Type::Optional(inner) => return isLinear(*inner),
3411
        case Type::Nominal(NominalType::Record(recInfo)) => {
3412
            if recInfo.declaredLinear {
3413
                return true;
3414
            }
3415
            for field in recInfo.fields {
3416
                if isLinear(field.fieldType) {
3417
                    return true;
3418
                }
3419
            }
3420
            return false;
3421
        }
3422
        case Type::Nominal(NominalType::Union(unionType)) => {
3423
            if unionType.declaredLinear {
3424
                return true;
3425
            }
3426
            for variant in unionType.variants {
3427
                if isLinear(variant.valueType) {
3428
                    return true;
3429
                }
3430
            }
3431
            return false;
3432
        }
3433
        else => return false,
3434
    }
3435
}
3436
3437
/// Return whether a by-value use moves `ty`.
3438
unsafe fn isMoveOnly(ty: Type) -> bool {
3439
    return not isCopy(ty);
3440
}
3441
3442
/// Return whether `ty` is a direct unsafe pointer-like value.
3443
fn isUnsafePointerType(ty: Type) -> bool {
3444
    match ty {
3445
        case Type::Cell { class: types::PointerClass::Unsafe, .. },
3446
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
3447
             Type::Slice { class: types::PointerClass::Unsafe, .. },
3448
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
3449
        else => return false,
3450
    }
3451
}
3452
3453
/// Get the record info from a record type.
3454
export unsafe fn getRecord(ty: Type) -> ?RecordType {
3455
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
3456
    return recInfo;
3457
}
3458
3459
/// Auto-dereference a type: if it's a pointer, return the target type.
3460
export fn autoDeref(ty: Type) -> Type {
3461
    if let case Type::Pointer { target, .. } = ty {
3462
        return *target;
3463
    }
3464
    return ty;
3465
}
3466
3467
/// Get field info for a record-like type (records, slices) by field index.
3468
export unsafe fn getRecordField(ty: Type, index: u32) -> ?RecordField {
3469
    if let case Type::Slice { class, item, mutable } = ty {
3470
        match index {
3471
            case 0 => return RecordField {
3472
                name: PTR_FIELD,
3473
                fieldType: Type::Pointer { class, target: item, mutable },
3474
                offset: 0,
3475
            },
3476
            case 1 => return RecordField {
3477
                name: LEN_FIELD,
3478
                fieldType: Type::U32,
3479
                offset: PTR_SIZE as i32,
3480
            },
3481
            case 2 => return RecordField {
3482
                name: CAP_FIELD,
3483
                fieldType: Type::U32,
3484
                offset: PTR_SIZE as i32 + 4,
3485
            },
3486
            else => return nil,
3487
        }
3488
    }
3489
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
3490
        index < recInfo.fields.len
3491
    {
3492
        return recInfo.fields[index];
3493
    }
3494
    return nil;
3495
}
3496
3497
/// Check if the two types can be compared for equality.
3498
unsafe fn isComparable(left: Type, right: Type) -> bool {
3499
    if left == Type::Unknown or right == Type::Unknown {
3500
        return false;
3501
    }
3502
    if left == right {
3503
        return true;
3504
    }
3505
    // Comparisons with optionals.
3506
    if let case Type::Optional(l) = left {
3507
        if let case Type::Optional(r) = right {
3508
            return isComparable(*l, *r);
3509
        } else if right == Type::Nil {
3510
            return true;
3511
        }
3512
        return isComparable(*l, right);
3513
    } else if let case Type::Optional(_) = right {
3514
        return isComparable(right, left); // Flip order.
3515
    }
3516
    // Pointer comparisons ignore mutability.
3517
    if let case Type::Pointer { target: lTarget, .. } = left {
3518
        if let case Type::Pointer { target: rTarget, .. } = right {
3519
            return typesEqual(*lTarget, *rTarget);
3520
        }
3521
    }
3522
    // Numeric types.
3523
    if isNumericType(left) and isNumericType(right) {
3524
        return true;
3525
    }
3526
    return false;
3527
}
3528
3529
/// Check if the `from` type is assignable to the `to` type, and return a
3530
/// coercion plan if so, or throw an error if not.
3531
unsafe fn expectAssignable 'arena (self: &mut Resolver 'arena, to: Type, source: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
3532
    let from = assignableValueType(self, site, source);
3533
    if isRefType(to) and isUnsafePointerType(from) {
3534
        try requireUnsafe(self, site);
3535
    }
3536
    // Ensure any nested nominal types are resolved before checking assignability.
3537
    try ensureTypeResolved(self, to, site);
3538
    if let coercion = isAssignable(self, to, from, site) {
3539
        return setNodeCoercion(self, site, coercion);
3540
    }
3541
    throw emitTypeMismatch(self, site, TypeMismatch {
3542
        expected: to,
3543
        actual: from,
3544
    });
3545
}
3546
3547
/// Check that a type is optional, otherwise throw an error.
3548
unsafe fn checkOptional 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *Type
3549
    throws (ResolveError)
3550
{
3551
    if let case Type::Optional(inner) = try infer(self, node) {
3552
        return inner;
3553
    }
3554
    throw emitError(self, node, ErrorKind::ExpectedOptional);
3555
}
3556
3557
/// Check that a node's type is equal to the expected type.
3558
unsafe fn checkEqual 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type
3559
    throws (ResolveError)
3560
{
3561
    let actualTy = try visit(self, node, expected);
3562
    if actualTy <> expected {
3563
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: actualTy });
3564
    }
3565
    return actualTy;
3566
}
3567
3568
/// Bind an identifier in the given scope.
3569
unsafe fn bindIdent 'arena (
3570
    self: &mut Resolver 'arena,
3571
    name: *[u8],
3572
    owner: *ast::Node,
3573
    data: SymbolData,
3574
    attrs: u32,
3575
    scope: *unsafe mut Scope
3576
) -> *unsafe mut Symbol throws (ResolveError) {
3577
    let sym = allocSymbol(self, data, name, owner, attrs);
3578
    try addSymbolToScope(self, sym, scope, owner);
3579
    setNodeSymbol(self, owner, sym);
3580
3581
    return sym;
3582
}
3583
3584
/// Add a symbol to the given scope.
3585
unsafe fn addSymbolToScope 'arena (self: &mut Resolver 'arena, sym: *unsafe mut Symbol, scope: *unsafe mut Scope, site: *ast::Node) throws (ResolveError) {
3586
    for i in 0..scope.symbolsLen {
3587
        if scope.symbols[i].name == sym.name {
3588
            throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name));
3589
        }
3590
    }
3591
    if scope.symbolsLen >= scope.symbols.len {
3592
        throw emitError(self, site, ErrorKind::SymbolOverflow);
3593
    }
3594
    // Preserve the defining module when importing an existing symbol into
3595
    // another module's scope.
3596
    if sym.moduleId == nil {
3597
        if let modId = scope.moduleId {
3598
            set sym.moduleId = modId;
3599
        }
3600
    }
3601
    set scope.symbols[scope.symbolsLen] = sym;
3602
    set scope.symbolsLen += 1;
3603
}
3604
3605
/// Bind a value identifier in the current scope.
3606
/// Returns `nil` if the identifier is a placeholder (`_`).
3607
unsafe fn bindValueIdent 'arena (
3608
    self: &mut Resolver 'arena,
3609
    ident: *ast::Node,
3610
    owner: *ast::Node,
3611
    type: Type,
3612
    mutable: bool,
3613
    alignment: u32,
3614
    attrs: u32
3615
) -> ?*unsafe mut Symbol throws (ResolveError) {
3616
    if let case ast::NodeValue::Placeholder = ident.value {
3617
        setNodeType(self, owner, type);
3618
        return nil;
3619
    }
3620
    let name = try nodeName(self, ident);
3621
    let data = SymbolData::Value { mutable, alignment, type, addressTaken: false };
3622
    let scope = self.scope;
3623
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
3624
    if ident <> owner {
3625
        setNodeSymbol(self, ident, sym);
3626
    }
3627
    setNodeType(self, owner, type);
3628
    setNodeType(self, ident, type);
3629
3630
    // Track number of local bindings for lowering stage.
3631
    if let owner = self.currentFnNode {
3632
        set self.nodeData.entries[owner.id].localCount += 1;
3633
    }
3634
    return sym;
3635
}
3636
3637
/// Bind a constant identifier in the current scope.
3638
unsafe fn bindConstIdent 'arena (
3639
    self: &mut Resolver 'arena,
3640
    ident: *ast::Node,
3641
    owner: *ast::Node,
3642
    type: Type,
3643
    val: ?ConstValue,
3644
    attrs: u32
3645
) -> *unsafe mut Symbol throws (ResolveError) {
3646
    let name = try nodeName(self, ident);
3647
    let data = SymbolData::Constant { type, value: val };
3648
    let scope = self.scope;
3649
    let sym = try bindIdent(self, name, owner, data, attrs, scope);
3650
    setNodeType(self, owner, type);
3651
    setNodeType(self, ident, type);
3652
3653
    return sym;
3654
}
3655
3656
/// Bind a module identifier in the given scope.
3657
/// This is used when declaring modules with `mod` or
3658
/// importing modules with `use`.
3659
unsafe fn bindModuleIdent 'arena (
3660
    self: &mut Resolver 'arena,
3661
    entry: *module::ModuleEntry,
3662
    scope: *unsafe mut Scope,
3663
    owner: *ast::Node,
3664
    attrs: u32,
3665
    bindingScope: *unsafe mut Scope
3666
) -> *unsafe mut Symbol throws (ResolveError) {
3667
    let data = SymbolData::Module { entry, scope };
3668
    let name = entry.name;
3669
3670
    return try bindIdent(self, name, owner, data, attrs, bindingScope);
3671
}
3672
3673
/// Bind a type identifier in the current scope.
3674
unsafe fn bindTypeIdent 'arena (
3675
    self: &mut Resolver 'arena,
3676
    ident: *ast::Node,
3677
    owner: *ast::Node,
3678
    type: *unsafe mut NominalType,
3679
    attrs: u32
3680
) -> *unsafe mut Symbol throws (ResolveError) {
3681
    let name = try nodeName(self, ident);
3682
    let data = SymbolData::Type(type);
3683
    let scope = self.scope;
3684
    return try bindIdent(self, name, owner, data, attrs, scope);
3685
}
3686
3687
/// Predicate that matches any symbol.
3688
fn isAnySymbol(_sym: &Symbol) -> bool {
3689
    return true;
3690
}
3691
3692
/// Predicate that matches value or constant symbols.
3693
fn isValueSymbol(sym: &Symbol) -> bool {
3694
    if let case SymbolData::Value { .. } = sym.data {
3695
        return true;
3696
    }
3697
    if let case SymbolData::Constant { .. } = sym.data {
3698
        return true;
3699
    }
3700
    return false;
3701
}
3702
3703
/// Predicate that matches type symbols.
3704
fn isTypeSymbol(sym: &Symbol) -> bool {
3705
    if let case SymbolData::Type(_) = sym.data {
3706
        return true;
3707
    }
3708
    return false;
3709
}
3710
3711
/// Find a symbol by name in a specific scope, filtered by a predicate.
3712
unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3713
    for i in 0..scope.symbolsLen {
3714
        let sym = scope.symbols[i];
3715
        if sym.name == name and predicate(sym) {
3716
            return sym;
3717
        }
3718
    }
3719
    return nil;
3720
}
3721
3722
/// Find a symbol by name, traversing scopes upwards, filtered by a predicate.
3723
unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3724
    let mut curr = scope;
3725
    loop {
3726
        if let sym = findInScope(curr, name, predicate) {
3727
            return sym;
3728
        }
3729
        if let parent = curr.parent {
3730
            set curr = parent;
3731
        } else {
3732
            break;
3733
        }
3734
    }
3735
    return nil;
3736
}
3737
3738
/// Find a symbol by name in a specific scope (matches any symbol kind).
3739
export unsafe fn findSymbolInScope(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3740
    return findInScope(scope, name, isAnySymbol);
3741
}
3742
3743
/// Look up a value symbol by name, searching from the given scope outward.
3744
unsafe fn findValueSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3745
    return findInScopeRecursive(scope, name, isValueSymbol);
3746
}
3747
3748
/// Look up a type symbol by name, searching from the given scope outward.
3749
unsafe fn findTypeSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3750
    return findInScopeRecursive(scope, name, isTypeSymbol);
3751
}
3752
3753
/// Like `findValueSymbol`, but finds symbols of any kinds.
3754
unsafe fn findAnySymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
3755
    return findInScopeRecursive(scope, name, isAnySymbol);
3756
}
3757
3758
/// Flatten an identifier or scope access chain into an array of name segments.
3759
/// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`.
3760
/// Return the number of segments written to the buffer.
3761
fn flattenPath 'arena (
3762
    self: &mut Resolver 'arena,
3763
    node: *ast::Node,
3764
    buf: &mut [*[u8]]
3765
) -> u32 throws (ResolveError) {
3766
    let mut out: u32 = 0;
3767
3768
    match node.value {
3769
        case ast::NodeValue::Ident(name) if name.len > 0 => {
3770
            assert buf.len >= 1, "flattenPath: invalid output buffer size";
3771
            set buf[0] = name;
3772
            set out = 1;
3773
        }
3774
        case ast::NodeValue::ScopeAccess(access) => {
3775
            // Recursively flatten parent path.
3776
            let parent = try flattenPath(self, access.parent, buf);
3777
            assert parent < buf.len, "flattenPath: invalid output buffer size";
3778
            let child = try nodeName(self, access.child);
3779
            set buf[parent] = child;
3780
            set out = parent + 1;
3781
        }
3782
        case ast::NodeValue::Super => {
3783
            // `super` is handled by scope adjustment in `checkSuperAccess`.
3784
            // Return empty prefix so the path continues from the next segment.
3785
            set out = 0;
3786
            return out;
3787
        }
3788
        else => {
3789
            // Fallthrough to error.
3790
        }
3791
    }
3792
    if out < 1 {
3793
        throw emitError(self, node, ErrorKind::InvalidIdentifier(node));
3794
    }
3795
    return out;
3796
}
3797
3798
/// Find the module ID for a given scope by walking up the scope chain until
3799
/// we hit the module's scope.
3800
unsafe fn findModuleForScope(scope: *unsafe Scope) -> ?u16 {
3801
    let mut s = scope;
3802
    loop {
3803
        if let id = s.moduleId {
3804
            return id;
3805
        }
3806
        if let parent = s.parent {
3807
            set s = parent;
3808
        } else {
3809
            return nil;
3810
        }
3811
    }
3812
}
3813
3814
/// Return a retained module identity, if its ID is registered.
3815
export fn moduleFor 'arena (self: &Resolver 'arena, id: u16) -> ?*module::ModuleEntry {
3816
    if id as u32 >= self.moduleEntries.len {
3817
        return nil;
3818
    }
3819
    return self.moduleEntries[id as u32];
3820
}
3821
3822
/// Find a retained child identity by its parent and name.
3823
fn findChildModule 'arena (self: &Resolver 'arena, name: *[u8], parentId: u16) -> ?*module::ModuleEntry {
3824
    for child in self.moduleEntries {
3825
        if let entry = child {
3826
            if entry.parent == parentId and mem::eq(entry.name, name) {
3827
                return entry;
3828
            }
3829
        }
3830
    }
3831
    return nil;
3832
}
3833
3834
/// Get the parent module scope for the current module.
3835
/// Returns the scope of the parent module, or `nil` if this is a root module.
3836
fn getParentModuleScope 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Scope throws (ResolveError) {
3837
    let currentMod = moduleFor(self, self.currentMod)
3838
        else throw emitError(self, node, ErrorKind::Internal);
3839
    let parentId = currentMod.parent
3840
        else return nil; // No parent module.
3841
3842
    return self.moduleScopes[parentId as u32];
3843
}
3844
3845
/// Check if a node has `super` at its root (e.g. `super::x` or `super::Union::Variant`).
3846
/// Returns the parent scope and the original node so `flattenPath` can strip `super`.
3847
fn checkSuperAccess 'arena (
3848
    self: &mut Resolver 'arena,
3849
    node: *ast::Node
3850
) -> ?SuperAccessResult throws (ResolveError) {
3851
    // TODO: Maybe we should deal with `super` after the path is flattened.
3852
    if let case ast::NodeValue::ScopeAccess(access) = node.value {
3853
        // Direct super access: `super::x`.
3854
        if let case ast::NodeValue::Super = access.parent.value {
3855
            let parentScope = try getParentModuleScope(self, node)
3856
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
3857
            return SuperAccessResult { scope: parentScope, child: node };
3858
        }
3859
        // Nested super access: `super::x::y`, check if parent path contains `super`.
3860
        if let _ = try checkSuperAccess(self, access.parent) {
3861
            let parentScope = try getParentModuleScope(self, node)
3862
                else throw emitError(self, node, ErrorKind::InvalidModulePath);
3863
            return SuperAccessResult { scope: parentScope, child: node };
3864
        }
3865
    }
3866
    return nil;
3867
}
3868
3869
/// Check symbol visibility from declaration attributes and module identities.
3870
/// A symbol is accessible if:
3871
/// * It has the `export` attribute, OR
3872
/// * It's being accessed from within the module where it was defined.
3873
fn isSymbolVisible(attrs: u32, symModuleId: ?u16, currentModuleId: ?u16) -> bool {
3874
    // Public symbols are visible from anywhere.
3875
    if ast::hasAttribute(attrs, ast::Attribute::Export) {
3876
        return true;
3877
    }
3878
    // In test mode, @test symbols are visible from anywhere
3879
    // so the test runner can reference them.
3880
    if ast::hasAttribute(attrs, ast::Attribute::Test) {
3881
        return true;
3882
    }
3883
    // Private symbols are only visible from the same module.
3884
    return symModuleId == currentModuleId;
3885
}
3886
3887
/// Resolve an access node (eg. `lang::resolver::MAX_ERRORS`) to a symbol,
3888
/// starting from the given scope.
3889
unsafe fn resolveAccess 'arena (
3890
    self: &mut Resolver 'arena,
3891
    node: *ast::Node,
3892
    access: ast::Access,
3893
    scope: *unsafe Scope
3894
) -> *unsafe mut Symbol throws (ResolveError) {
3895
    if let case ast::NodeValue::RegionApply { .. } = access.parent.value {
3896
        let ty = try infer(self, access.parent);
3897
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3898
        try ensureNominalResolved(self, info, access.parent);
3899
        let case NominalType::Union(body) = *info else throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3900
        let name = try nodeName(self, access.child);
3901
        let symbol = try resolveUnionVariantAccess(self, node, access, body, name);
3902
        setNodeType(self, node, ty);
3903
        return symbol;
3904
    }
3905
    // Handle `super` access by adjusting scope and node.
3906
    let mut startScope = scope;
3907
    let mut pathNode = node;
3908
    if let superAccess = try checkSuperAccess(self, node) {
3909
        set startScope = superAccess.scope;
3910
        set pathNode = superAccess.child;
3911
    }
3912
    // TODO: It doesn't make sense that `flattenPath` handles identifiers and scope access,
3913
    // while this function requires a scope access.
3914
    let mut buffer: [*[u8]; 32] = undefined;
3915
    let pathLen = try flattenPath(self, pathNode, &mut buffer[..]);
3916
3917
    return try resolvePath(self, node, access, &buffer[..pathLen], startScope);
3918
}
3919
3920
/// Resolve a path (eg. ["lang", "resolver", "MAX_ERRORS"]) to a symbol,
3921
/// starting from the given scope.
3922
unsafe fn resolvePath 'arena (
3923
    self: &mut Resolver 'arena,
3924
    node: *ast::Node,
3925
    access: ast::Access,
3926
    path: &[*[u8]],
3927
    scope: *unsafe Scope
3928
) -> *unsafe mut Symbol throws (ResolveError) {
3929
    assert path.len <> 0, "resolvePath: empty path";
3930
    // Start by finding the root of the path.
3931
    let root = path[0];
3932
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
3933
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
3934
3935
    // Check visibility for symbol.
3936
    if not isSymbolVisible(sym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) {
3937
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
3938
    }
3939
    // End condition.
3940
    if path.len == 1 {
3941
        return sym;
3942
    }
3943
    // Otherwise, we need to enter the next scope with the path suffix.
3944
    match sym.data {
3945
        case SymbolData::Module { scope, .. } => {
3946
            return try resolvePath(self, node, access, &path[1..], scope);
3947
        }
3948
        case SymbolData::Type(ty) => {
3949
            // Lazily resolve union body if not yet done.
3950
            try ensureNominalResolved(self, ty, node);
3951
3952
            if let case NominalType::Union(unionType) = *ty {
3953
                // TODO: Recurse with variant so we consolidate everything.
3954
                if path.len > 2 {
3955
                    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3956
                }
3957
                let variantName = path[1];
3958
                let variantSym = try resolveUnionVariantAccess(
3959
                    self, node, access, unionType, variantName
3960
                );
3961
                // TODO: This shouldn't be here.
3962
                setNodeType(self, node, Type::Nominal(ty));
3963
                return variantSym;
3964
            }
3965
        }
3966
        else => {} // Fallthrough.
3967
    }
3968
    throw emitError(self, node, ErrorKind::InvalidScopeAccess);
3969
}
3970
3971
/// Resolve a module path (e.g., `foo::bar::baz`) to a module entry and scope.
3972
/// This traverses the module hierarchy, checking visibility at each step.
3973
unsafe fn resolveModulePath 'arena (
3974
    self: &mut Resolver 'arena,
3975
    module: *ast::Node
3976
) -> ResolvedModule throws (ResolveError) {
3977
    let mut startScope = self.scope;
3978
    let mut pathNode = module;
3979
3980
    // Handle `super` access.
3981
    if let superAccess = try checkSuperAccess(self, module) {
3982
        set startScope = superAccess.scope;
3983
        set pathNode = superAccess.child;
3984
    }
3985
    let mut pathBuf: [*[u8]; 16] = undefined;
3986
    let pathLen = try flattenPath(self, pathNode, &mut pathBuf[..]);
3987
    if pathLen == 0 {
3988
        throw emitError(self, module, ErrorKind::UnresolvedSymbol(""));
3989
    }
3990
    let parentName = pathBuf[0];
3991
3992
    // First, check if this is a sub-module of the start scope.
3993
    if let sym = findSymbolInScope(startScope, parentName) {
3994
        return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
3995
    }
3996
    // Not a sub-module, so look in the global scope for a package root.
3997
    let sym = findSymbolInScope(self.pkgScope, parentName)
3998
        else throw emitError(self, module, ErrorKind::UnresolvedSymbol(parentName));
3999
4000
    return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym);
4001
}
4002
4003
/// Recursively resolve the remaining path segments by traversing child modules.
4004
unsafe fn resolveModulePathRecursive 'arena (
4005
    self: &mut Resolver 'arena,
4006
    node: *ast::Node,
4007
    path: &[*[u8]],
4008
    sym: *unsafe Symbol
4009
) -> ResolvedModule throws (ResolveError) {
4010
    let case SymbolData::Module { entry, scope } = sym.data
4011
        else throw emitError(self, node, ErrorKind::Internal);
4012
4013
    if path.len == 0 {
4014
        return ResolvedModule { entry, scope };
4015
    }
4016
    let childName = path[0];
4017
    let childSym = findSymbolInScope(scope, childName)
4018
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
4019
4020
    if not isSymbolVisible(childSym.attrs, findModuleForScope(scope), findModuleForScope(self.scope)) {
4021
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(childName));
4022
    }
4023
    return try resolveModulePathRecursive(
4024
        self,
4025
        node,
4026
        &path[1..],
4027
        childSym
4028
    );
4029
}
4030
4031
/// Resolve a type name, which could be an identifier or scoped path.
4032
unsafe fn resolveTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe NominalType throws (ResolveError) {
4033
    match node.value {
4034
        case ast::NodeValue::Ident(name) => {
4035
            let sym = findTypeSymbol(self.scope, name)
4036
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
4037
            let case SymbolData::Type(ty) = sym.data
4038
                else throw emitError(self, node, ErrorKind::Internal);
4039
4040
            setNodeSymbol(self, node, sym);
4041
4042
            return ty;
4043
        }
4044
        case ast::NodeValue::ScopeAccess(access) => {
4045
            let scope = self.scope;
4046
            let sym = try resolveAccess(self, node, access, scope);
4047
            let case SymbolData::Type(ty) = sym.data
4048
                else throw emitError(self, node, ErrorKind::Internal);
4049
4050
            setNodeSymbol(self, node, sym);
4051
4052
            return ty;
4053
        }
4054
        else => panic "resolveTypeName: unsupported node value",
4055
    }
4056
}
4057
4058
/// Visit a top-level declaration in the declaration phase.
4059
/// This binds all names and analyzes signatures, types, and initializers.
4060
/// Function bodies are deferred to the definition phase.
4061
///
4062
/// Nb. User-defined types are already handled by this point.
4063
unsafe fn visitDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
4064
    match node.value {
4065
        case ast::NodeValue::FnDecl(_),
4066
             ast::NodeValue::ConstDecl(_),
4067
             ast::NodeValue::Mod(_),
4068
             ast::NodeValue::Use(_) => {
4069
            // Handled in previous passes.
4070
        }
4071
        case ast::NodeValue::StaticDecl(_) => {
4072
            try infer(self, node);
4073
        }
4074
        case ast::NodeValue::InstanceDecl { traitName, targetType, regions, methods } => {
4075
            try resolveInstanceDecl(self, node, traitName, targetType, regions, methods);
4076
        }
4077
        case ast::NodeValue::MethodDecl {
4078
            ..
4079
        } => {
4080
            try resolveMethodDecl(self, node);
4081
        }
4082
        else => {
4083
            // Ignore non-declaration nodes.
4084
        }
4085
    }
4086
}
4087
4088
/// Require an unsafe function or block.
4089
fn requireUnsafe 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
4090
    if not self.inUnsafeContext {
4091
        throw emitError(self, node, ErrorKind::UnsafeOperation);
4092
    }
4093
}
4094
4095
/// Require an unsafe context for any access to an unsafe static.
4096
fn checkStaticAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, sym: &Symbol)
4097
    throws (ResolveError)
4098
{
4099
    if let case ast::NodeValue::StaticDecl(_) = sym.node.value {
4100
        if ast::hasAttribute(sym.attrs, ast::Attribute::Unsafe) {
4101
            try requireUnsafe(self, node);
4102
        }
4103
    }
4104
}
4105
4106
/// Reject calls from safe code through unsafe function types.
4107
fn checkUnsafeCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, info: *FnType)
4108
    throws (ResolveError)
4109
{
4110
    if info.isUnsafe and not self.inUnsafeContext {
4111
        throw emitError(self, node, ErrorKind::UnsafeCall);
4112
    }
4113
}
4114
4115
/// Visit a top-level definition, recursing into sub-modules.
4116
unsafe fn visitDef 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
4117
    match node.value {
4118
        case ast::NodeValue::FnDecl(decl) => {
4119
            try resolveFnDeclBody(self, node, decl) catch {
4120
                return;
4121
            };
4122
        }
4123
        case ast::NodeValue::Mod(decl) => {
4124
            let modName = try nodeName(self, decl.name);
4125
            if not shouldAnalyzeModule(self, decl.attrs, modName) {
4126
                return;
4127
            }
4128
            let submod = try enterSubModule(self, modName, node);
4129
            let case ast::NodeValue::Block(block) = submod.root.value
4130
                else panic "visitDef: expected block for module root";
4131
            try resolveModuleDefs(self, &block) catch e {
4132
                exitModuleScope(self, submod);
4133
                throw e;
4134
            };
4135
            exitModuleScope(self, submod);
4136
        }
4137
        case ast::NodeValue::RecordDecl(_),
4138
             ast::NodeValue::UnionDecl(_),
4139
             ast::NodeValue::Use(_),
4140
             ast::NodeValue::TraitDecl { .. } => {
4141
            // Skip: already analyzed in declaration phase.
4142
        }
4143
        case ast::NodeValue::InstanceDecl { methods, .. } => {
4144
            try resolveInstanceMethodBodies(self, methods);
4145
        }
4146
        case ast::NodeValue::MethodDecl {
4147
            ..
4148
        } => {
4149
            try resolveMethodBody(self, node);
4150
        }
4151
        else => {
4152
            // FIXME: This allows module-level statements that should
4153
            // normally only be valid inside function bodies. We currently
4154
            // need this because of how tests are written, but it should
4155
            // be eventually removed.
4156
            try infer(self, node) catch {
4157
                return;
4158
            };
4159
        }
4160
    }
4161
}
4162
4163
/// Try to infer a node's type.
4164
unsafe fn infer 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
4165
    return try visit(self, node, Type::Unknown);
4166
}
4167
4168
/// Permit named reference dependencies and direct call-scoped or local references.
4169
unsafe fn validateValueTypeReferences 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
4170
    throws (ResolveError)
4171
{
4172
    try validateRegionDependencies(self, node, ty);
4173
    if let case Type::Fn(info) = ty; info.regions == nil {
4174
        return;
4175
    }
4176
    if isRefType(ty) {
4177
        if let case Type::Pointer { target, .. } = ty {
4178
            if containsUnscopedRef(*target) {
4179
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
4180
            }
4181
        } else if let case Type::Slice { item, .. } = ty {
4182
            if containsUnscopedRef(*item) {
4183
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
4184
            }
4185
        }
4186
    } else if containsUnscopedRef(ty) {
4187
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
4188
    }
4189
}
4190
4191
/// Require a type that may be stored or escape a call.
4192
unsafe fn ensureStorableType 'arena (self: &mut Resolver 'arena, node: *ast::Node, ty: Type)
4193
    throws (ResolveError)
4194
{
4195
    try validateRegionDependencies(self, node, ty);
4196
    if containsUnscopedRef(ty) {
4197
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
4198
    }
4199
}
4200
4201
/// Resolve a type signature node.
4202
unsafe fn resolveValueType 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> Type throws (ResolveError) {
4203
    let ty = try visit(self, node, Type::Unknown);
4204
    if let case Type::Nominal(info) = ty {
4205
        try requireNominalArguments(self, info, node);
4206
    }
4207
    // Opaque value types are not allowed.
4208
    if ty == Type::Opaque {
4209
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
4210
    }
4211
    try validateValueTypeReferences(self, node, ty);
4212
    return ty;
4213
}
4214
4215
/// Analyze a node's type and check that it can be assigned to the expected type.
4216
unsafe fn checkAssignable 'arena (self: &mut Resolver 'arena, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
4217
    let actual = try visit(self, node, expected);
4218
    let _ = try expectAssignable(self, expected, actual, node);
4219
    if isRefType(expected) and isMutablePointerLike(expected) and isMutablePointerLike(actual) {
4220
        if not try canMutateThrough(self, node) {
4221
            throw emitError(self, node, ErrorKind::ImmutableBinding);
4222
        }
4223
    }
4224
    return actual;
4225
}
4226
4227
/// Analyze a node and propagate the resolved type.
4228
/// The `hint` parameter provides type context for inference and validation.
4229
/// When `nil`, the type must be inferred from the expression itself.
4230
unsafe fn visit 'arena (self: &mut Resolver 'arena, node: *ast::Node, hint: Type) -> Type
4231
    throws (ResolveError)
4232
{
4233
    if let ty = typeFor(self, node) {
4234
        // An optional context completes a nil expression's storage type.
4235
        if ty <> Type::Nil or not isOptionalType(hint) {
4236
            return ty;
4237
        }
4238
    }
4239
    match node.value {
4240
        case ast::NodeValue::Ident(name) => {
4241
            let sym = findAnySymbol(self.scope, name)
4242
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
4243
            try checkStaticAccess(self, node, sym);
4244
            setNodeSymbol(self, node, sym);
4245
            match sym.data {
4246
                case SymbolData::Value { type, .. } =>
4247
                    return setNodeType(self, node, type),
4248
                case SymbolData::Constant { type, value } => {
4249
                    if let val = value {
4250
                        setNodeConstValue(self, node, val);
4251
                    }
4252
                    return setNodeType(self, node, type);
4253
                },
4254
                case SymbolData::Type(t) =>
4255
                    return setNodeType(self, node, Type::Nominal(hintedNominal(t, hint))),
4256
                case SymbolData::Variant { .. } =>
4257
                    return Type::Void,
4258
                case SymbolData::Module { .. } =>
4259
                    throw emitError(self, node, ErrorKind::UnexpectedModuleName),
4260
                case SymbolData::Trait(_) =>
4261
                    throw emitError(self, node, ErrorKind::UnexpectedTraitName),
4262
            }
4263
        },
4264
        case ast::NodeValue::Call(call) => return try resolveCall(self, node, call, CallCtx::Normal, hint),
4265
        case ast::NodeValue::FieldAccess(access) => return try resolveFieldAccess(self, node, access),
4266
        case ast::NodeValue::BinOp(binop) => return try resolveBinOp(self, node, binop),
4267
        case ast::NodeValue::RegionBlock { region, bindings, body, isSession } => {
4268
            if isSession {
4269
                return try resolveSessionBlock(self, node, region, bindings, body);
4270
            }
4271
            return try resolveBorrowBlock(self, node, region, bindings, body);
4272
        }
4273
        case ast::NodeValue::Block(block) => return try resolveBlock(self, node, block),
4274
        case ast::NodeValue::Let(decl) => return try resolveLet(self, node, decl),
4275
        case ast::NodeValue::ConstDecl(decl) => return try resolveConstOrStatic(
4276
            self, node, decl.ident, decl.type, decl.value, decl.attrs, true
4277
        ),
4278
        case ast::NodeValue::StaticDecl(decl) => return try resolveConstOrStatic(
4279
            self, node, decl.ident, decl.type, decl.value, decl.attrs, false
4280
        ),
4281
        case ast::NodeValue::FnParam(param) => return try resolveFnParam(self, node, param),
4282
        case ast::NodeValue::If(cond) => return try resolveIf(self, node, cond),
4283
        case ast::NodeValue::CondExpr(cond) => return try resolveCondExpr(self, node, cond, hint),
4284
        case ast::NodeValue::IfLet(cond) => return try resolveIfLet(self, node, cond),
4285
        case ast::NodeValue::While(loopNode) => return try resolveWhile(self, node, loopNode),
4286
        case ast::NodeValue::WhileLet(loopNode) => return try resolveWhileLet(self, node, loopNode),
4287
        case ast::NodeValue::For(loopNode) => return try resolveFor(self, node, loopNode),
4288
        case ast::NodeValue::Loop { body } => {
4289
            let loopType = try visitLoop(self, body);
4290
            return setNodeType(self, node, loopType);
4291
        },
4292
        case ast::NodeValue::Break, ast::NodeValue::Continue => return try resolveLoopControl(self, node),
4293
        case ast::NodeValue::Match(sw) => return try resolveMatch(self, node, sw),
4294
        case ast::NodeValue::MatchProng(_) => panic "visit: `MatchProng` not handled here",
4295
        case ast::NodeValue::LetElse(letElse) => return try resolveLetElse(self, node, letElse),
4296
        case ast::NodeValue::BuiltinCall { kind, args } => return try resolveBuiltinCall(self, node, kind, args),
4297
        case ast::NodeValue::Assign(assign) => return try resolveAssign(self, node, assign),
4298
        case ast::NodeValue::RecordLit(lit) => return try resolveRecordLit(self, node, lit, hint),
4299
        case ast::NodeValue::ArrayLit(items) => return try resolveArrayLit(self, node, items, hint),
4300
        case ast::NodeValue::ArrayRepeatLit(lit) => return try resolveArrayRepeat(self, node, lit, hint),
4301
        case ast::NodeValue::Subscript { container, index } => return try resolveSubscript(self, node, container, index),
4302
        case ast::NodeValue::ScopeAccess(access) => return try resolveScopeAccess(self, node, access, hint),
4303
        case ast::NodeValue::AddressOf(addr) => return try resolveAddressOf(self, node, addr, hint),
4304
        case ast::NodeValue::Deref(target) => return try resolveDeref(self, node, target, hint),
4305
        case ast::NodeValue::As(expr) => return try resolveAs(self, node, expr),
4306
        case ast::NodeValue::Range(range) => return try resolveRange(self, node, range),
4307
        case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint),
4308
        case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value),
4309
        case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr),
4310
        case ast::NodeValue::Panic { message } => {
4311
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
4312
                class: types::PointerClass::Owned,
4313
                item: allocType(self, Type::U8),
4314
                mutable: false,
4315
            });
4316
            return setNodeType(self, node, Type::Never);
4317
        },
4318
        case ast::NodeValue::Assert { condition, message } => {
4319
            try visit(self, condition, Type::Bool);
4320
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
4321
                class: types::PointerClass::Owned,
4322
                item: allocType(self, Type::U8),
4323
                mutable: false,
4324
            });
4325
            return setNodeType(self, node, Type::Void);
4326
        },
4327
        case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop),
4328
        case ast::NodeValue::ExprStmt(expr) => {
4329
            // Pass `Void` as expected type to indicate value is discarded.
4330
            let exprTy = try visit(self, expr, Type::Void);
4331
            return setNodeType(self, node, Type::Never if exprTy == Type::Never else Type::Void);
4332
        },
4333
        case ast::NodeValue::RegionApply { value, regions } =>
4334
            return try resolveRegionApply(self, node, value, regions),
4335
        case ast::NodeValue::TypeSig(sig) => return try inferTypeSig(self, node, sig),
4336
        case ast::NodeValue::Super => {
4337
            // `super` by itself is invalid, must be used in scope access.
4338
            throw emitError(self, node, ErrorKind::InvalidModulePath);
4339
        },
4340
        case ast::NodeValue::Nil => {
4341
            // Use the hint type if it's an optional, otherwise fall back to `Nil`.
4342
            if let case Type::Optional(_) = hint {
4343
                return setNodeType(self, node, hint);
4344
            }
4345
            return setNodeType(self, node, Type::Nil);
4346
        },
4347
        case ast::NodeValue::Undef => {
4348
            try requireUnsafe(self, node);
4349
            return setNodeType(self, node, Type::Undefined);
4350
        },
4351
        case ast::NodeValue::Bool(value) => {
4352
            setNodeConstValue(self, node, ConstValue::Bool(value));
4353
            return setNodeType(self, node, Type::Bool);
4354
        }
4355
        case ast::NodeValue::Char(value) => {
4356
            setNodeConstValue(self, node, ConstValue::Char(value));
4357
            return setNodeType(self, node, Type::U8);
4358
        }
4359
        case ast::NodeValue::String(text) => {
4360
            setNodeConstValue(self, node, ConstValue::String(text));
4361
            let byteTy = allocType(self, Type::U8);
4362
            let sliceTy = allocType(self, Type::Slice {
4363
                class: types::PointerClass::Owned,
4364
                item: byteTy,
4365
                mutable: false,
4366
            });
4367
            return setNodeType(self, node, *sliceTy);
4368
        },
4369
        case ast::NodeValue::Number(lit) => {
4370
            setNodeConstValue(self, node, ConstValue::Int(ConstInt {
4371
                magnitude: lit.magnitude,
4372
                bits: 64,
4373
                signed: false,
4374
                negative: false,
4375
            }));
4376
            return setNodeType(self, node, Type::Int);
4377
        },
4378
        case ast::NodeValue::Placeholder => {
4379
            throw emitError(self, node, ErrorKind::PlaceholderExpression);
4380
        },
4381
        else => {
4382
            throw emitError(self, node, ErrorKind::UnexpectedNode(node));
4383
        }
4384
    }
4385
}
4386
4387
/// Visit an optional node when present.
4388
unsafe fn visitOptional 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, hint: Type) -> ?Type
4389
    throws (ResolveError)
4390
{
4391
    if let n = node {
4392
        return try visit(self, n, hint);
4393
    }
4394
    return nil;
4395
}
4396
4397
/// Visit every node contained in a list, returning the last resolved type.
4398
unsafe fn visitList 'arena (self: &mut Resolver 'arena, list: *[*ast::Node]) -> Type
4399
    throws (ResolveError)
4400
{
4401
    let mut diverges = false;
4402
    for item in list {
4403
        if try infer(self, item) == Type::Never {
4404
            set diverges = true;
4405
        }
4406
    }
4407
    if diverges {
4408
        return Type::Never;
4409
    }
4410
    return Type::Void;
4411
}
4412
4413
/// Collect attribute flags applied to a declaration.
4414
fn resolveAttributes(attrs: ?ast::Attributes) -> u32 {
4415
    let list = attrs else return 0;
4416
    let mut mask: u32 = 0;
4417
4418
    for node in list.list {
4419
        let case ast::NodeValue::Attribute(attr) = node.value
4420
            else panic "resolveAttributes: invalid attribute node";
4421
        set mask |= (attr as u32);
4422
    }
4423
    return mask;
4424
}
4425
4426
/// Ensure the `default` attribute is only applied to functions.
4427
fn ensureDefaultAttrNotAllowed 'arena (self: &mut Resolver 'arena, node: *ast::Node, attrs: u32)
4428
    throws (ResolveError)
4429
{
4430
    let defaultBit = ast::Attribute::Default as u32;
4431
    if (attrs & defaultBit) <> 0 {
4432
        throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn);
4433
    }
4434
}
4435
4436
/// Analyze a block node, allocating a nested lexical scope.
4437
unsafe fn resolveBlock 'arena (self: &mut Resolver 'arena, node: *ast::Node, block: ast::Block) -> Type
4438
    throws (ResolveError)
4439
{
4440
    enterScope(self, node);
4441
    let wasUnsafe = self.inUnsafeContext;
4442
    set self.inUnsafeContext = wasUnsafe or block.isUnsafe;
4443
    let blockTy = try visitList(self, block.statements) catch {
4444
        // One of the statements in the block failed analysis. We simply proceed
4445
        // without checking the rest of the block statements. Return `Never` to
4446
        // avoid spurious `FnMissingReturn` errors.
4447
        exitScope(self);
4448
        set self.inUnsafeContext = wasUnsafe;
4449
        return setNodeType(self, node, Type::Never);
4450
    };
4451
    exitScope(self);
4452
    set self.inUnsafeContext = wasUnsafe;
4453
4454
    return setNodeType(self, node, blockTy);
4455
}
4456
4457
/// Introduce a concrete region under an explicit parent or enclosing region block.
4458
unsafe fn borrowRegion 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *RegionScope throws (ResolveError) {
4459
    let case ast::NodeValue::Region { name, parent } = node.value
4460
        else panic "borrowRegion: invalid region node";
4461
    if findRegion(self.regionScope, name) <> nil {
4462
        throw emitError(self, node, ErrorKind::DuplicateBinding(name));
4463
    }
4464
    let mut enclosing: ?*unsafe types::Region = nil;
4465
    let mut current = self.regionScope;
4466
    while let scope = current {
4467
        for region in scope.entries {
4468
            if region.origin == types::RegionOrigin::Block {
4469
                set enclosing = region;
4470
                break;
4471
            }
4472
        }
4473
        if enclosing <> nil {
4474
            break;
4475
        }
4476
        set current = scope.parent;
4477
    }
4478
    if let parentNode = parent {
4479
        set enclosing = try resolveRegion(self, parentNode);
4480
    }
4481
    let region = try! alloc::allocRaw(self.arena, @sizeOf(types::Region), @alignOf(types::Region))
4482
        as *unsafe mut types::Region;
4483
    set *region = types::Region { id: node.id, origin: types::RegionOrigin::Block, name, parent: enclosing };
4484
    let entries = try! alloc::allocRawSlice(
4485
        self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1
4486
    ) as *unsafe mut [*unsafe mut types::Region];
4487
    set entries[0] = region;
4488
    let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope;
4489
    set *scope = RegionScope {
4490
        declarations: RegionDeclarations::Block(node),
4491
        entries,
4492
        parent: self.regionScope,
4493
    };
4494
    return scope;
4495
}
4496
4497
/// Qualify an existing-place borrow with its block's region.
4498
///
4499
/// A permission cell loan deliberately joins independent storage and
4500
/// permission regions. Its paired authority witness proves the lexical
4501
/// shortening that the single-parent region graph cannot represent.
4502
unsafe fn qualifyBlockBorrow 'arena (
4503
    self: &mut Resolver 'arena, node: *ast::Node, ty: Type,
4504
    region: *unsafe types::Region, hasPermissionWitness: bool
4505
) -> Type throws (ResolveError) {
4506
    let mut class = types::PointerClass::Ref;
4507
    match ty {
4508
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
4509
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
4510
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
4511
        else => throw emitError(self, node, ErrorKind::RefBinding),
4512
    }
4513
    if let case types::PointerClass::Region(source) = class {
4514
        if not hasPermissionWitness and not types::regionContains(source, region) {
4515
            throw emitError(self, node, ErrorKind::RegionParent(region.name));
4516
        }
4517
    }
4518
    match ty {
4519
        case Type::Cell { permission, payload, .. } =>
4520
            return Type::Cell {
4521
                class: types::PointerClass::Region(region), permission, payload,
4522
            },
4523
        case Type::Pointer { target, mutable, .. } =>
4524
            return Type::Pointer { class: types::PointerClass::Region(region), target, mutable },
4525
        case Type::Slice { item, mutable, .. } =>
4526
            return Type::Slice { class: types::PointerClass::Region(region), item, mutable },
4527
        else => throw emitError(self, node, ErrorKind::RefBinding),
4528
    }
4529
}
4530
4531
/// Check source places before publishing the region's bindings to its body.
4532
unsafe fn resolveBorrowBlock 'arena (
4533
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
4534
    bindings: *[*ast::Node], body: *ast::Node
4535
) -> Type throws (ResolveError) {
4536
    if self.currentFn == nil {
4537
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
4538
    }
4539
    let scope = try borrowRegion(self, regionNode);
4540
    let region = scope.entries[0];
4541
    let mut authorityPermissions: [?*unsafe types::Region; MAX_REGIONAL_LOANS] =
4542
        [nil; MAX_REGIONAL_LOANS];
4543
    let mut authorityExclusive: [bool; MAX_REGIONAL_LOANS] =
4544
        [false; MAX_REGIONAL_LOANS];
4545
    let mut authorityLen: u32 = 0;
4546
    let mut payloadPermissions: [?*unsafe types::Region; MAX_REGIONAL_LOANS] =
4547
        [nil; MAX_REGIONAL_LOANS];
4548
    let mut payloadExclusive: [bool; MAX_REGIONAL_LOANS] =
4549
        [false; MAX_REGIONAL_LOANS];
4550
    let mut payloadLen: u32 = 0;
4551
    for bindingNode in bindings {
4552
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4553
            else panic "resolveBorrowBlock: invalid binding node";
4554
        let case ast::NodeValue::AddressOf(address) = binding.value.value
4555
            else panic "resolveBorrowBlock: invalid source node";
4556
        try infer(self, binding.value);
4557
        if not ast::isPlaceExpr(address.target) or borrowPlace(self, address.target).root == nil {
4558
            throw emitError(self, binding.value, ErrorKind::RefBinding);
4559
        }
4560
        if address.kind == ast::AddressKind::Cell {
4561
            continue;
4562
        }
4563
        if let cellTy = try inferCellPayload(self, address.target) {
4564
            if let case Type::Cell { permission: controlled, .. } = cellTy {
4565
                if let permission = controlled {
4566
                    if payloadLen >= MAX_REGIONAL_LOANS {
4567
                        throw emitError(self, binding.value, ErrorKind::RegionalLoanOverflow);
4568
                    }
4569
                    set payloadPermissions[payloadLen] = permission;
4570
                    set payloadExclusive[payloadLen] =
4571
                        address.kind == ast::AddressKind::Mutable;
4572
                    set payloadLen += 1;
4573
                    continue;
4574
                }
4575
            }
4576
        }
4577
        if let case ast::NodeValue::Deref(source) = address.target.value {
4578
            let sourceTy = try infer(self, source);
4579
            if let case Type::Pointer {
4580
                class: types::PointerClass::Region(permission),
4581
                mutable: true,
4582
                ..
4583
            } = sourceTy {
4584
                if authorityLen >= MAX_REGIONAL_LOANS {
4585
                    throw emitError(self, binding.value, ErrorKind::RegionalLoanOverflow);
4586
                }
4587
                set authorityPermissions[authorityLen] = permission;
4588
                set authorityExclusive[authorityLen] =
4589
                    address.kind == ast::AddressKind::Mutable;
4590
                set authorityLen += 1;
4591
            }
4592
        }
4593
    }
4594
    for bindingNode in bindings {
4595
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4596
            else panic "resolveBorrowBlock: invalid binding node";
4597
        let case ast::NodeValue::AddressOf(address) = binding.value.value
4598
            else panic "resolveBorrowBlock: invalid source node";
4599
        let mut permission: ?*unsafe types::Region = nil;
4600
        let mut isPayload = false;
4601
        if address.kind <> ast::AddressKind::Cell {
4602
            if let cellTy = try inferCellPayload(self, address.target) {
4603
                if let case Type::Cell { permission: controlled, .. } = cellTy {
4604
                    set permission = controlled;
4605
                    set isPayload = controlled <> nil;
4606
                }
4607
            }
4608
            if permission == nil {
4609
                if let case ast::NodeValue::Deref(source) = address.target.value {
4610
                    let sourceTy = try infer(self, source);
4611
                    if let case Type::Pointer {
4612
                        class: types::PointerClass::Region(controlled),
4613
                        mutable: true,
4614
                        ..
4615
                    } = sourceTy {
4616
                        set permission = controlled;
4617
                    }
4618
                }
4619
            }
4620
        }
4621
        let mut hasPermissionWitness = false;
4622
        if let controlled = permission {
4623
            let exclusive = address.kind == ast::AddressKind::Mutable;
4624
            if isPayload {
4625
                for i in 0..authorityLen {
4626
                    let candidate = authorityPermissions[i]
4627
                        else panic "resolveBorrowBlock: missing authority permission";
4628
                    if candidate.id == controlled.id and authorityExclusive[i] == exclusive {
4629
                        set hasPermissionWitness = true;
4630
                        break;
4631
                    }
4632
                }
4633
            } else {
4634
                for i in 0..payloadLen {
4635
                    let candidate = payloadPermissions[i]
4636
                        else panic "resolveBorrowBlock: missing payload permission";
4637
                    if candidate.id == controlled.id and payloadExclusive[i] == exclusive {
4638
                        set hasPermissionWitness = true;
4639
                        break;
4640
                    }
4641
                }
4642
            }
4643
        }
4644
        let ty = try infer(self, binding.value);
4645
        let qualified = try qualifyBlockBorrow(
4646
            self, binding.value, ty, region, hasPermissionWitness,
4647
        );
4648
        setNodeType(self, binding.value, qualified);
4649
    }
4650
    let previous = self.regionScope;
4651
    set self.regionScope = scope;
4652
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4653
    enterScope(self, node);
4654
    let result = try resolveBorrowBody(self, bindings, body) catch error {
4655
        exitScope(self);
4656
        set self.regionScope = previous;
4657
        throw error;
4658
    };
4659
    exitScope(self);
4660
    set self.regionScope = previous;
4661
    return setNodeType(self, node, result);
4662
}
4663
4664
/// Bind a checked region's source references and resolve its statement body.
4665
unsafe fn resolveBorrowBody 'arena (self: &mut Resolver 'arena, bindings: *[*ast::Node], body: *ast::Node) -> Type
4666
    throws (ResolveError)
4667
{
4668
    for bindingNode in bindings {
4669
        let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4670
            else panic "resolveBorrowBody: invalid binding node";
4671
        try resolveLet(self, bindingNode, ast::borrowBinding(binding));
4672
    }
4673
    return try infer(self, body);
4674
}
4675
4676
/// Find a declaration by spelling when the name is not interned.
4677
unsafe fn findSpelledSymbol(scope: *unsafe Scope, name: *[u8]) -> ?*unsafe mut Symbol {
4678
    for i in 0..scope.symbolsLen {
4679
        let symbol = scope.symbols[i];
4680
        if mem::eq(symbol.name, name) {
4681
            return symbol;
4682
        }
4683
    }
4684
    return nil;
4685
}
4686
4687
/// Look up a compiler-known declaration in the standard allocation module.
4688
unsafe fn allocationSymbol 'arena (self: &Resolver 'arena, name: *[u8]) -> ?*unsafe mut Symbol {
4689
    let mut scope = self.pkgScope;
4690
    for segment in ["std", "lang", "alloc"] {
4691
        let symbol = findSpelledSymbol(scope, segment) else return nil;
4692
        let case SymbolData::Module { scope: child, .. } = symbol.data else return nil;
4693
        set scope = child;
4694
    }
4695
    return findSpelledSymbol(scope, name);
4696
}
4697
4698
/// Bind an allocation interface while retaining the source arena until region exit.
4699
unsafe fn resolveSessionBlock 'arena (
4700
    self: &mut Resolver 'arena, node: *ast::Node, regionNode: *ast::Node,
4701
    bindings: *[*ast::Node], body: *ast::Node
4702
) -> Type throws (ResolveError) {
4703
    if self.currentFn == nil or bindings.len <> 1 {
4704
        throw emitError(self, node, ErrorKind::InvalidSessionSource);
4705
    }
4706
    let bindingNode = bindings[0];
4707
    let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4708
        else panic "resolveSessionBlock: invalid binding";
4709
    let case ast::NodeValue::AddressOf(address) = binding.value.value
4710
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4711
    let ty = try infer(self, binding.value);
4712
    let case Type::Pointer { target, .. } = ty
4713
        else throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4714
    let allocTrait = allocationSymbol(self, "Alloc")
4715
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4716
    let case SymbolData::Trait(allocInfo) = allocTrait.data
4717
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4718
    let allocModule = moduleIdForSymbol(self, allocTrait)
4719
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4720
    let mut allocInstance: ?*unsafe InstanceEntry = nil;
4721
    for i in 0..self.instancesLen {
4722
        let candidate: *unsafe InstanceEntry = &self.instances[i];
4723
        if candidate.traitType.moduleId == allocModule and mem::eq(candidate.traitType.name, allocInfo.name)
4724
            and erasedTypesEqual(candidate.concreteType, *target)
4725
        {
4726
            set allocInstance = candidate;
4727
            break;
4728
        }
4729
    }
4730
    let selected = allocInstance
4731
        else throw emitError(self, node, ErrorKind::InvalidSessionSource);
4732
    if address.kind <> ast::AddressKind::Mutable or not ast::isPlaceExpr(address.target)
4733
        or borrowPlace(self, address.target).root == nil
4734
    {
4735
        throw emitError(self, binding.value, ErrorKind::InvalidSessionSource);
4736
    }
4737
    let _ = setNodeCoercion(self, binding.value, Coercion::TraitObject {
4738
        traitInfo: allocInfo, inst: selected,
4739
    });
4740
    let scope = try borrowRegion(self, regionNode);
4741
    let region = scope.entries[0];
4742
    if let sourceRegion = referenceRegion(ty) {
4743
        set region.parent = sourceRegion;
4744
    }
4745
    setNodeType(self, binding.value, try qualifyBlockBorrow(self, binding.value, ty, region, false));
4746
    let previous = self.regionScope;
4747
    set self.regionScope = scope;
4748
    set self.nodeData.entries[node.id].extra = NodeExtra::Regions(scope);
4749
    enterScope(self, node);
4750
    let result = try resolveSessionBody(self, bindingNode, binding, region, body) catch error {
4751
        exitScope(self);
4752
        set self.regionScope = previous;
4753
        throw error;
4754
    };
4755
    exitScope(self);
4756
    set self.regionScope = previous;
4757
    return setNodeType(self, node, result);
4758
}
4759
4760
/// Introduce the opaque session value and check its body.
4761
unsafe fn resolveSessionBody 'arena (
4762
    self: &mut Resolver 'arena, node: *ast::Node, binding: ast::Arg,
4763
    region: *unsafe types::Region, body: *ast::Node
4764
) -> Type throws (ResolveError) {
4765
    let ident = binding.label else panic "resolveSessionBody: missing binding name";
4766
    let _ = try bindValueIdent(self, ident, node, Type::Session(region), false, 0, 0);
4767
    return try infer(self, body);
4768
}
4769
4770
/// Analyze a `let` declaration and bind its identifier.
4771
unsafe fn resolveLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Let) -> Type
4772
    throws (ResolveError)
4773
{
4774
    let mut alignment: u32 = 0; // Zero is default.
4775
    let mut bindingTy = Type::Unknown;
4776
    let mut valueTy = Type::Unknown;
4777
4778
    // Check type.
4779
    if let declTy = try visitOptional(self, decl.type, Type::Unknown) {
4780
        set valueTy = try checkAssignable(self, decl.value, declTy);
4781
        set bindingTy = declTy;
4782
    } else {
4783
        set bindingTy = try infer(self, decl.value);
4784
        set valueTy = bindingTy;
4785
4786
        if not isTypeInferrable(bindingTy) {
4787
            throw emitError(self, decl.value, ErrorKind::CannotInferType);
4788
        }
4789
    }
4790
    try validateValueTypeReferences(self, node, bindingTy);
4791
    if isRefType(bindingTy) {
4792
        if self.currentFn == nil {
4793
            throw emitError(self, node, ErrorKind::InvalidRefPosition);
4794
        }
4795
        if decl.mutable and (referenceRegion(bindingTy) == nil or not isCopy(bindingTy)) {
4796
            throw emitError(self, node, ErrorKind::RefBinding);
4797
        }
4798
    }
4799
    // Variables cannot have void type.
4800
    if bindingTy == Type::Void {
4801
        throw emitError(self, decl.value, ErrorKind::CannotAssignVoid);
4802
    }
4803
    // Variables cannot have opaque type directly.
4804
    if bindingTy == Type::Opaque {
4805
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
4806
    }
4807
    // Check alignment.
4808
    if let a = decl.alignment {
4809
        let case ast::NodeValue::Align { value } = a.value
4810
            else panic "resolveLet: expected Align node";
4811
        set alignment = try checkSizeInt(self, value);
4812
    }
4813
    assert bindingTy <> Type::Unknown;
4814
4815
    // Alignment must be zero or a power of two.
4816
    if alignment <> 0 and (alignment & (alignment - 1)) <> 0 {
4817
        throw emitError(self, decl.value, ErrorKind::InvalidAlignmentValue(alignment));
4818
    }
4819
    let _ = try bindValueIdent(self, decl.ident, node, bindingTy, decl.mutable, alignment, 0);
4820
4821
    // Untyped initializers use the declared storage type.
4822
    if not isTypeInferrable(valueTy) {
4823
        setNodeType(self, decl.value, bindingTy);
4824
    }
4825
4826
    return Type::Never if valueTy == Type::Never else Type::Void;
4827
}
4828
4829
/// Check whether a node is an integer literal, optionally under unary negation.
4830
fn isIntegerLiteralExpr(node: *ast::Node) -> bool {
4831
    match node.value {
4832
        case ast::NodeValue::Number(_) => return true,
4833
        case ast::NodeValue::UnOp(unop) => {
4834
            if unop.op == ast::UnaryOp::Neg {
4835
                return isIntegerLiteralExpr(unop.value);
4836
            }
4837
            return false;
4838
        },
4839
        else => return false,
4840
    }
4841
}
4842
4843
/// Determine whether a node represents a compile-time constant expression.
4844
export unsafe fn isConstExpr 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
4845
    match node.value {
4846
        case ast::NodeValue::Bool(_),
4847
             ast::NodeValue::Char(_),
4848
             ast::NodeValue::Number(_),
4849
             ast::NodeValue::String(_),
4850
             ast::NodeValue::Undef,
4851
             ast::NodeValue::Nil => {
4852
            return true;
4853
        },
4854
        case ast::NodeValue::ArrayLit(items) => {
4855
            for item in items {
4856
                if not isConstExpr(self, item) {
4857
                    return false;
4858
                }
4859
            }
4860
            return true;
4861
        },
4862
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
4863
            return isConstExpr(self, repeat.item);
4864
        },
4865
        case ast::NodeValue::AddressOf(addr) => {
4866
            let ty = typeFor(self, node) else {
4867
                return false;
4868
            };
4869
            if let case Type::Slice { .. } = ty {
4870
                return isConstExpr(self, addr.target);
4871
            }
4872
            return false;
4873
        },
4874
        case ast::NodeValue::RecordLit(lit) => {
4875
            // Record literals are constant if all field values are constant.
4876
            for field in lit.fields {
4877
                if let case ast::NodeValue::RecordLitField(fieldLit) = field.value {
4878
                    if not isConstExpr(self, fieldLit.value) {
4879
                        return false;
4880
                    }
4881
                }
4882
            }
4883
            return true;
4884
        },
4885
        case ast::NodeValue::Ident(_),
4886
             ast::NodeValue::ScopeAccess(_) => {
4887
            // Identifiers and scope accesses referencing constants, union
4888
            // variants, or function values are constant expressions.
4889
            if let sym = symbolFor(self, node) {
4890
                match sym.data {
4891
                    case SymbolData::Variant { .. },
4892
                         SymbolData::Constant { .. } => return true,
4893
                    case SymbolData::Value { type, .. } => {
4894
                        if let case Type::Fn(_) = type {
4895
                            return true;
4896
                        }
4897
                    }
4898
                    else => {}
4899
                }
4900
            }
4901
            return false;
4902
        },
4903
        case ast::NodeValue::Call(call) => {
4904
            // Constructor calls (union variants, unlabeled records) are constant
4905
            // if all payload args are themselves constant.
4906
            if let sym = symbolFor(self, call.callee) {
4907
                match sym.data {
4908
                    case SymbolData::Variant { .. } => {}
4909
                    case SymbolData::Type(NominalType::Record(recInfo)) => {
4910
                        if recInfo.labeled {
4911
                            return false;
4912
                        }
4913
                    },
4914
                    else => return false,
4915
                }
4916
                for arg in call.args {
4917
                    if not isConstExpr(self, arg) {
4918
                        return false;
4919
                    }
4920
                }
4921
                return true;
4922
            }
4923
            return false;
4924
        },
4925
        case ast::NodeValue::BinOp(binop) => {
4926
            // Binary expressions are constant if both operands are constant.
4927
            return isConstExpr(self, binop.left) and isConstExpr(self, binop.right);
4928
        },
4929
        case ast::NodeValue::UnOp(unop) => {
4930
            // Unary expressions are constant if the operand is constant.
4931
            return isConstExpr(self, unop.value);
4932
        },
4933
        case ast::NodeValue::As(expr) => {
4934
            // Cast expressions are constant if the source value is constant.
4935
            return isConstExpr(self, expr.value);
4936
        },
4937
        else => {
4938
            return false;
4939
        }
4940
    }
4941
}
4942
4943
/// Construct an integer constant descriptor.
4944
fn constInt(magnitude: u64, bits: u8, signed: bool, negative: bool) -> ConstValue {
4945
    return ConstValue::Int(ConstInt { magnitude, bits, signed, negative });
4946
}
4947
4948
/// Apply an integer cast to a constant value, including target-width
4949
/// truncation and signed interpretation.
4950
fn castConstInt(value: ConstInt, target: Type) -> ConstValue {
4951
    let raw = constIntToBits(value);
4952
    let range = integerRange(target)
4953
        else panic "castConstInt: expected integer type";
4954
4955
    match range {
4956
        case IntegerRange::Unsigned { bits, .. } =>
4957
            return ConstValue::Int(constIntFromBits(raw, bits, false)),
4958
        case IntegerRange::Signed { bits, .. } =>
4959
            return ConstValue::Int(constIntFromBits(raw, bits, true)),
4960
    }
4961
}
4962
4963
/// Return the constant `u32` value for a slice bound when known.
4964
fn constSliceIndex 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?u32 {
4965
    let value = constValueEntry(self, node)
4966
        else return nil;
4967
    let case ConstValue::Int(int) = value
4968
        else return nil;
4969
    if int.negative {
4970
        return nil;
4971
    }
4972
    return int.magnitude as u32;
4973
}
4974
4975
/// Validates and extracts a non-negative integer constant from a compile-time expression.
4976
///
4977
/// This function ensures that a node represents a valid, non-negative integer constant
4978
/// that fits within a machine word. It is used for contexts requiring compile-time
4979
/// non-negative integers, such as array sizes and alignment specifications.
4980
///
4981
/// Returns the unsigned magnitude of the constant as `u32`.
4982
unsafe fn checkSizeInt 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> u32
4983
    throws (ResolveError)
4984
{
4985
    // First traverse the node expect a numeric type.
4986
    let _ = try checkNumeric(self, node);
4987
4988
    // Look up the compile-time constant value associated with this node.
4989
    let value = constValueEntry(self, node)
4990
        else throw emitError(self, node, ErrorKind::ConstExprRequired);
4991
4992
    let case ConstValue::Int(int) = value
4993
        else panic "checkSizeInt: expected integer constant";
4994
4995
    // Validate it fits within u32 range.
4996
    if not validateConstIntRange(value, Type::U32) {
4997
        throw emitError(self, node, ErrorKind::NumericLiteralOverflow);
4998
    }
4999
    assert not int.negative;
5000
    setNodeType(self, node, Type::U32);
5001
5002
    return int.magnitude as u32;
5003
}
5004
5005
/// Check that constructor arguments match record fields.
5006
///
5007
/// Verifies argument count matches field count, and that each argument is
5008
/// assignable to its corresponding field type.
5009
unsafe fn checkRecordConstructorArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, args: *[*ast::Node], recInfo: RecordType)
5010
    throws (ResolveError)
5011
{
5012
    try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: args.len }, node);
5013
    for arg, i in args {
5014
        let fieldType = recInfo.fields[i].fieldType;
5015
        try checkAssignable(self, arg, fieldType);
5016
    }
5017
}
5018
5019
/// Check that the argument count of a constructor pattern or call matches the record field count.
5020
fn checkRecordArity 'arena (self: &mut Resolver 'arena, counts: CountMismatch, pattern: *ast::Node) throws (ResolveError) {
5021
    if counts.actual <> counts.expected {
5022
        throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(counts));
5023
    }
5024
}
5025
5026
/// Helper for analyzing `constant` and `static` declarations.
5027
unsafe fn resolveConstOrStatic 'arena (
5028
    self: &mut Resolver 'arena,
5029
    node: *ast::Node,
5030
    ident: *ast::Node,
5031
    typeNode: *ast::Node,
5032
    valueNode: *ast::Node,
5033
    attrList: ?ast::Attributes,
5034
    isConst: bool
5035
) -> Type throws (ResolveError) {
5036
    let attrs = resolveAttributes(attrList);
5037
    let bindingTy = try infer(self, typeNode);
5038
    if containsRegion(bindingTy) {
5039
        throw emitError(self, typeNode, ErrorKind::InvalidRefPosition);
5040
    }
5041
    try ensureStorableType(self, typeNode, bindingTy);
5042
    let wasUnsafe = self.inUnsafeContext;
5043
    set self.inUnsafeContext = wasUnsafe or (
5044
        not isConst and ast::hasAttribute(attrs, ast::Attribute::Unsafe)
5045
    );
5046
    let valueTy = try checkAssignable(self, valueNode, bindingTy) catch e {
5047
        set self.inUnsafeContext = wasUnsafe;
5048
        throw e;
5049
    };
5050
    set self.inUnsafeContext = wasUnsafe;
5051
5052
    if isConst {
5053
        let mut constVal = constValueEntry(self, valueNode);
5054
        if constVal == nil and not isConstExpr(self, valueNode) {
5055
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
5056
        }
5057
        if let val = constVal {
5058
            if let case ConstValue::Int(int) = val; isNumericType(bindingTy) {
5059
                set constVal = castConstInt(int, bindingTy);
5060
            }
5061
        }
5062
        try bindConstIdent(self, ident, node, bindingTy, constVal, attrs);
5063
    } else {
5064
        if not isConstExpr(self, valueNode) {
5065
            throw emitError(self, valueNode, ErrorKind::ConstExprRequired);
5066
        }
5067
        try bindValueIdent(self, ident, node, bindingTy, true, 0, attrs);
5068
    }
5069
    setNodeType(self, valueNode, bindingTy);
5070
5071
    return Type::Void;
5072
}
5073
5074
/// Analyze a function declaration signature and bind the function name.
5075
unsafe fn resolveFnDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
5076
    throws (ResolveError)
5077
{
5078
    let previous = self.regionScope;
5079
    set self.regionScope = try bindRegions(self, node, decl.regions);
5080
    let result = try resolveFnSignature(self, node, decl) catch error {
5081
        set self.regionScope = previous;
5082
        throw error;
5083
    };
5084
    set self.regionScope = previous;
5085
    return result;
5086
}
5087
5088
/// Resolve a function signature in its declared region environment.
5089
unsafe fn resolveFnSignature 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) -> Type
5090
    throws (ResolveError)
5091
{
5092
    let attrMask = resolveAttributes(decl.attrs);
5093
    let mut retTy = Type::Void;
5094
    if let retNode = decl.sig.returnType {
5095
        set retTy = try infer(self, retNode);
5096
        try ensureStorableType(self, retNode, retTy);
5097
    }
5098
    let a = alloc::arenaAllocator(self.arena);
5099
    let mut paramTypes: *mut [*Type] = &mut [];
5100
    let mut throwList: *mut [*Type] = &mut [];
5101
    let mut fnType = FnType {
5102
        regions: self.regionScope,
5103
        paramTypes: &[],
5104
        returnType: allocType(self, retTy),
5105
        throwList: &[],
5106
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
5107
    };
5108
    // Enter the function scope to process parameters.
5109
    enterFn(self, node, &fnType);
5110
5111
    if decl.sig.params.len > MAX_FN_PARAMS {
5112
        exitFn(self);
5113
        throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
5114
            expected: MAX_FN_PARAMS,
5115
            actual: decl.sig.params.len,
5116
        }));
5117
    }
5118
    for paramNode in decl.sig.params {
5119
        let paramTy = try infer(self, paramNode) catch e {
5120
            exitFn(self);
5121
            throw e;
5122
        };
5123
        paramTypes.append(allocType(self, paramTy), a);
5124
    }
5125
5126
    if decl.sig.throwList.len > MAX_FN_THROWS {
5127
        exitFn(self);
5128
        throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
5129
            expected: MAX_FN_THROWS,
5130
            actual: decl.sig.throwList.len,
5131
        }));
5132
    }
5133
    for throwNode in decl.sig.throwList {
5134
        let throwTy = try infer(self, throwNode) catch e {
5135
            exitFn(self);
5136
            throw e;
5137
        };
5138
        try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
5139
        throwList.append(allocType(self, throwTy), a);
5140
        try ensureStorableType(self, throwNode, throwTy);
5141
    }
5142
    exitFn(self);
5143
    set fnType.paramTypes = &paramTypes[..];
5144
    set fnType.throwList = &throwList[..];
5145
5146
    // Bind the function name.
5147
    let ty = Type::Fn(allocFnType(self, fnType));
5148
    let sym = try bindValueIdent(self, decl.name, node, ty, false, 0, attrMask)
5149
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
5150
5151
    return ty;
5152
}
5153
5154
/// Analyze a function body.
5155
unsafe fn resolveFnDeclBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::FnDecl) throws (ResolveError) {
5156
    let sym = symbolFor(self, node) else {
5157
        // The function declaration failed to type check, therefore
5158
        // no symbol was associated with it.
5159
        return;
5160
    };
5161
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
5162
        panic "resolveFnDeclBody: unexpected symbol data for function";
5163
    };
5164
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
5165
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
5166
5167
    if let body = decl.body {
5168
        if isIntrinsic {
5169
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
5170
        }
5171
        if isExtern {
5172
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
5173
        }
5174
        let previous = self.regionScope;
5175
        set self.regionScope = try bindRegions(self, node, decl.regions);
5176
        try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body) catch error {
5177
            set self.regionScope = previous;
5178
            throw error;
5179
        };
5180
        set self.regionScope = previous;
5181
    } else if not isExtern {
5182
        throw emitError(self, node, ErrorKind::FnMissingBody);
5183
    }
5184
}
5185
5186
/// Resolve a function or method body and restore the enclosing context.
5187
unsafe fn resolveExecutableBody 'arena (
5188
    self: &mut Resolver 'arena,
5189
    node: *ast::Node,
5190
    fnType: *FnType,
5191
    receiverName: ?*ast::Node,
5192
    params: *[*ast::Node],
5193
    body: *ast::Node,
5194
) throws (ResolveError) {
5195
    let wasUnsafe = self.inUnsafeContext;
5196
    set self.inUnsafeContext = fnType.isUnsafe;
5197
    // Enter function scope.
5198
    enterFn(self, node, fnType); // Enter function scope for body analysis.
5199
5200
    let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e {
5201
        exitFn(self);
5202
        set self.inUnsafeContext = wasUnsafe;
5203
        throw e;
5204
    };
5205
    exitFn(self);
5206
    set self.inUnsafeContext = wasUnsafe;
5207
    if missingReturn {
5208
        throw emitError(self, body, ErrorKind::FnMissingReturn);
5209
    }
5210
}
5211
5212
/// Check parameters, body types, and ownership.
5213
/// Return whether a required return is missing.
5214
unsafe fn checkExecutableBody 'arena (
5215
    self: &mut Resolver 'arena,
5216
    fnType: *FnType,
5217
    receiverName: ?*ast::Node,
5218
    params: *[*ast::Node],
5219
    body: *ast::Node,
5220
) -> bool throws (ResolveError) {
5221
    if let receiver = receiverName {
5222
        // Bind the receiver parameter.
5223
        let receiverTy = *fnType.paramTypes[0];
5224
        try bindValueIdent(self, receiver, receiver, receiverTy, false, 0, 0);
5225
        // Bind the remaining parameters from the signature.
5226
        for paramNode in params {
5227
            let paramTy = try infer(self, paramNode);
5228
        }
5229
    }
5230
    // Resolve the body.
5231
    let retTy = *fnType.returnType;
5232
    let bodyTy = try checkAssignable(self, body, Type::Void);
5233
    if retTy <> Type::Void and bodyTy <> Type::Never {
5234
        return true;
5235
    }
5236
    // Ownership checks require complete type and call metadata.
5237
    if self.errors.len == 0 {
5238
        try checkLinearFn(self, receiverName, params, body);
5239
    }
5240
    return false;
5241
}
5242
5243
/// Analyze a function parameter and bind its identifier.
5244
unsafe fn resolveFnParam 'arena (self: &mut Resolver 'arena, node: *ast::Node, param: ast::FnParam) -> Type
5245
    throws (ResolveError)
5246
{
5247
    let ty = try resolveValueType(self, param.type);
5248
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
5249
5250
    return ty;
5251
}
5252
5253
/// Compiler-known ownership markers carried by a composite declaration.
5254
record OwnershipMarkers: Copy {
5255
    /// The declaration requires exact consumption.
5256
    linear: bool,
5257
    /// The declaration permits implicit copies.
5258
    copy: bool,
5259
}
5260
5261
/// Resolve compiler-known ownership markers from a derive list.
5262
unsafe fn resolveOwnershipMarkers 'arena (self: &mut Resolver 'arena, derives: *[*ast::Node]) -> OwnershipMarkers
5263
    throws (ResolveError)
5264
{
5265
    let mut result = OwnershipMarkers { linear: false, copy: false };
5266
    for derive in derives {
5267
        if let case ast::NodeValue::Region { .. } = derive.value {
5268
            continue;
5269
        }
5270
        let name = try nodeName(self, derive);
5271
        if mem::eq(name, "Once") {
5272
            if result.linear {
5273
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
5274
            }
5275
            if result.copy {
5276
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
5277
            }
5278
            set result.linear = true;
5279
        } else if mem::eq(name, "Copy") {
5280
            if result.copy {
5281
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
5282
            }
5283
            if result.linear {
5284
                throw emitError(self, derive, ErrorKind::ConflictingOwnershipMarkers);
5285
            }
5286
            set result.copy = true;
5287
        } else {
5288
            // Resolve an ordinary trait derive.
5289
            try infer(self, derive);
5290
        }
5291
    }
5292
    return result;
5293
}
5294
5295
/// Resolve record fields from a node list.
5296
unsafe fn resolveRecordFields 'arena (self: &mut Resolver 'arena, node: *ast::Node, fields: *[*ast::Node], labeled: bool) -> RecordType
5297
    throws (ResolveError)
5298
{
5299
    let a = alloc::arenaAllocator(self.arena);
5300
    let mut result: *mut [RecordField] = &mut [];
5301
    let mut layout = Layout { size: 0, alignment: 1 };
5302
5303
    if fields.len > parser::MAX_RECORD_FIELDS {
5304
        throw emitError(self, node, ErrorKind::Internal);
5305
    }
5306
    for field in fields {
5307
        let case ast::NodeValue::RecordField {
5308
            field: fieldNode,
5309
            type: typeNode,
5310
            value: valueNode
5311
        } = field.value else panic "resolveRecordFields: invalid record field";
5312
        let fieldTy = try resolveValueType(self, typeNode);
5313
        try ensureStorableType(self, typeNode, fieldTy);
5314
5315
        if let v = valueNode {
5316
            let _valTy = try checkAssignable(self, v, fieldTy);
5317
        }
5318
        // Get field name for labeled records.
5319
        let mut fieldName: ?*[u8] = nil;
5320
        if labeled {
5321
            let n = fieldNode
5322
                else panic "resolveRecordFields: labeled record field missing name";
5323
            set fieldName = try nodeName(self, n);
5324
        }
5325
        let fieldType = typeFor(self, typeNode)
5326
            else throw emitError(self, typeNode, ErrorKind::CannotInferType);
5327
5328
        // Ensure field type is fully resolved before computing layout.
5329
        try ensureTypeResolved(self, fieldType, typeNode);
5330
5331
        appendRecordField(&mut result, &mut layout, fieldName, fieldType, a);
5332
    }
5333
    // Compute cached layout.
5334
    let recordLayout = Layout {
5335
        size: mem::alignUp(layout.size, layout.alignment),
5336
        alignment: layout.alignment
5337
    };
5338
    return RecordType {
5339
        privateModule: nil,
5340
        regions: nil,
5341
        application: nil,
5342
        fields: (&result[..]) as *unsafe [RecordField],
5343
        labeled,
5344
        layout: allocLayout(self, recordLayout),
5345
        declaredLinear: false,
5346
        declaredCopy: false,
5347
    };
5348
}
5349
5350
/// Append an owned record field and update the layout before tail padding.
5351
fn appendRecordField(fields: &mut *mut [RecordField], layout: &mut Layout, name: ?*[u8], fieldType: Type, allocator: alloc::Allocator) {
5352
    // Compute field offset by aligning to field's alignment.
5353
    let fieldLayout = typeLayout(fieldType);
5354
    let offset = mem::alignUp(layout.size, fieldLayout.alignment);
5355
    fields.append(RecordField { name, fieldType, offset: offset as i32 }, allocator);
5356
5357
    // Advance offset past this field.
5358
    set layout.size = offset + fieldLayout.size;
5359
5360
    // Track max alignment for record layout.
5361
    set layout.alignment = max(layout.alignment, fieldLayout.alignment);
5362
}
5363
5364
/// Resolve record field types for a named record declaration.
5365
unsafe fn resolveRecordBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
5366
    throws (ResolveError)
5367
{
5368
    let previous = self.regionScope;
5369
    set self.regionScope = try bindRegions(self, node, decl.regions);
5370
    try resolveRecordContents(self, node, decl) catch error {
5371
        set self.regionScope = previous;
5372
        throw error;
5373
    };
5374
    set self.regionScope = previous;
5375
}
5376
5377
/// Resolve record contents in the declaration's region environment.
5378
unsafe fn resolveRecordContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::RecordDecl)
5379
    throws (ResolveError)
5380
{
5381
    // Get the type symbol that was bound to this declaration node.
5382
    // If there's no symbol, it's because an earlier phase failed.
5383
    let sym = symbolFor(self, node)
5384
        else return;
5385
    let case SymbolData::Type(nominalTy) = sym.data
5386
        else panic "resolveRecordBody: unexpected type symbol data";
5387
5388
    // Skip if already resolved.
5389
    if let case NominalType::Record(_) = *nominalTy {
5390
        return;
5391
    }
5392
    let markers = try resolveOwnershipMarkers(self, decl.derives);
5393
    set *nominalTy = NominalType::Resolving(node);
5394
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled) catch error {
5395
        set *nominalTy = NominalType::Placeholder(node);
5396
        throw error;
5397
    };
5398
    set recordType.regions = self.regionScope;
5399
    if ast::hasAttribute(sym.attrs, ast::Attribute::Opaque) {
5400
        set recordType.privateModule = self.currentMod;
5401
    }
5402
    if markers.copy {
5403
        for field in recordType.fields {
5404
            if not isCopy(field.fieldType) {
5405
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
5406
            }
5407
        }
5408
    }
5409
    set recordType.declaredLinear = markers.linear;
5410
    set recordType.declaredCopy = markers.copy;
5411
5412
    set *nominalTy = NominalType::Record(recordType);
5413
}
5414
5415
/// Bind a type name.
5416
unsafe fn bindTypeName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
5417
    throws (ResolveError)
5418
{
5419
    let attrMask = resolveAttributes(attrs);
5420
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
5421
5422
    // Create a placeholder nominal type that will be replaced in
5423
    // the next phase.
5424
    let nominalTy = allocNominalType(self, NominalType::Placeholder(node));
5425
5426
    return try bindTypeIdent(self, name, node, nominalTy, attrMask);
5427
}
5428
5429
/// Allocate a trait type descriptor and return a pointer to it.
5430
unsafe fn allocTraitType 'arena (self: &mut Resolver 'arena, name: *[u8]) -> *unsafe mut TraitType {
5431
    let p = try! alloc::allocRaw(self.arena, @sizeOf(TraitType), @alignOf(TraitType));
5432
    let entry = p as *unsafe mut TraitType;
5433
    set *entry = TraitType { name, moduleId: self.currentMod, methods: &mut [], supertraits: &mut [] };
5434
5435
    return entry;
5436
}
5437
5438
/// Bind a trait name in the current scope.
5439
unsafe fn bindTraitName 'arena (self: &mut Resolver 'arena, node: *ast::Node, name: *ast::Node, attrs: ?ast::Attributes) -> *unsafe mut Symbol
5440
    throws (ResolveError)
5441
{
5442
    let attrMask = resolveAttributes(attrs);
5443
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
5444
5445
    let traitName = try nodeName(self, name);
5446
    let traitType = allocTraitType(self, traitName);
5447
    let data = SymbolData::Trait(traitType);
5448
    let scope = self.scope;
5449
    let sym = try bindIdent(self, traitName, node, data, attrMask, scope);
5450
5451
    setNodeType(self, node, Type::Void);
5452
    setNodeType(self, name, Type::Void);
5453
5454
    return sym;
5455
}
5456
5457
/// Find a trait method by name and return its resolved metadata.
5458
export fn findTraitMethod(methods: &[TraitMethod], name: *[u8]) -> ?TraitMethod {
5459
    for method in methods {
5460
        if mem::eq(method.name, name) {
5461
            return method;
5462
        }
5463
    }
5464
    return nil;
5465
}
5466
5467
/// Resolve a trait declaration body: supertrait methods, then own methods.
5468
unsafe fn resolveTraitBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, supertraits: *[*ast::Node], methods: *[*ast::Node])
5469
    throws (ResolveError)
5470
{
5471
    let sym = symbolFor(self, node)
5472
        else return;
5473
    let case SymbolData::Trait(traitType) = sym.data
5474
        else return;
5475
    if traitType.methods.len > 0 {
5476
        return;
5477
    }
5478
5479
    // Resolve supertrait bounds and copy their methods into this trait.
5480
    for superNode in supertraits {
5481
        let superSym = try resolveNamePath(self, superNode);
5482
        let case SymbolData::Trait(superTrait) = superSym.data
5483
            else throw emitError(self, superNode, ErrorKind::Internal);
5484
        // Trait bodies are otherwise resolved in source order. Recursively
5485
        // resolve a supertrait only when it is declared later.
5486
        if superSym.node.id > node.id {
5487
            let case ast::NodeValue::TraitDecl {
5488
                supertraits: inheritedTraits, methods: inheritedMethods, ..
5489
            } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal);
5490
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
5491
        }
5492
5493
        setNodeSymbol(self, superNode, superSym);
5494
5495
        let a = alloc::arenaAllocator(self.arena);
5496
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
5497
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
5498
                expected: ast::MAX_TRAIT_METHODS,
5499
                actual: traitType.methods.len as u32 + superTrait.methods.len as u32,
5500
            }));
5501
        }
5502
        // Copy inherited methods into this trait's method table.
5503
        for inherited in superTrait.methods {
5504
            if let _ = findTraitMethod(&traitType.methods[..], inherited.name) {
5505
                throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name));
5506
            }
5507
            traitType.methods.append(TraitMethod {
5508
                name: inherited.name,
5509
                fnType: inherited.fnType,
5510
                mutable: inherited.mutable,
5511
                receiverClass: inherited.receiverClass,
5512
                index: traitType.methods.len as u32,
5513
            }, a);
5514
        }
5515
        traitType.supertraits.append(superTrait, a);
5516
    }
5517
5518
    if traitType.methods.len + methods.len > ast::MAX_TRAIT_METHODS {
5519
        throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
5520
            expected: ast::MAX_TRAIT_METHODS,
5521
            actual: traitType.methods.len as u32 + methods.len as u32,
5522
        }));
5523
    }
5524
5525
    for methodNode in methods {
5526
        let case ast::NodeValue::TraitMethodSig { name, modifiers, receiver, sig } = methodNode.value
5527
            else continue;
5528
        let attrs = modifiers.attrs;
5529
        let methodName = try nodeName(self, name);
5530
        let attrMask = resolveAttributes(attrs);
5531
        let previousRegions = self.regionScope;
5532
        set self.regionScope = try bindRegions(self, methodNode, modifiers.regions);
5533
5534
        // Reject duplicate method names.
5535
        if let _ = findTraitMethod(&traitType.methods[..], methodName) {
5536
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
5537
        }
5538
        // Determine the receiver class and mutability, and validate that it
5539
        // points to the declaring trait.
5540
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
5541
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5542
        let case ast::TypeSig::Pointer {
5543
            class: receiverSyntax, valueType: receiverValueType, mutable,
5544
        } = typeSig
5545
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5546
        let receiverClass = resolvePointerClass(receiverSyntax);
5547
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
5548
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5549
        let case ast::TypeSig::Nominal(nameNode) = innerSig
5550
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5551
        let receiverTargetName = try nodeName(self, nameNode);
5552
5553
        if receiverTargetName <> traitType.name {
5554
            throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
5555
        }
5556
        // Resolve parameter types and return type.
5557
        let a = alloc::arenaAllocator(self.arena);
5558
        let mut paramTypes: *mut [*Type] = &mut [];
5559
        let mut throwList: *mut [*Type] = &mut [];
5560
        let mut retType = allocType(self, Type::Void);
5561
5562
        if sig.params.len > MAX_FN_PARAMS {
5563
            throw emitError(self, methodNode, ErrorKind::FnParamOverflow(CountMismatch {
5564
                expected: MAX_FN_PARAMS,
5565
                actual: sig.params.len,
5566
            }));
5567
        }
5568
        for paramNode in sig.params {
5569
            let case ast::NodeValue::FnParam(param) = paramNode.value
5570
                else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5571
            let paramTy = try resolveValueType(self, param.type);
5572
            paramTypes.append(allocType(self, paramTy), a);
5573
        }
5574
        if let ret = sig.returnType {
5575
            set retType = allocType(self, try infer(self, ret));
5576
        }
5577
        // Resolve throws list.
5578
        if sig.throwList.len > MAX_FN_THROWS {
5579
            throw emitError(self, methodNode, ErrorKind::FnThrowOverflow(CountMismatch {
5580
                expected: MAX_FN_THROWS,
5581
                actual: sig.throwList.len,
5582
            }));
5583
        }
5584
        for throwNode in sig.throwList {
5585
            let throwTy = try infer(self, throwNode);
5586
            try validateErrorTag(self, throwNode, throwTy, &throwList[..]);
5587
            throwList.append(allocType(self, throwTy), a);
5588
        }
5589
        let fnType = FnType {
5590
            regions: self.regionScope,
5591
            paramTypes: &paramTypes[..],
5592
            returnType: retType,
5593
            throwList: &throwList[..],
5594
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
5595
        };
5596
        traitType.methods.append(TraitMethod {
5597
            name: methodName,
5598
            fnType: allocFnType(self, fnType),
5599
            mutable,
5600
            receiverClass,
5601
            index: traitType.methods.len as u32,
5602
        }, a);
5603
5604
        setNodeType(self, methodNode, Type::Void);
5605
        set self.regionScope = previousRegions;
5606
    }
5607
}
5608
5609
/// Resolve a name path node to a symbol.
5610
/// Used for trait and type references in instance declarations and trait objects.
5611
unsafe fn resolveNamePath 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *unsafe mut Symbol
5612
    throws (ResolveError)
5613
{
5614
    match node.value {
5615
        case ast::NodeValue::Ident(name) => {
5616
            let sym = findAnySymbol(self.scope, name)
5617
                else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name));
5618
            return sym;
5619
        }
5620
        case ast::NodeValue::ScopeAccess(access) => {
5621
            let scope = self.scope;
5622
            return try resolveAccess(self, node, access, scope);
5623
        }
5624
        else => {
5625
            throw emitError(self, node, ErrorKind::ExpectedIdentifier);
5626
        }
5627
    }
5628
}
5629
5630
/// Join instance and method region parameters into one function binder.
5631
unsafe fn instanceMethodRegions 'arena (
5632
    self: &mut Resolver 'arena, instanceRegions: *[*ast::Node], methodRegions: *[*ast::Node]
5633
) -> *[*ast::Node] {
5634
    let count = instanceRegions.len + methodRegions.len;
5635
    if count == 0 {
5636
        return &[];
5637
    }
5638
    let allocator = alloc::arenaAllocator(self.arena);
5639
    let mut nodes: *mut [*ast::Node] = &mut [];
5640
    for region in instanceRegions {
5641
        nodes.append(region, allocator);
5642
    }
5643
    for region in methodRegions {
5644
        nodes.append(region, allocator);
5645
    }
5646
    return &nodes[..];
5647
}
5648
5649
/// Map one region binder to a contiguous part of another binder.
5650
unsafe fn mapRegionScopes 'arena (
5651
    self: &mut Resolver 'arena, source: ?*RegionScope, target: ?*RegionScope,
5652
    offset: u32, site: *ast::Node
5653
) -> ?RegionSubstitution throws (ResolveError) {
5654
    let sourceScope = source else return nil;
5655
    let targetScope = target else throw emitError(self, site, ErrorKind::Internal);
5656
    if offset + sourceScope.entries.len > targetScope.entries.len {
5657
        throw emitError(self, site, ErrorKind::RegionArgumentCount(CountMismatch {
5658
            expected: sourceScope.entries.len,
5659
            actual: targetScope.entries.len - offset,
5660
        }));
5661
    }
5662
    let map = regionSubstitution(self, sourceScope);
5663
    for _, i in sourceScope.entries {
5664
        set map.arguments[i] = targetScope.entries[offset + i];
5665
    }
5666
    try validateRegionArguments(self, &map, nil, nil, site);
5667
    return map;
5668
}
5669
5670
/// Resolved implementation of one trait method.
5671
record ResolvedInstanceMethod: Copy {
5672
    /// Canonical trait method.
5673
    method: TraitMethod,
5674
    /// Concrete function symbol.
5675
    symbol: *unsafe mut Symbol,
5676
}
5677
5678
/// Shared declaration state for instance method resolution.
5679
record InstanceMethodContext: Copy {
5680
    /// Implemented trait.
5681
    traitInfo: *unsafe TraitType,
5682
    /// Instance target with declaration regions applied.
5683
    concreteType: Type,
5684
    /// Instance region nodes in declaration order.
5685
    regions: *[*ast::Node],
5686
    /// Bound instance region scope.
5687
    scope: ?*RegionScope,
5688
}
5689
5690
/// Resolve one instance method in its combined region environment.
5691
unsafe fn resolveInstanceMethod 'arena (
5692
    self: &mut Resolver 'arena, methodNode: *ast::Node,
5693
    context: &InstanceMethodContext
5694
) -> ResolvedInstanceMethod throws (ResolveError) {
5695
    let case ast::NodeValue::MethodDecl {
5696
        name, modifiers, receiverType, sig, ..
5697
    } = methodNode.value else panic "resolveInstanceMethod: invalid method";
5698
    let combinedRegions = instanceMethodRegions(self, context.regions, modifiers.regions);
5699
    let methodScope = try bindRegions(self, methodNode, combinedRegions);
5700
    set self.regionScope = methodScope;
5701
5702
    let methodName = try nodeName(self, name);
5703
    let attrMask = resolveAttributes(modifiers.attrs);
5704
    let tm = findTraitMethod(&context.traitInfo.methods[..], methodName)
5705
        else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
5706
    if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe {
5707
        throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
5708
    }
5709
5710
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer {
5711
        class: receiverSyntax, valueType, mutable: receiverMut,
5712
    }) = receiverType.value else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5713
    let receiverClass = resolvePointerClass(receiverSyntax);
5714
    if receiverClass <> tm.receiverClass {
5715
        throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5716
    }
5717
    let annotatedTy = try infer(self, valueType);
5718
    let mut expectedConcrete = context.concreteType;
5719
    if let map = try mapRegionScopes(self, context.scope, methodScope, 0, methodNode) {
5720
        set expectedConcrete = substituteRegions(self, &map, context.concreteType);
5721
    }
5722
    if not typesEqual(annotatedTy, expectedConcrete) {
5723
        throw emitTypeMismatch(self, receiverType, TypeMismatch { expected: expectedConcrete, actual: annotatedTy });
5724
    }
5725
    if tm.mutable and not receiverMut {
5726
        throw emitError(self, receiverType, ErrorKind::ImmutableBinding);
5727
    }
5728
    if receiverMut and not tm.mutable {
5729
        throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch);
5730
    }
5731
5732
    let mut traitFn = tm.fnType;
5733
    let mut traitRegionCount: u32 = 0;
5734
    if let traitScope = tm.fnType.regions {
5735
        set traitRegionCount = traitScope.entries.len;
5736
    }
5737
    if traitRegionCount <> modifiers.regions.len {
5738
        throw emitError(self, methodNode, ErrorKind::RegionArgumentCount(CountMismatch {
5739
            expected: traitRegionCount, actual: modifiers.regions.len,
5740
        }));
5741
    }
5742
    if let map = try mapRegionScopes(self, tm.fnType.regions, methodScope, context.regions.len, methodNode) {
5743
        set traitFn = substituteFnRegions(self, &map, tm.fnType, nil);
5744
    }
5745
    if sig.params.len <> traitFn.paramTypes.len {
5746
        throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch {
5747
            expected: traitFn.paramTypes.len, actual: sig.params.len,
5748
        }));
5749
    }
5750
5751
    let allocator = alloc::arenaAllocator(self.arena);
5752
    let mut paramTypes: *mut [*Type] = &mut [];
5753
    let receiverPtrType = Type::Pointer {
5754
        class: receiverClass, target: allocType(self, annotatedTy), mutable: receiverMut,
5755
    };
5756
    paramTypes.append(allocType(self, receiverPtrType), allocator);
5757
    for paramNode, i in sig.params {
5758
        let case ast::NodeValue::FnParam(param) = paramNode.value
5759
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
5760
        let instanceParamTy = try resolveValueType(self, param.type);
5761
        if not typesEqual(instanceParamTy, *traitFn.paramTypes[i]) {
5762
            throw emitTypeMismatch(self, paramNode, TypeMismatch {
5763
                expected: *traitFn.paramTypes[i], actual: instanceParamTy,
5764
            });
5765
        }
5766
        paramTypes.append(allocType(self, instanceParamTy), allocator);
5767
    }
5768
    let mut returnType = Type::Void;
5769
    if let returnNode = sig.returnType {
5770
        set returnType = try resolveValueType(self, returnNode);
5771
    }
5772
    if not typesEqual(returnType, *traitFn.returnType) {
5773
        throw emitTypeMismatch(self, methodNode, TypeMismatch {
5774
            expected: *traitFn.returnType, actual: returnType,
5775
        });
5776
    }
5777
    if sig.throwList.len <> traitFn.throwList.len {
5778
        throw emitError(self, methodNode, ErrorKind::FnThrowCountMismatch(CountMismatch {
5779
            expected: traitFn.throwList.len, actual: sig.throwList.len,
5780
        }));
5781
    }
5782
    let mut throwList: *mut [*Type] = &mut [];
5783
    for throwNode, i in sig.throwList {
5784
        let throwType = try resolveValueType(self, throwNode);
5785
        if not typesEqual(throwType, *traitFn.throwList[i]) {
5786
            throw emitTypeMismatch(self, throwNode, TypeMismatch {
5787
                expected: *traitFn.throwList[i], actual: throwType,
5788
            });
5789
        }
5790
        throwList.append(allocType(self, throwType), allocator);
5791
    }
5792
5793
    let fnType = FnType {
5794
        regions: methodScope, paramTypes: &paramTypes[..],
5795
        returnType: allocType(self, returnType), throwList: &throwList[..],
5796
        isUnsafe: tm.fnType.isUnsafe,
5797
    };
5798
    let fnTy = Type::Fn(allocFnType(self, fnType));
5799
    let sym = allocSymbol(self, SymbolData::Value {
5800
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
5801
    }, methodName, methodNode, attrMask);
5802
    setNodeSymbol(self, methodNode, sym);
5803
    setNodeType(self, methodNode, fnTy);
5804
    setNodeType(self, name, fnTy);
5805
    set self.regionScope = context.scope;
5806
    return ResolvedInstanceMethod { method: tm, symbol: sym };
5807
}
5808
5809
/// Resolve an instance declaration.
5810
/// Validates that the trait exists, the target type exists, and all methods
5811
/// match the trait's signatures.
5812
unsafe fn resolveInstanceDecl 'arena (
5813
    self: &mut Resolver 'arena,
5814
    node: *ast::Node,
5815
    traitName: *ast::Node,
5816
    targetType: *ast::Node,
5817
    regions: *[*ast::Node],
5818
    methods: *[*ast::Node]
5819
) throws (ResolveError) {
5820
    let previous = self.regionScope;
5821
    set self.regionScope = try bindRegions(self, node, regions);
5822
    try resolveInstanceContents(self, node, traitName, targetType, regions, methods) catch error {
5823
        set self.regionScope = previous;
5824
        throw error;
5825
    };
5826
    set self.regionScope = previous;
5827
}
5828
5829
/// Resolve an instance in its declaration region environment.
5830
unsafe fn resolveInstanceContents 'arena (
5831
    self: &mut Resolver 'arena,
5832
    node: *ast::Node,
5833
    traitName: *ast::Node,
5834
    targetType: *ast::Node,
5835
    regions: *[*ast::Node],
5836
    methods: *[*ast::Node]
5837
) throws (ResolveError) {
5838
    let instanceScope = self.regionScope;
5839
    // Look up the trait.
5840
    let traitSym = try resolveNamePath(self, traitName);
5841
    let case SymbolData::Trait(traitInfo) = traitSym.data
5842
        else throw emitError(self, traitName, ErrorKind::Internal);
5843
5844
    setNodeSymbol(self, traitName, traitSym);
5845
5846
    // Look up the target type.
5847
    let typeSym = try resolveNamePath(self, targetType);
5848
    let case SymbolData::Type(nominalTy) = typeSym.data
5849
        else throw emitError(self, targetType, ErrorKind::Internal);
5850
    setNodeSymbol(self, targetType, typeSym);
5851
    // Ensure the concrete type body is resolved.
5852
    try ensureNominalResolved(self, nominalTy, targetType);
5853
5854
    // Reject duplicate instance for the same (trait, type) pair.
5855
    let mut concreteInfo = nominalTy;
5856
    if regions.len > 0 {
5857
        set concreteInfo = try applyNominalRegions(self, nominalTy, regions, targetType);
5858
    } else {
5859
        try requireNominalArguments(self, nominalTy, targetType);
5860
    }
5861
    let concreteType = Type::Nominal(concreteInfo);
5862
    if let _ = findInstance(self, traitInfo, concreteType) {
5863
        throw emitError(self, node, ErrorKind::DuplicateInstance);
5864
    }
5865
5866
    // Build the instance entry.
5867
    if self.instancesLen >= MAX_INSTANCES {
5868
        throw emitError(self, node, ErrorKind::Internal);
5869
    }
5870
    let methodSlice = try! alloc::allocRawSlice(
5871
        self.arena, @sizeOf(*unsafe mut Symbol), @alignOf(*unsafe mut Symbol), traitInfo.methods.len as u32
5872
    ) as *unsafe mut [*unsafe mut Symbol];
5873
    let mut entry = InstanceEntry {
5874
        traitType: traitInfo,
5875
        concreteType,
5876
        concreteTypeName: typeSym.name,
5877
        moduleId: self.currentMod,
5878
        methods: methodSlice,
5879
    };
5880
    // Track which trait methods are covered by the instance.
5881
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
5882
    let methodContext = InstanceMethodContext {
5883
        traitInfo, concreteType, regions, scope: instanceScope,
5884
    };
5885
5886
    // Match each instance method to a trait method.
5887
    for methodNode in methods {
5888
        let resolved = try resolveInstanceMethod(
5889
            self, methodNode, &methodContext
5890
        );
5891
        set entry.methods[resolved.method.index] = resolved.symbol;
5892
        set covered[resolved.method.index] = true;
5893
    }
5894
5895
    // Fill inherited method slots from supertrait instances.
5896
    for superTrait in traitInfo.supertraits {
5897
        let superInst = findInstance(self, superTrait, concreteType)
5898
            else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name));
5899
        for superMethod, mi in superTrait.methods {
5900
            let merged = findTraitMethod(&traitInfo.methods[..], superMethod.name)
5901
                else panic "resolveInstanceDecl: inherited method not found";
5902
            if not covered[merged.index] {
5903
                set entry.methods[merged.index] = superInst.methods[mi];
5904
                set covered[merged.index] = true;
5905
            }
5906
        }
5907
    }
5908
5909
    // Check that all trait methods are implemented.
5910
    for method, i in traitInfo.methods {
5911
        if not covered[i] {
5912
            throw emitError(self, node, ErrorKind::MissingTraitMethod(method.name));
5913
        }
5914
    }
5915
    set self.instances[self.instancesLen] = entry;
5916
    set self.instancesLen += 1;
5917
5918
    setNodeType(self, node, Type::Void);
5919
}
5920
5921
/// Resolve instance method bodies.
5922
unsafe fn resolveInstanceMethodBodies 'arena (self: &mut Resolver 'arena, methods: *[*ast::Node])
5923
    throws (ResolveError)
5924
{
5925
    for methodNode in methods {
5926
        let case ast::NodeValue::MethodDecl { .. } = methodNode.value else continue;
5927
5928
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
5929
        // for this method (eg. unknown method name). Skip gracefully.
5930
        if symbolFor(self, methodNode) == nil {
5931
            continue;
5932
        }
5933
5934
        try resolveMethodBody(self, methodNode);
5935
    }
5936
}
5937
5938
/// Resolve a method body shared by instance methods and standalone methods.
5939
/// Binds the receiver and parameters, then type-checks the body.
5940
unsafe fn resolveMethodBody 'arena (
5941
    self: &mut Resolver 'arena,
5942
    node: *ast::Node,
5943
) throws (ResolveError) {
5944
    let case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } = node.value
5945
        else panic "resolveMethodBody: invalid method";
5946
    let sym = symbolFor(self, node)
5947
        else throw emitError(self, node, ErrorKind::Internal);
5948
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
5949
        else panic "resolveMethodBody: expected value symbol";
5950
    let previous = self.regionScope;
5951
    set self.regionScope = fnType.regions;
5952
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body) catch error {
5953
        set self.regionScope = previous;
5954
        throw error;
5955
    };
5956
    set self.regionScope = previous;
5957
}
5958
5959
/// Resolve a standalone method declaration (signature only).
5960
/// Validates the receiver type and registers the method in the method table.
5961
5962
/// Extract the type name from a resolved receiver type node.
5963
unsafe fn receiverTypeName 'arena (
5964
    self: &mut Resolver 'arena,
5965
    receiverType: *ast::Node,
5966
) -> *[u8] throws (ResolveError) {
5967
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
5968
        receiverType.value
5969
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
5970
    let mut nameNode: *ast::Node = valueType;
5971
    match valueType.value {
5972
        case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(name)) => set nameNode = name,
5973
        case ast::NodeValue::TypeSig(ast::TypeSig::Applied { name, .. }) => set nameNode = name,
5974
        else => throw emitError(self, receiverType, ErrorKind::Internal),
5975
    }
5976
    let sym = symbolFor(self, nameNode)
5977
        else throw emitError(self, receiverType, ErrorKind::Internal);
5978
5979
    return sym.name;
5980
}
5981
5982
/// Resolve and register a standalone method declaration.
5983
unsafe fn resolveMethodDecl 'arena (
5984
    self: &mut Resolver 'arena,
5985
    node: *ast::Node,
5986
) throws (ResolveError) {
5987
    let case ast::NodeValue::MethodDecl { modifiers, .. } = node.value
5988
        else panic "resolveMethodDecl: invalid method";
5989
    let previous = self.regionScope;
5990
    set self.regionScope = try bindRegions(self, node, modifiers.regions);
5991
    try resolveMethodSignature(self, node) catch error {
5992
        set self.regionScope = previous;
5993
        throw error;
5994
    };
5995
    set self.regionScope = previous;
5996
}
5997
5998
/// Resolve a standalone method signature in its region environment.
5999
unsafe fn resolveMethodSignature 'arena (
6000
    self: &mut Resolver 'arena,
6001
    node: *ast::Node,
6002
) throws (ResolveError) {
6003
    let case ast::NodeValue::MethodDecl {
6004
        name, modifiers, receiverType, sig, ..
6005
    } = node.value else panic "resolveMethodSignature: invalid method";
6006
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
6007
    // nominal type.
6008
    let fullReceiverTy = try infer(self, receiverType);
6009
    let case Type::Pointer {
6010
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
6011
    } = fullReceiverTy
6012
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
6013
    let concreteType = *receiverTarget;
6014
    let case Type::Nominal(nominalTy) = concreteType
6015
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
6016
    try ensureNominalResolved(self, nominalTy, receiverType);
6017
6018
    // Get the type name from the inner type node's symbol.
6019
    let typeName = try receiverTypeName(self, receiverType);
6020
    let methodName = try nodeName(self, name);
6021
    let attrMask = resolveAttributes(modifiers.attrs);
6022
6023
    // Reject duplicate method for the same (type, name).
6024
    if let _ = findMethod(self, concreteType, methodName) {
6025
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
6026
    }
6027
6028
    // Resolve parameter types.
6029
    let a = alloc::arenaAllocator(self.arena);
6030
    let mut paramTypes: *mut [*Type] = &mut [];
6031
6032
    // Receiver is the first parameter.
6033
    let receiverPtrType = Type::Pointer {
6034
        class: receiverClass,
6035
        target: allocType(self, concreteType),
6036
        mutable: receiverMut,
6037
    };
6038
    paramTypes.append(allocType(self, receiverPtrType), a);
6039
6040
    for paramNode in sig.params {
6041
        let case ast::NodeValue::FnParam(param) = paramNode.value
6042
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
6043
        let paramTy = try resolveValueType(self, param.type);
6044
        paramTypes.append(allocType(self, paramTy), a);
6045
    }
6046
6047
    // Resolve return type.
6048
    let mut returnType = Type::Void;
6049
    if let retNode = sig.returnType {
6050
        set returnType = try resolveValueType(self, retNode);
6051
    }
6052
6053
    // Resolve throw list.
6054
    let mut throwTypes: *mut [*Type] = &mut [];
6055
    for throwNode in sig.throwList {
6056
        let throwTy = try resolveValueType(self, throwNode);
6057
        try validateErrorTag(self, throwNode, throwTy, &throwTypes[..]);
6058
        throwTypes.append(allocType(self, throwTy), a);
6059
    }
6060
6061
    let retTypePtr = allocType(self, returnType);
6062
    let throwList = &throwTypes[..];
6063
6064
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
6065
    // Full function type (receiver + params) for lowering.
6066
    let fullFnType = FnType {
6067
        regions: self.regionScope,
6068
        paramTypes: &paramTypes[..],
6069
        returnType: retTypePtr,
6070
        throwList,
6071
        isUnsafe,
6072
    };
6073
    let fullFnInfo = allocFnType(self, fullFnType);
6074
    let fnTy = Type::Fn(fullFnInfo);
6075
6076
    // Function type excluding receiver, for call arg checking.
6077
    let checkFnType = FnType {
6078
        regions: self.regionScope,
6079
        paramTypes: &paramTypes[1..],
6080
        returnType: retTypePtr,
6081
        throwList,
6082
        isUnsafe,
6083
    };
6084
6085
    // Create a symbol for the method without binding it into the module scope.
6086
    let sym = allocSymbol(self, SymbolData::Value {
6087
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
6088
    }, methodName, node, attrMask);
6089
6090
    setNodeSymbol(self, node, sym);
6091
    setNodeType(self, node, fnTy);
6092
    setNodeType(self, name, fnTy);
6093
6094
    // Register in the method table.
6095
    if self.methodsLen >= MAX_METHODS {
6096
        throw emitError(self, node, ErrorKind::Internal);
6097
    }
6098
    set self.methods[self.methodsLen] = MethodEntry {
6099
        moduleId: self.currentMod,
6100
        concreteType,
6101
        concreteTypeName: typeName,
6102
        name: methodName,
6103
        fnType: allocFnType(self, checkFnType),
6104
        mutable: receiverMut,
6105
        receiverClass,
6106
        symbolId: sym.id,
6107
        fullFnType: fullFnInfo,
6108
    };
6109
    set self.methodsLen += 1;
6110
}
6111
6112
/// Look up an instance entry by trait and concrete type.
6113
unsafe fn findInstance 'arena (self: &Resolver 'arena, traitInfo: *unsafe TraitType, concreteType: Type) -> ?*unsafe InstanceEntry {
6114
    for i in 0..self.instancesLen {
6115
        let entry: *unsafe InstanceEntry = &self.instances[i];
6116
        if entry.traitType == traitInfo and erasedTypesEqual(entry.concreteType, concreteType) {
6117
            return entry;
6118
        }
6119
    }
6120
    return nil;
6121
}
6122
6123
/// Look up a standalone method by concrete type and name.
6124
export unsafe fn findMethod 'arena (self: &Resolver 'arena, concreteType: Type, name: *[u8]) -> ?*unsafe MethodEntry {
6125
    for i in 0..self.methodsLen {
6126
        let entry: *unsafe MethodEntry = &self.methods[i];
6127
        if erasedTypesEqual(entry.concreteType, concreteType) and entry.name == name {
6128
            return entry;
6129
        }
6130
    }
6131
    return nil;
6132
}
6133
6134
/// Look up standalone method metadata by its resolver-local symbol identity.
6135
export fn findMethodBySymbol 'arena (self: &Resolver 'arena, symbolId: u32) -> ?MethodEntry {
6136
    for i in 0..self.methodsLen {
6137
        let entry = self.methods[i];
6138
        if entry.symbolId == symbolId {
6139
            return entry;
6140
        }
6141
    }
6142
    return nil;
6143
}
6144
6145
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
6146
unsafe fn resolveUnionBody 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
6147
    throws (ResolveError)
6148
{
6149
    let previous = self.regionScope;
6150
    set self.regionScope = try bindRegions(self, node, decl.regions);
6151
    try resolveUnionContents(self, node, decl) catch error {
6152
        set self.regionScope = previous;
6153
        throw error;
6154
    };
6155
    set self.regionScope = previous;
6156
}
6157
6158
/// Resolve union contents in the declaration's region environment.
6159
unsafe fn resolveUnionContents 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::UnionDecl)
6160
    throws (ResolveError)
6161
{
6162
    // Get the type symbol that was bound to this declaration node.
6163
    // If there's no symbol, it's because an earlier phase failed.
6164
    let sym = symbolFor(self, node)
6165
        else return;
6166
    let case SymbolData::Type(nominalTy) = sym.data
6167
        else panic "resolveUnionBody: unexpected symbol data";
6168
6169
    // Check if already resolved, in which case there's no need to
6170
    // do it again.
6171
    if let case NominalType::Union(_) = *nominalTy {
6172
        return;
6173
    }
6174
    let a = alloc::arenaAllocator(self.arena);
6175
    let mut variants: *mut [UnionVariant] = &mut [];
6176
6177
    let markers = try resolveOwnershipMarkers(self, decl.derives);
6178
    set *nominalTy = NominalType::Resolving(node);
6179
6180
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
6181
    let mut iota: u32 = 0;
6182
    for variantNode, i in decl.variants {
6183
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
6184
            else panic "resolveUnionBody: invalid union variant";
6185
        let variantName = try nodeName(self, variantDecl.name);
6186
        // Resolve the variant's payload type if present.
6187
        let mut variantType = Type::Void;
6188
        if let typeNode = variantDecl.type {
6189
            set variantType = try infer(self, typeNode);
6190
            try ensureStorableType(self, typeNode, variantType);
6191
            try ensureTypeResolved(self, variantType, typeNode);
6192
        }
6193
        // Process the variant's explicit discriminant value if present.
6194
        try visitOptional(self, variantDecl.value, variantType);
6195
        let tag = variantTag(variantDecl, &mut iota);
6196
        // Create a symbol for this variant.
6197
        let data = SymbolData::Variant { type: variantType, decl: node, ordinal: i, index: tag };
6198
        let variantSym = allocSymbol(self, data, variantName, variantNode, 0);
6199
6200
        variants.append(UnionVariant {
6201
            name: variantName,
6202
            valueType: variantType,
6203
            symbol: variantSym,
6204
        }, a);
6205
    }
6206
    if markers.copy {
6207
        for variant in &variants[..] {
6208
            if not isCopy(variant.valueType) {
6209
                throw emitError(self, node, ErrorKind::CopyContainsNonCopy);
6210
            }
6211
        }
6212
    }
6213
    let info = computeUnionLayout(&variants[..]);
6214
6215
    // Update the nominal type with the resolved variants.
6216
    set *nominalTy = NominalType::Union(UnionType {
6217
        regions: self.regionScope,
6218
        application: nil,
6219
        variants: (&variants[..]) as *unsafe [UnionVariant],
6220
        layout: allocLayout(self, info.layout),
6221
        valOffset: info.valOffset,
6222
        isAllVoid: info.isAllVoid,
6223
        declaredLinear: markers.linear,
6224
        declaredCopy: markers.copy,
6225
    });
6226
}
6227
6228
/// Check whether an attributed module or import is active in this build.
6229
/// A test module is active only when its source module was registered.
6230
fn shouldAnalyzeModule 'arena (self: &Resolver 'arena, attrs: ?ast::Attributes, name: ?*[u8]) -> bool {
6231
    if let attributes = attrs {
6232
        if ast::attributesContains(&attributes, ast::Attribute::Test) {
6233
            if not self.config.buildTest {
6234
                return false;
6235
            }
6236
            if let moduleName = name {
6237
                return findChildModule(self, moduleName, self.currentMod) <> nil;
6238
            }
6239
        }
6240
    }
6241
    return true;
6242
}
6243
6244
/// Analyze a module during the graph analysis phase.
6245
unsafe fn resolveModGraph 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
6246
    throws (ResolveError)
6247
{
6248
    let modName = try nodeName(self, decl.name);
6249
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
6250
        return;
6251
    }
6252
    let attrMask = resolveAttributes(decl.attrs);
6253
    try ensureDefaultAttrNotAllowed(self, node, attrMask);
6254
    let submod = try enterSubModule(self, modName, node);
6255
6256
    // Bind the module symbol in the outer scope, ie. where the `mod` statement is.
6257
    try bindModuleIdent(self, submod.entry, submod.newScope, submod.root, attrMask, submod.prevScope);
6258
    let case ast::NodeValue::Block(block) = submod.root.value
6259
        else panic "resolveModGraph: expected block for module root";
6260
    try resolveModuleGraph(self, &block);
6261
6262
    exitModuleScope(self, submod);
6263
}
6264
6265
/// Analyze a module in the declaration phase.
6266
unsafe fn resolveModDecl 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Mod)
6267
    throws (ResolveError)
6268
{
6269
    // Find module under the current module.
6270
    let modName = try nodeName(self, decl.name);
6271
    if not shouldAnalyzeModule(self, decl.attrs, modName) {
6272
        return;
6273
    }
6274
    let submod = try enterSubModule(self, modName, node);
6275
    let case ast::NodeValue::Block(block) = submod.root.value
6276
        else panic "resolveModDecl: expected block for module root";
6277
    try resolveModuleDecls(self, &block);
6278
6279
    exitModuleScope(self, submod);
6280
}
6281
6282
/// Analyze a `use` statement and create a symbol for the imported module.
6283
unsafe fn resolveUse 'arena (self: &mut Resolver 'arena, node: *ast::Node, decl: ast::Use) -> Type
6284
    throws (ResolveError)
6285
{
6286
    if not shouldAnalyzeModule(self, decl.attrs, nil) {
6287
        return Type::Void;
6288
    }
6289
    let resolved = try resolveModulePath(self, decl.path);
6290
    let attrMask = resolveAttributes(decl.attrs);
6291
6292
    if decl.wildcard {
6293
        // Import all public symbols from the target module.
6294
        for i in 0..resolved.scope.symbolsLen {
6295
            let sym = resolved.scope.symbols[i];
6296
            if ast::hasAttribute(sym.attrs, ast::Attribute::Export) {
6297
                if let existing = findSymbolInScope(self.scope, sym.name) {
6298
                    if existing == sym {
6299
                        continue;
6300
                    }
6301
                }
6302
                let scope = self.scope;
6303
                try addSymbolToScope(self, sym, scope, node);
6304
            }
6305
        }
6306
    } else {
6307
        // Regular module import.
6308
        let scope = self.scope;
6309
        try bindModuleIdent(self, resolved.entry, resolved.scope, node, attrMask, scope);
6310
    }
6311
    return Type::Void;
6312
}
6313
6314
/// Analyze a standard `if` statement.
6315
unsafe fn resolveIf 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::If) -> Type
6316
    throws (ResolveError)
6317
{
6318
    try checkBoolean(self, cond.condition);
6319
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
6320
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
6321
6322
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
6323
}
6324
6325
/// Analyze a conditional expression.
6326
unsafe fn resolveCondExpr 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::CondExpr, hint: Type) -> Type
6327
    throws (ResolveError)
6328
{
6329
    try checkBoolean(self, cond.condition);
6330
    let thenValue = try visit(self, cond.thenExpr, hint);
6331
    let thenTy = assignableValueType(self, cond.thenExpr, thenValue);
6332
    let elseValue = try visit(self, cond.elseExpr, hint);
6333
    let elseTy = assignableValueType(self, cond.elseExpr, elseValue);
6334
6335
    // Either branch may supply the concrete type for an otherwise context-
6336
    // dependent expression, such as an unsuffixed integer or `nil`.
6337
    if let coercion = isAssignable(self, thenTy, elseTy, cond.elseExpr) {
6338
        setNodeCoercion(self, cond.elseExpr, coercion);
6339
        return setNodeType(self, node, thenTy);
6340
    }
6341
    if let coercion = isAssignable(self, elseTy, thenTy, cond.thenExpr) {
6342
        setNodeCoercion(self, cond.thenExpr, coercion);
6343
        return setNodeType(self, node, elseTy);
6344
    }
6345
    try expectAssignable(self, thenTy, elseTy, cond.elseExpr);
6346
6347
    return setNodeType(self, node, thenTy);
6348
}
6349
6350
/// Analyze a pattern match structure (used by if-let, while-let).
6351
unsafe fn resolvePatternMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, pat: &ast::PatternMatch)
6352
    throws (ResolveError)
6353
{
6354
    match pat.kind {
6355
        case ast::PatternKind::Case => {
6356
            // Analyze pattern against scrutinee type.
6357
            let scrutineeTy = try infer(self, pat.scrutinee);
6358
            if isUnsafePointerType(scrutineeTy) {
6359
                try requireUnsafe(self, pat.scrutinee);
6360
            }
6361
            let subject = unwrapMatchSubject(scrutineeTy);
6362
            try resolveCasePattern(self, pat.pattern, subject.effectiveTy, IdentMode::Compare, subject.by);
6363
        }
6364
        case ast::PatternKind::Binding => {
6365
            // Scrutinee must be optional, bind the payload.
6366
            let scrutineeTy = try checkOptional(self, pat.scrutinee);
6367
            let payloadTy = *scrutineeTy;
6368
6369
            try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
6370
            setNodeType(self, pat.pattern, payloadTy);
6371
        }
6372
    }
6373
    if let guard = pat.guard {
6374
        try checkBoolean(self, guard);
6375
    }
6376
}
6377
6378
/// Analyze an `if let` or `if let case` pattern binding.
6379
unsafe fn resolveIfLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, cond: ast::IfLet) -> Type
6380
    throws (ResolveError)
6381
{
6382
    enterScope(self, node);
6383
    try resolvePatternMatch(self, node, &cond.pattern);
6384
6385
    let thenTy = try visit(self, cond.thenBranch, Type::Void);
6386
    exitScope(self);
6387
6388
    let elseTy = try visitOptional(self, cond.elseBranch, Type::Void);
6389
6390
    return setNodeType(self, node, unifyBranches(thenTy, elseTy));
6391
}
6392
6393
/// Controls how bare identifiers are handled in case patterns.
6394
union IdentMode: Copy {
6395
    /// Identifier is a value to compare against.
6396
    Compare,
6397
    /// Identifier introduces a new binding.
6398
    Bind,
6399
}
6400
6401
/// Check whether a pattern node is a destructuring pattern that looks
6402
/// through structure (union variant, record literal, scope access).
6403
/// Identifiers, placeholders, and plain literals are not destructuring.
6404
export fn isDestructuringPattern(pattern: *ast::Node) -> bool {
6405
    match pattern.value {
6406
        case ast::NodeValue::Call(_),
6407
             ast::NodeValue::RecordLit(_),
6408
             ast::NodeValue::ScopeAccess(_) => return true,
6409
        else => return false,
6410
    }
6411
}
6412
6413
/// Analyze a case pattern for match, if-case, let-case, or while-case.
6414
///
6415
/// At the top level, bare identifiers are compared against existing values.
6416
/// Inside destructuring patterns (arrays, records), identifiers become bindings.
6417
unsafe fn resolveCasePattern 'arena (
6418
    self: &mut Resolver 'arena,
6419
    pattern: *ast::Node,
6420
    scrutineeTy: Type,
6421
    mode: IdentMode,
6422
    matchBy: MatchBy
6423
) throws (ResolveError) {
6424
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
6425
        if isUnsafePointerType(scrutineeTy) {
6426
            try requireUnsafe(self, pattern);
6427
        }
6428
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
6429
        return;
6430
    }
6431
    // TODO: Collapse these nested matches.
6432
    match scrutineeTy {
6433
        case Type::Nominal(info) => {
6434
            try ensureNominalResolved(self, info, pattern);
6435
6436
            match *info {
6437
                case NominalType::Union(unionType) => {
6438
                    try resolveUnionPattern(self, pattern, scrutineeTy, unionType, matchBy);
6439
                    return;
6440
                }
6441
                case NominalType::Record(recInfo) => {
6442
                    match pattern.value {
6443
                        case ast::NodeValue::Call(_), ast::NodeValue::RecordLit(_) => {
6444
                            try resolveRecordPattern(self, pattern, scrutineeTy, recInfo, matchBy);
6445
                            return;
6446
                        } else => {}
6447
                    }
6448
                } else => {}
6449
            }
6450
        }
6451
        case Type::Array(arrayInfo) => {
6452
            if let case ast::NodeValue::ArrayLit(items) = pattern.value {
6453
                if items.len as u32 <> arrayInfo.length {
6454
                    throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(
6455
                        CountMismatch { expected: arrayInfo.length, actual: items.len as u32 }
6456
                    ));
6457
                }
6458
                let elemTy = *arrayInfo.item;
6459
                for item in items {
6460
                    try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy);
6461
                }
6462
                setNodeType(self, pattern, scrutineeTy);
6463
                return;
6464
            }
6465
        } else => {}
6466
    }
6467
    // Handle non-binding patterns (literals, placeholders) and bindings.
6468
    match pattern.value {
6469
        case ast::NodeValue::Placeholder => {
6470
            // Placeholder matches without introducing bindings.
6471
        }
6472
        case ast::NodeValue::Ident(_) => {
6473
            match mode {
6474
                case IdentMode::Bind => try bindPatternVar(self, pattern, scrutineeTy, matchBy),
6475
                case IdentMode::Compare => try checkAssignable(self, pattern, scrutineeTy),
6476
            }
6477
        }
6478
        else => {
6479
            // Literals and other expressions: check type compatibility.
6480
            try checkAssignable(self, pattern, scrutineeTy);
6481
        }
6482
    }
6483
}
6484
6485
/// Analyze a traditional `while` loop.
6486
unsafe fn resolveWhile 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::While) -> Type
6487
    throws (ResolveError)
6488
{
6489
    try checkBoolean(self, loopNode.condition);
6490
    let loopTy = try visitLoop(self, loopNode.body);
6491
    try visitOptional(self, loopNode.elseBranch, Type::Void);
6492
6493
    if loopNode.condition.value == ast::NodeValue::Bool(true) {
6494
        return setNodeType(self, node, loopTy);
6495
    }
6496
    return setNodeType(self, node, Type::Void);
6497
}
6498
6499
/// Analyze a `while let` loop with pattern binding.
6500
unsafe fn resolveWhileLet 'arena (self: &mut Resolver 'arena, node: *ast::Node, loopNode: ast::WhileLet) -> Type
6501
    throws (ResolveError)
6502
{
6503
    enterScope(self, node);
6504
    try resolvePatternMatch(self, node, &loopNode.pattern);
6505
6506
    try visitLoop(self, loopNode.body);
6507
    exitScope(self);
6508
6509
    try visitOptional(self, loopNode.elseBranch, Type::Void);
6510
6511
    return setNodeType(self, node, Type::Void);
6512
}
6513
6514
/// Store complete iteration metadata and return the loop binding type.
6515
fn resolveForInfo 'arena (
6516
    self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For, iterableTy: Type
6517
) -> Type throws (ResolveError) {
6518
    // Extract binding names for the lowerer.
6519
    let mut bindingName: ?*[u8] = nil;
6520
    if let case ast::NodeValue::Ident(name) = forStmt.binding.value {
6521
        set bindingName = name;
6522
    }
6523
    let mut indexName: ?*[u8] = nil;
6524
    if let idx = forStmt.index {
6525
        if let case ast::NodeValue::Ident(name) = idx.value {
6526
            set indexName = name;
6527
        }
6528
    }
6529
    // Extract item type and store pre-computed loop metadata for the lowerer.
6530
    match iterableTy {
6531
        case Type::Slice { item, class, .. } => {
6532
            if class == types::PointerClass::Unsafe {
6533
                try requireUnsafe(self, forStmt.iterable);
6534
            }
6535
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6536
                elemType: item, length: nil, bindingName, indexName
6537
            });
6538
            return *item;
6539
        }
6540
        case Type::Range { start, .. } => {
6541
            // Iterable ranges must have a start, and since we enforce type
6542
            // equality for start and end, that is always the item type.
6543
            let valType = start else {
6544
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6545
            };
6546
            let case ast::NodeValue::Range(range) = forStmt.iterable.value else {
6547
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
6548
            };
6549
            setForLoopInfo(self, node, ForLoopInfo::Range {
6550
                valType, range, bindingName, indexName
6551
            });
6552
            return *valType;
6553
        }
6554
        case Type::Array(arrayInfo) => {
6555
            setForLoopInfo(self, node, ForLoopInfo::Collection {
6556
                elemType: arrayInfo.item,
6557
                length: arrayInfo.length,
6558
                bindingName,
6559
                indexName,
6560
            });
6561
            return *arrayInfo.item;
6562
        }
6563
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
6564
    }
6565
}
6566
6567
/// Analyze a `for` loop, binding iteration variables.
6568
unsafe fn resolveFor 'arena (self: &mut Resolver 'arena, node: *ast::Node, forStmt: ast::For) -> Type
6569
    throws (ResolveError)
6570
{
6571
    let iterableTy = try infer(self, forStmt.iterable);
6572
    let itemTy = try resolveForInfo(self, node, forStmt, iterableTy);
6573
    enterScope(self, node);
6574
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
6575
6576
    if let pat = forStmt.index {
6577
        try bindForLoopPattern(self, pat, Type::U32, false);
6578
    }
6579
    // The lowerer always creates at least one internal variable for iteration,
6580
    // even when the binding is a placeholder or no explicit index is given.
6581
    if let owner = self.currentFnNode {
6582
        set self.nodeData.entries[owner.id].localCount += 1;
6583
    }
6584
    try visitLoop(self, forStmt.body);
6585
    exitScope(self);
6586
6587
    try visitOptional(self, forStmt.elseBranch, Type::Void);
6588
6589
    return setNodeType(self, node, Type::Void);
6590
}
6591
6592
/// Get the node within a pattern that carries the `UnionVariant` extra.
6593
/// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the
6594
/// type name, and for `Call` it is the callee.
6595
export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node {
6596
    match pattern.value {
6597
        case ast::NodeValue::ScopeAccess(_) => return pattern,
6598
        case ast::NodeValue::RecordLit(lit) => return lit.typeName,
6599
        case ast::NodeValue::Call(call) => return call.callee,
6600
        else => return nil,
6601
    }
6602
}
6603
6604
/// Get the i-th sub-pattern element from a compound pattern.
6605
/// For `RecordLit` this is the i-th field's value; for `Call` it is the
6606
/// i-th argument.
6607
fn patternSubElement(pattern: *ast::Node, idx: u32) -> ?*ast::Node {
6608
    match pattern.value {
6609
        case ast::NodeValue::RecordLit(lit) => {
6610
            if idx < lit.fields.len as u32 {
6611
                if let case ast::NodeValue::RecordLitField(field) = lit.fields[idx].value {
6612
                    return field.value;
6613
                }
6614
            }
6615
        }
6616
        case ast::NodeValue::Call(call) => {
6617
            if idx < call.args.len as u32 {
6618
                return call.args[idx];
6619
            }
6620
        }
6621
        else => {}
6622
    }
6623
    return nil;
6624
}
6625
6626
/// Get the number of sub-pattern elements in a compound pattern.
6627
fn patternSubCount(pattern: *ast::Node) -> u32 {
6628
    match pattern.value {
6629
        case ast::NodeValue::RecordLit(lit) => return lit.fields.len as u32,
6630
        case ast::NodeValue::Call(call) => return call.args.len as u32,
6631
        else => return 0,
6632
    }
6633
}
6634
6635
/// Check whether a pattern contains nested sub-patterns that further
6636
/// refine the match beyond the outer variant (e.g. nested union variant
6637
/// tests or literal comparisons). Used to allow the same outer variant
6638
/// to appear in multiple match arms.
6639
fn hasNestedRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
6640
    for i in 0..patternSubCount(pattern) {
6641
        if let sub = patternSubElement(pattern, i) {
6642
            if isRefiningPattern(self, sub) {
6643
                return true;
6644
            }
6645
        }
6646
    }
6647
    return false;
6648
}
6649
6650
/// Check whether a single pattern node is a refining pattern that tests
6651
/// a value rather than just binding it. Union variants, literals, and
6652
/// scope accesses are refining; identifiers, placeholders, and plain
6653
/// record destructurings are not.
6654
fn isRefiningPattern 'arena (self: &Resolver 'arena, pattern: *ast::Node) -> bool {
6655
    match pattern.value {
6656
        case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder =>
6657
            return false,
6658
        case ast::NodeValue::RecordLit(_), ast::NodeValue::Call(_) => {
6659
            if let keyNode = patternVariantKeyNode(pattern) {
6660
                if let case NodeExtra::UnionVariant { .. } = self.nodeData.entries[keyNode.id].extra {
6661
                    return true;
6662
                }
6663
            }
6664
            // Plain record destructuring / non-variant call is not directly
6665
            // refining; recurse to check sub-patterns.
6666
            return hasNestedRefiningPattern(self, pattern);
6667
        }
6668
        case ast::NodeValue::ArrayLit(items) => {
6669
            for item in items {
6670
                if isRefiningPattern(self, item) {
6671
                    return true;
6672
                }
6673
            }
6674
            return false;
6675
        }
6676
        case ast::NodeValue::ScopeAccess(_) =>
6677
            return true,
6678
        else =>
6679
            return true,
6680
    }
6681
}
6682
6683
/// Check whether any pattern in a case prong matches unconditionally.
6684
/// A plain `_` or an all-binding array pattern (e.g. `[x, y]`) qualifies.
6685
/// Note: top-level identifiers in `case` are comparisons, not bindings,
6686
/// so they do not count as wildcards.
6687
fn hasWildcardPattern(patterns: *[*ast::Node]) -> bool {
6688
    for pattern in patterns {
6689
        match pattern.value {
6690
            case ast::NodeValue::Placeholder => return true,
6691
            case ast::NodeValue::ArrayLit(items) => {
6692
                if isIrrefutableArrayPattern(items) {
6693
                    return true;
6694
                }
6695
            }
6696
            else => {}
6697
        }
6698
    }
6699
    return false;
6700
}
6701
6702
/// Check whether all elements of an array pattern are irrefutable.
6703
/// Inside array patterns, identifiers are bindings, not comparisons.
6704
fn isIrrefutableArrayPattern(items: *[*ast::Node]) -> bool {
6705
    for item in items {
6706
        match item.value {
6707
            case ast::NodeValue::Ident(_), ast::NodeValue::Placeholder => {}
6708
            case ast::NodeValue::ArrayLit(inner) => {
6709
                if not isIrrefutableArrayPattern(inner) {
6710
                    return false;
6711
                }
6712
            }
6713
            else => return false,
6714
        }
6715
    }
6716
    return true;
6717
}
6718
6719
/// Classify a match prong and reject unreachable prongs after a catch-all.
6720
/// Record whether lowering can omit the prong's pattern test.
6721
fn checkMatchProng 'arena (
6722
    self: &mut Resolver 'arena,
6723
    prongNode: *ast::Node,
6724
    prong: ast::MatchProng,
6725
    subjectTy: Type,
6726
    state: &mut MatchState
6727
) throws (ResolveError) {
6728
    // Whether this prong is catch-all.
6729
    let mut isCatchAll = false;
6730
6731
    if prong.guard <> nil {
6732
        set state.isConst = false;
6733
    } else {
6734
        match prong.arm {
6735
            case ast::ProngArm::Binding(_) => {
6736
                // For optionals, a binding matches only a present value.
6737
                set isCatchAll = not isOptionalType(subjectTy);
6738
            },
6739
            case ast::ProngArm::Else => set isCatchAll = true,
6740
            case ast::ProngArm::Case(patterns) => set isCatchAll = hasWildcardPattern(patterns),
6741
        }
6742
    }
6743
    if state.catchAll {
6744
        if isCatchAll {
6745
            throw emitError(self, prongNode, ErrorKind::DuplicateCatchAll);
6746
        }
6747
        throw emitError(self, prongNode, ErrorKind::CatchAllMustBeLast);
6748
    }
6749
    if isCatchAll {
6750
        set state.catchAll = true;
6751
    }
6752
    setProngCatchAll(self, prongNode, isCatchAll);
6753
6754
}
6755
6756
/// Analyze a `match` expression. Dispatches to specialized functions based on
6757
/// the subject type.
6758
unsafe fn resolveMatch 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match) -> Type
6759
    throws (ResolveError)
6760
{
6761
    let subjectTy = try infer(self, sw.subject);
6762
    if isUnsafePointerType(subjectTy) {
6763
        try requireUnsafe(self, sw.subject);
6764
    }
6765
    let subject = unwrapMatchSubject(subjectTy);
6766
6767
    if let case Type::Optional(inner) = subject.effectiveTy {
6768
        try resolveMatchOptional(self, node, sw, inner, subject.by);
6769
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
6770
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
6771
    } else {
6772
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy, subject.by);
6773
    }
6774
6775
    // Mark last non-guarded prong as exhaustive.
6776
    let lastProng = sw.prongs[sw.prongs.len - 1];
6777
    let case ast::NodeValue::MatchProng(p) = lastProng.value
6778
        else panic "resolveMatch: expected match prong";
6779
    if p.guard == nil {
6780
        setProngCatchAll(self, lastProng, true);
6781
    }
6782
    let ty = typeFor(self, node) else {
6783
        return Type::Void;
6784
    };
6785
    return ty;
6786
}
6787
6788
/// Analyze a `match` expression on an optional subject.
6789
unsafe fn resolveMatchOptional 'arena (
6790
    self: &mut Resolver 'arena,
6791
    node: *ast::Node,
6792
    sw: ast::Match,
6793
    innerTy: *Type,
6794
    matchBy: MatchBy
6795
) -> Type throws (ResolveError)
6796
{
6797
    let subjectTy = Type::Optional(innerTy);
6798
    let prongs = sw.prongs;
6799
    let mut hasValue = false;
6800
    let mut hasNil = false;
6801
    let mut state = MatchState { catchAll: false, isConst: false };
6802
    let mut matchType = Type::Never;
6803
6804
    for prongNode in prongs {
6805
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6806
            else panic "resolveMatchOptional: expected match prong";
6807
6808
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6809
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6810
6811
        // Track coverage. Guarded prongs don't count as covering a case.
6812
        if prong.guard == nil {
6813
            if let case ast::ProngArm::Binding(_) = prong.arm {
6814
                if hasValue {
6815
                    throw emitError(self, prongNode, ErrorKind::DuplicateMatchPattern);
6816
                }
6817
                set hasValue = true;
6818
            } else if let case ast::ProngArm::Case(patterns) = prong.arm {
6819
                for pat in patterns {
6820
                    if let case ast::NodeValue::Nil = pat.value {
6821
                        if hasNil {
6822
                            throw emitError(self, pat, ErrorKind::DuplicateMatchPattern);
6823
                        }
6824
                        set hasNil = true;
6825
                    }
6826
                }
6827
            }
6828
        }
6829
    }
6830
6831
    // Check exhaustiveness.
6832
    if not state.catchAll {
6833
        if not hasValue {
6834
            throw emitError(self, node, ErrorKind::OptionalMatchMissingValue);
6835
        }
6836
        if not hasNil {
6837
            throw emitError(self, node, ErrorKind::OptionalMatchMissingNil);
6838
        }
6839
    } else if hasValue and hasNil {
6840
        throw emitError(self, node, ErrorKind::UnreachableElse);
6841
    }
6842
    return setNodeType(self, node, matchType);
6843
}
6844
6845
/// Analyze a `match` expression on a union subject.
6846
unsafe fn resolveMatchUnion 'arena (
6847
    self: &mut Resolver 'arena,
6848
    node: *ast::Node,
6849
    sw: ast::Match,
6850
    subjectTy: Type,
6851
    info: UnionType,
6852
    matchBy: MatchBy
6853
) -> Type throws (ResolveError) {
6854
    let prongs = sw.prongs;
6855
    let mut covered: [bool; MAX_UNION_VARIANTS] = [false; MAX_UNION_VARIANTS];
6856
    let mut coveredCount: u32 = 0;
6857
    let mut state = MatchState { catchAll: false, isConst: false };
6858
    let mut matchType = Type::Never;
6859
6860
    for prongNode in prongs {
6861
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6862
            else panic "resolveMatchUnion: expected match prong";
6863
6864
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6865
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6866
6867
        // Guarded prongs don't count as covering. Patterns with nested
6868
        // refining sub-patterns (e.g. matching different inner union variants)
6869
        // don't count as duplicates or as fully covering.
6870
        if prong.guard == nil {
6871
            if let case ast::ProngArm::Case(patterns) = prong.arm {
6872
                for pattern in patterns {
6873
                    if let case NodeExtra::UnionVariant { ordinal: ix, .. } = self.nodeData.entries[pattern.id].extra {
6874
                        if not hasNestedRefiningPattern(self, pattern) {
6875
                            if covered[ix] {
6876
                                throw emitError(self, pattern, ErrorKind::DuplicateMatchPattern);
6877
                            }
6878
                            set covered[ix] = true;
6879
                            set coveredCount += 1;
6880
                        }
6881
                    }
6882
                }
6883
            }
6884
        }
6885
    }
6886
    // Check that all variants are covered.
6887
    if not state.catchAll {
6888
        for variant, i in info.variants {
6889
            if not covered[i] {
6890
                throw emitError(
6891
                    self, node, ErrorKind::UnionMatchNonExhaustive(variant.name)
6892
                );
6893
            }
6894
        }
6895
    } else if coveredCount == info.variants.len as u32 {
6896
        throw emitError(self, node, ErrorKind::UnreachableElse);
6897
    }
6898
    return setNodeType(self, node, matchType);
6899
}
6900
6901
/// Analyze a `match` expression on a generic subject type. Requires exhaustiveness:
6902
/// booleans must cover both `true` and `false`, other types require a catch-all.
6903
unsafe fn resolveMatchGeneric 'arena (self: &mut Resolver 'arena, node: *ast::Node, sw: ast::Match, subjectTy: Type, matchBy: MatchBy) -> Type
6904
    throws (ResolveError)
6905
{
6906
    let prongs = sw.prongs;
6907
    let mut state = MatchState { catchAll: false, isConst: true };
6908
    let mut matchType = Type::Never;
6909
    let mut hasTrue = false;
6910
    let mut hasFalse = false;
6911
    let mut hasConstCase = false;
6912
6913
    for prongNode in prongs {
6914
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
6915
            else panic "resolveMatchGeneric: expected match prong";
6916
6917
        try checkMatchProng(self, prongNode, prong, subjectTy, &mut state);
6918
        set matchType = try visitMatchProng(self, prongNode, prong, subjectTy, matchType, matchBy);
6919
        // Track boolean coverage. Guarded prongs don't count as covering.
6920
        if let case ast::ProngArm::Case(patterns) = prong.arm {
6921
            for p in patterns {
6922
                if prong.guard == nil {
6923
                    if let case ast::NodeValue::Bool(val) = p.value {
6924
                        if (val and hasTrue) or (not val and hasFalse) {
6925
                            throw emitError(self, p, ErrorKind::DuplicateMatchPattern);
6926
                        }
6927
                        if val {
6928
                            set hasTrue = true;
6929
                        } else {
6930
                            set hasFalse = true;
6931
                        }
6932
                    }
6933
                }
6934
                // Scalar constant patterns allow the match to be lowered
6935
                // to a switch instruction.
6936
                if let c = constValueEntry(self, p) {
6937
                    match c {
6938
                        case ConstValue::Bool(_), ConstValue::Char(_), ConstValue::Int(_) =>
6939
                            set hasConstCase = true,
6940
                        else =>
6941
                            set state.isConst = false,
6942
                    }
6943
                }
6944
            }
6945
        }
6946
    }
6947
6948
    // Check exhaustiveness.
6949
    if not state.catchAll {
6950
        if let case Type::Bool = subjectTy {
6951
            if not hasTrue {
6952
                throw emitError(self, node, ErrorKind::BoolMatchMissing(true));
6953
            }
6954
            if not hasFalse {
6955
                throw emitError(self, node, ErrorKind::BoolMatchMissing(false));
6956
            }
6957
        } else {
6958
            throw emitError(self, node, ErrorKind::MatchNonExhaustive);
6959
        }
6960
    } else if let case Type::Bool = subjectTy {
6961
        if hasTrue and hasFalse {
6962
            throw emitError(self, node, ErrorKind::UnreachableElse);
6963
        }
6964
    }
6965
    setMatchConst(self, node, state.isConst and hasConstCase);
6966
6967
    return setNodeType(self, node, matchType);
6968
}
6969
6970
/// Analyze a single `match` prong branch. Returns the unified match type.
6971
unsafe fn visitMatchProng 'arena (
6972
    self: &mut Resolver 'arena,
6973
    node: *ast::Node,
6974
    prongNode: ast::MatchProng,
6975
    subjectTy: Type,
6976
    matchType: Type,
6977
    matchBy: MatchBy
6978
) -> Type throws (ResolveError) {
6979
    enterScope(self, node);
6980
    let prongTy = try resolveMatchProngBody(self, prongNode, subjectTy, matchBy) catch e {
6981
        exitScope(self);
6982
        throw e;
6983
    };
6984
    exitScope(self);
6985
    setNodeType(self, node, prongTy);
6986
6987
    return unifyBranches(matchType, prongTy);
6988
}
6989
6990
/// Analyze the contents of a `match` prong while inside the prong scope.
6991
unsafe fn resolveMatchProngBody 'arena (
6992
    self: &mut Resolver 'arena,
6993
    prong: ast::MatchProng,
6994
    subjectTy: Type,
6995
    matchBy: MatchBy
6996
) -> Type throws (ResolveError) {
6997
    match prong.arm {
6998
        case ast::ProngArm::Binding(pat) => {
6999
            // For optionals, bind the unwrapped inner type.
7000
            let mut bindTy = subjectTy;
7001
            if let case Type::Optional(inner) = subjectTy {
7002
                set bindTy = *inner;
7003
            }
7004
            try bindPatternVar(self, pat, bindTy, matchBy);
7005
        }
7006
        case ast::ProngArm::Case(patterns) => {
7007
            for pattern in patterns {
7008
                try resolveCasePattern(self, pattern, subjectTy, IdentMode::Compare, matchBy);
7009
            }
7010
        }
7011
        case ast::ProngArm::Else => {}
7012
    }
7013
    if let g = prong.guard {
7014
        try checkBoolean(self, g);
7015
    }
7016
    return try visit(self, prong.body, Type::Void);
7017
}
7018
7019
/// Ensure a scope access pattern references a compatible union variant.
7020
unsafe fn resolveUnionScopePattern 'arena (
7021
    self: &mut Resolver 'arena,
7022
    pattern: *ast::Node,
7023
    access: ast::Access,
7024
    subjectTy: Type,
7025
    unionType: UnionType
7026
) throws (ResolveError) {
7027
    let patternTy = try visit(self, pattern, subjectTy);
7028
    if not isComparable(patternTy, subjectTy) {
7029
        throw emitTypeMismatch(self, pattern, TypeMismatch {
7030
            expected: subjectTy,
7031
            actual: patternTy,
7032
        });
7033
    }
7034
    let case NodeExtra::UnionVariant { ordinal: index, .. } = self.nodeData.entries[pattern.id].extra else {
7035
        throw emitError(self, pattern, ErrorKind::Internal);
7036
    };
7037
    let variant = &unionType.variants[index];
7038
    // If this variant has a payload, throw an error, since the user hasn't
7039
    // provided one.
7040
    if variant.valueType <> Type::Void {
7041
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadMissing(variant.name));
7042
    }
7043
}
7044
7045
/// Validate and bind a union constructor call used as a `match` pattern.
7046
unsafe fn resolveUnionCallPattern 'arena (
7047
    self: &mut Resolver 'arena,
7048
    pattern: *ast::Node,
7049
    call: ast::Call,
7050
    subjectTy: Type,
7051
    unionType: UnionType,
7052
    matchBy: MatchBy
7053
) throws (ResolveError) {
7054
    let calleeTy = try checkEqual(self, call.callee, subjectTy);
7055
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
7056
        throw emitError(self, call.callee, ErrorKind::Internal);
7057
    };
7058
    let variant = &unionType.variants[index];
7059
    // Copy variant index to the pattern node for the lowerer.
7060
    setVariantInfo(self, pattern, index, tag);
7061
7062
    if variant.valueType <> Type::Void {
7063
        try bindUnionPatternPayload(self, pattern, call, variant.name, variant.valueType, matchBy);
7064
    } else {
7065
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
7066
    }
7067
}
7068
7069
/// Bind the payload introduced by a union constructor pattern.
7070
unsafe fn bindUnionPatternPayload 'arena (
7071
    self: &mut Resolver 'arena,
7072
    pattern: *ast::Node,
7073
    call: ast::Call,
7074
    variantName: *[u8],
7075
    payloadTy: Type,
7076
    matchBy: MatchBy
7077
) throws (ResolveError) {
7078
    if call.args.len == 0 {
7079
        throw emitError(
7080
            self, pattern, ErrorKind::UnionVariantPayloadMissing(variantName)
7081
        );
7082
    }
7083
    // All variant payloads are records.
7084
    try ensureTypeResolved(self, payloadTy, pattern);
7085
    let recInfo = getRecord(payloadTy)
7086
        else panic "bindUnionPatternPayload: payload is not a record";
7087
7088
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
7089
}
7090
7091
/// Bind a pattern variable. For ref matches, wraps the type in a pointer.
7092
unsafe fn bindPatternVar 'arena (self: &mut Resolver 'arena, binding: *ast::Node, ty: Type, matchBy: MatchBy)
7093
    throws (ResolveError)
7094
{
7095
    let mut bindTy = ty;
7096
    match matchBy {
7097
        case MatchBy::Value => {}
7098
        case MatchBy::Ref(class) => set bindTy = Type::Pointer {
7099
            class,
7100
            target: allocType(self, ty),
7101
            mutable: false,
7102
        },
7103
        case MatchBy::MutRef => set bindTy = Type::Pointer {
7104
            class: types::PointerClass::Ref,
7105
            target: allocType(self, ty),
7106
            mutable: true,
7107
        },
7108
    }
7109
    match binding.value {
7110
        case ast::NodeValue::Placeholder => {
7111
            // Nothing to do.
7112
        }
7113
        case ast::NodeValue::Ident(_) => {
7114
            try bindValueIdent(self, binding, binding, bindTy, false, 0, 0);
7115
        }
7116
        else => {
7117
            // Nested pattern: recursively resolve (record destructuring,
7118
            // union variant, scope access, call, literals, etc).
7119
            try resolveCasePattern(self, binding, ty, IdentMode::Bind, matchBy);
7120
        }
7121
    }
7122
}
7123
7124
/// Check a record pattern's exact nominal type before binding its fields.
7125
unsafe fn resolveRecordPattern 'arena (
7126
    self: &mut Resolver 'arena, pattern: *ast::Node, subjectTy: Type, body: RecordType, matchBy: MatchBy
7127
) throws (ResolveError) {
7128
    let mut name: ?*ast::Node = nil;
7129
    match pattern.value {
7130
        case ast::NodeValue::Call(call) => set name = call.callee,
7131
        case ast::NodeValue::RecordLit(lit) => set name = lit.typeName,
7132
        else => panic "resolveRecordPattern: expected record pattern",
7133
    }
7134
    if let typeName = name {
7135
        let actual = try visit(self, typeName, subjectTy);
7136
        let symbol = symbolFor(self, typeName) else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
7137
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, typeName, ErrorKind::ExpectedRecord);
7138
        if not typesEqual(actual, subjectTy) {
7139
            throw emitTypeMismatch(self, typeName, TypeMismatch { expected: subjectTy, actual });
7140
        }
7141
    }
7142
    setNodeType(self, pattern, subjectTy);
7143
    try bindRecordPatternFields(self, pattern, body, matchBy);
7144
}
7145
7146
/// Require access to a record's module-owned representation.
7147
fn requireRecordAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, recordType: RecordType)
7148
    throws (ResolveError)
7149
{
7150
    if let owner = recordType.privateModule; owner <> self.currentMod {
7151
        throw emitError(self, node, ErrorKind::OpaqueRecordAccess);
7152
    }
7153
}
7154
7155
/// Bind record pattern fields to variables in the current scope.
7156
unsafe fn bindRecordPatternFields 'arena (
7157
    self: &mut Resolver 'arena,
7158
    pattern: *ast::Node,
7159
    recInfo: RecordType,
7160
    matchBy: MatchBy
7161
) throws (ResolveError) {
7162
    try requireRecordAccess(self, pattern, recInfo);
7163
    match pattern.value {
7164
        case ast::NodeValue::Call(call) => {
7165
            // Unlabeled patterns: `S(x, y)`.
7166
            try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: call.args.len }, pattern);
7167
7168
            for binding, i in call.args {
7169
                let fieldType = recInfo.fields[i].fieldType;
7170
                try bindPatternVar(self, binding, fieldType, matchBy);
7171
            }
7172
        }
7173
        case ast::NodeValue::RecordLit(lit) => {
7174
            // Labeled patterns: `T { x, y }` or `T { x: binding }`.
7175
            if not lit.ignoreRest {
7176
                try checkRecordArity(self, CountMismatch { expected: recInfo.fields.len, actual: lit.fields.len }, pattern);
7177
            }
7178
            for fieldNode in lit.fields {
7179
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7180
                    else panic "expected RecordLitField";
7181
7182
                // Brace patterns require labeled fields.
7183
                let label = field.label else panic "expected labeled field";
7184
                let fieldName = try nodeName(self, label);
7185
                let fieldIndex = findRecordField(&recInfo.fields[..], fieldName)
7186
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
7187
                let fieldType = recInfo.fields[fieldIndex].fieldType;
7188
                // Store field index for the lowerer.
7189
                setRecordFieldIndex(self, fieldNode, fieldIndex);
7190
                try bindPatternVar(self, field.value, fieldType, matchBy);
7191
            }
7192
        }
7193
        else => throw emitError(self, pattern, ErrorKind::Internal)
7194
    }
7195
}
7196
7197
/// Validate and bind a record literal pattern for matching labeled union variants.
7198
unsafe fn resolveUnionRecordPattern 'arena (
7199
    self: &mut Resolver 'arena,
7200
    pattern: *ast::Node,
7201
    lit: ast::RecordLit,
7202
    subjectTy: Type,
7203
    unionType: UnionType,
7204
    matchBy: MatchBy
7205
) throws (ResolveError) {
7206
    let typeName = lit.typeName else {
7207
        throw emitError(self, pattern, ErrorKind::Internal);
7208
    };
7209
    // Verify the type matches the subject.
7210
    let patternTy = try visit(self, typeName, subjectTy);
7211
    if not isComparable(patternTy, subjectTy) {
7212
        throw emitTypeMismatch(self, pattern, TypeMismatch {
7213
            expected: subjectTy,
7214
            actual: patternTy,
7215
        });
7216
    }
7217
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[typeName.id].extra else {
7218
        throw emitError(self, typeName, ErrorKind::Internal);
7219
    };
7220
    let variant = &unionType.variants[index];
7221
7222
    // Copy variant index to the pattern node for the lowerer.
7223
    setVariantInfo(self, pattern, index, tag);
7224
7225
    if variant.valueType == Type::Void {
7226
        throw emitError(self, pattern, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
7227
    }
7228
    try ensureTypeResolved(self, variant.valueType, pattern);
7229
    let recInfo = getRecord(variant.valueType)
7230
        else panic "resolveUnionRecordPattern: payload is not a record";
7231
7232
    try bindRecordPatternFields(self, pattern, recInfo, matchBy);
7233
}
7234
7235
/// Analyze a pattern appearing in a union case.
7236
unsafe fn resolveUnionPattern 'arena (
7237
    self: &mut Resolver 'arena,
7238
    pattern: *ast::Node,
7239
    subjectTy: Type,
7240
    unionType: UnionType,
7241
    matchBy: MatchBy
7242
) throws (ResolveError) {
7243
    match pattern.value {
7244
        case ast::NodeValue::ScopeAccess(access) =>
7245
            try resolveUnionScopePattern(self, pattern, access, subjectTy, unionType),
7246
        case ast::NodeValue::Call(call) =>
7247
            try resolveUnionCallPattern(self, pattern, call, subjectTy, unionType, matchBy),
7248
        case ast::NodeValue::RecordLit(lit) =>
7249
            try resolveUnionRecordPattern(self, pattern, lit, subjectTy, unionType, matchBy),
7250
        else => {
7251
            let patternTy = try visit(self, pattern, subjectTy);
7252
            throw emitTypeMismatch(self, pattern, TypeMismatch {
7253
                expected: subjectTy,
7254
                actual: patternTy,
7255
            });
7256
        }
7257
    }
7258
}
7259
7260
/// Return whether a case pattern introduces value bindings.
7261
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
7262
    match pattern.value {
7263
        case ast::NodeValue::Ident(_) => return nested,
7264
        case ast::NodeValue::Call(call) => {
7265
            for arg in call.args {
7266
                if casePatternIntroducesBindings(arg, true) {
7267
                    return true;
7268
                }
7269
            }
7270
        }
7271
        case ast::NodeValue::RecordLit(lit) => {
7272
            for fieldNode in lit.fields {
7273
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7274
                    else continue;
7275
                if casePatternIntroducesBindings(field.value, true) {
7276
                    return true;
7277
                }
7278
            }
7279
        }
7280
        case ast::NodeValue::ArrayLit(items) => {
7281
            for item in items {
7282
                if casePatternIntroducesBindings(item, true) {
7283
                    return true;
7284
                }
7285
            }
7286
        }
7287
        else => {}
7288
    }
7289
    return false;
7290
}
7291
7292
/// Analyze a `let-else` guard.
7293
unsafe fn resolveLetElse 'arena (self: &mut Resolver 'arena, node: *ast::Node, letElse: ast::LetElse) -> Type
7294
    throws (ResolveError)
7295
{
7296
    let pat = letElse.pattern;
7297
    let exprTy = try infer(self, pat.scrutinee);
7298
7299
    match pat.kind {
7300
        case ast::PatternKind::Binding => {
7301
            // Simple binding requires an optional expression.
7302
            let case Type::Optional(inner) = exprTy else {
7303
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
7304
            };
7305
            let payloadTy = *inner;
7306
            // The `else` branch supplies the binding when the optional is nil.
7307
            try checkAssignable(self, letElse.elseBranch, payloadTy);
7308
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
7309
7310
            return setNodeType(self, node, Type::Void);
7311
        }
7312
        case ast::PatternKind::Case => {
7313
            // Resolve the failure path before introducing success-only bindings.
7314
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
7315
            try resolveCasePattern(
7316
                self,
7317
                pat.pattern,
7318
                exprTy,
7319
                IdentMode::Compare,
7320
                MatchBy::Value,
7321
            );
7322
            if let guardExpr = pat.guard {
7323
                try checkBoolean(self, guardExpr);
7324
            }
7325
            if elseTy <> Type::Never and
7326
               casePatternIntroducesBindings(pat.pattern, false)
7327
            {
7328
                throw emitError(
7329
                    self,
7330
                    letElse.elseBranch,
7331
                    ErrorKind::LinearLetElseMustTerminate,
7332
                );
7333
            }
7334
        }
7335
    }
7336
    return setNodeType(self, node, Type::Void);
7337
}
7338
7339
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
7340
unsafe fn resolveBuiltinCall 'arena (
7341
    self: &mut Resolver 'arena,
7342
    node: *ast::Node,
7343
    kind: ast::Builtin,
7344
    args: *[*ast::Node]
7345
) -> Type throws (ResolveError) {
7346
    // Handle `@sliceOf(ptr, len)` and `@sliceOf(ptr, len, cap)`.
7347
    if kind == ast::Builtin::SliceOf {
7348
        if args.len <> 2 and args.len <> 3 {
7349
            throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
7350
                expected: 2,
7351
                actual: args.len as u32,
7352
            }));
7353
        }
7354
        let ptrType = try visit(self, args[0], Type::Unknown);
7355
        let case Type::Pointer { class, target, mutable } = ptrType else {
7356
            throw emitError(self, node, ErrorKind::ExpectedPointer);
7357
        };
7358
        let _ = try checkAssignable(self, args[1], Type::U32);
7359
        if args.len == 3 {
7360
            let _ = try checkAssignable(self, args[2], Type::U32);
7361
        }
7362
        try requireUnsafe(self, node);
7363
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
7364
    }
7365
    if args.len <> 1 {
7366
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
7367
            expected: 1,
7368
            actual: args.len as u32,
7369
        }));
7370
    }
7371
7372
    let ty = try resolveValueType(self, args[0]);
7373
    // Ensure the type body is resolved before computing layout.
7374
    // TODO: Somehow, ensuring the type is resolved should just happen all
7375
    // the time, lazily.
7376
    try ensureTypeResolved(self, ty, args[0]);
7377
    // TODO: This should be stored in `symbol` instead of having to recompute it.
7378
    // That way there's a canonical place to look for code gen.
7379
    let layout = getTypeLayout(ty);
7380
7381
    // Evaluate the built-in.
7382
    let mut value: u32 = undefined;
7383
    match kind {
7384
        case ast::Builtin::SizeOf => {
7385
            set value = layout.size;
7386
        },
7387
        case ast::Builtin::AlignOf => {
7388
            set value = layout.alignment;
7389
        },
7390
        case ast::Builtin::SliceOf => {
7391
            panic "unreachable: @sliceOf handled above";
7392
        }
7393
    }
7394
    // Record as constant value for constant folding.
7395
    setNodeConstValue(self, node, ConstValue::Int(ConstInt {
7396
        magnitude: value as u64,
7397
        bits: 32,
7398
        signed: false,
7399
        negative: false,
7400
    }));
7401
    return setNodeType(self, node, Type::U32);
7402
}
7403
7404
/// Allocate an initially empty argument map for a region-parameterized signature.
7405
unsafe fn regionSubstitution 'arena (self: &mut Resolver 'arena, parameters: *RegionScope) -> RegionSubstitution {
7406
    let count = parameters.entries.len;
7407
    let arguments = try! alloc::allocRawSlice(
7408
        self.arena, @sizeOf(?*unsafe types::Region), @alignOf(?*unsafe types::Region), count
7409
    ) as *unsafe mut [?*unsafe types::Region];
7410
    for i in 0..count {
7411
        set arguments[i] = nil;
7412
    }
7413
    return RegionSubstitution { parameters, arguments };
7414
}
7415
7416
/// Find a region's position among the region declarations in one scope.
7417
fn regionIndex(scope: &RegionScope, regionId: u32) -> ?u32 {
7418
    match scope.declarations {
7419
        case RegionDeclarations::Parameters(nodes) => {
7420
            let mut index: u32 = 0;
7421
            for node in nodes {
7422
                let case ast::NodeValue::Region { .. } = node.value else continue;
7423
                if node.id == regionId {
7424
                    return index;
7425
                }
7426
                set index += 1;
7427
            }
7428
        }
7429
        case RegionDeclarations::Block(node) => {
7430
            if node.id == regionId {
7431
                return 0;
7432
            }
7433
        }
7434
    }
7435
    return nil;
7436
}
7437
7438
/// Infer one region argument from a pair of reference classes.
7439
unsafe fn inferRegionClass 'arena (
7440
    self: &mut Resolver 'arena, map: &RegionSubstitution,
7441
    expected: types::PointerClass, actual: types::PointerClass, site: *ast::Node
7442
) throws (ResolveError) {
7443
    let case types::PointerClass::Region(parameter) = expected else return;
7444
    let index = regionIndex(map.parameters, parameter.id) else return;
7445
    let case types::PointerClass::Region(argument) = actual
7446
        else throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
7447
    if let previous = map.arguments[index]; previous.id <> argument.id {
7448
        throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
7449
    }
7450
    set map.arguments[index] = argument;
7451
}
7452
7453
/// Infer regions through matching type structure without adding lifetime subtyping.
7454
unsafe fn inferRegionArguments 'arena (
7455
    self: &mut Resolver 'arena, map: &RegionSubstitution, expected: Type, actual: Type, site: *ast::Node
7456
) throws (ResolveError) {
7457
    match expected {
7458
        case Type::Cell { class, permission, payload } => {
7459
            let case Type::Cell {
7460
                class: otherClass, permission: otherPermission, payload: other,
7461
            } = actual else return;
7462
            try inferRegionClass(self, map, class, otherClass, site);
7463
            if let expectedPermission = permission {
7464
                let actualPermission = otherPermission else return;
7465
                try inferRegionClass(
7466
                    self, map,
7467
                    types::PointerClass::Region(expectedPermission),
7468
                    types::PointerClass::Region(actualPermission),
7469
                    site
7470
                );
7471
            }
7472
            try inferRegionArguments(self, map, *payload, *other, site);
7473
        }
7474
        case Type::Session(region) => {
7475
            let case Type::Session(other) = actual else return;
7476
            try inferRegionClass(self, map, types::PointerClass::Region(region),
7477
                types::PointerClass::Region(other), site);
7478
        }
7479
        case Type::Pointer { class, target, .. } => {
7480
            let case Type::Pointer { class: otherClass, target: otherTarget, .. } = actual else return;
7481
            try inferRegionClass(self, map, class, otherClass, site);
7482
            try inferRegionArguments(self, map, *target, *otherTarget, site);
7483
        }
7484
        case Type::Slice { class, item, .. } => {
7485
            let case Type::Slice { class: otherClass, item: otherItem, .. } = actual else return;
7486
            try inferRegionClass(self, map, class, otherClass, site);
7487
            try inferRegionArguments(self, map, *item, *otherItem, site);
7488
        }
7489
        case Type::TraitObject { class, .. } => {
7490
            if let case Type::TraitObject { class: otherClass, .. } = actual {
7491
                try inferRegionClass(self, map, class, otherClass, site);
7492
            }
7493
        }
7494
        case Type::Array(array) => {
7495
            if let case Type::Array(other) = actual {
7496
                try inferRegionArguments(self, map, *array.item, *other.item, site);
7497
            }
7498
        }
7499
        case Type::Optional(inner) => {
7500
            if let case Type::Optional(other) = actual {
7501
                try inferRegionArguments(self, map, *inner, *other, site);
7502
            } else {
7503
                try inferRegionArguments(self, map, *inner, actual, site);
7504
            }
7505
        }
7506
        case Type::Fn(info) => {
7507
            let case Type::Fn(other) = actual else return;
7508
            if info.paramTypes.len <> other.paramTypes.len or info.throwList.len <> other.throwList.len {
7509
                return;
7510
            }
7511
            for parameter, i in info.paramTypes {
7512
                try inferRegionArguments(self, map, *parameter, *other.paramTypes[i], site);
7513
            }
7514
            for error, i in info.throwList {
7515
                try inferRegionArguments(self, map, *error, *other.throwList[i], site);
7516
            }
7517
            try inferRegionArguments(self, map, *info.returnType, *other.returnType, site);
7518
        }
7519
        case Type::Nominal(info) => {
7520
            let applied = nominalApplication(info) else return;
7521
            let case Type::Nominal(otherInfo) = actual else return;
7522
            let other = nominalApplication(otherInfo) else return;
7523
            if applied.base <> other.base {
7524
                return;
7525
            }
7526
            for region, i in applied.arguments {
7527
                try inferRegionClass(self, map, types::PointerClass::Region(region),
7528
                    types::PointerClass::Region(other.arguments[i]), site);
7529
            }
7530
        }
7531
        else => {
7532
        }
7533
    }
7534
}
7535
7536
/// Return whether a type uses one formal region as a cell permission.
7537
/// Unapplied nominals cannot capture a free formal region, so only exact
7538
/// applications need cycle marking and member traversal.
7539
unsafe fn typeHasFormalCellPermission 'arena (
7540
    self: &mut Resolver 'arena,
7541
    ty: Type,
7542
    permission: *unsafe types::Region,
7543
    generation: u32,
7544
    site: *ast::Node,
7545
) -> bool throws (ResolveError) {
7546
    match ty {
7547
        case Type::Cell { permission: identity, payload, .. } => {
7548
            if let cellPermission = identity; cellPermission == permission {
7549
                return true;
7550
            }
7551
            return try typeHasFormalCellPermission(
7552
                self, *payload, permission, generation, site,
7553
            );
7554
        }
7555
        case Type::Pointer { target, .. } =>
7556
            return try typeHasFormalCellPermission(
7557
                self, *target, permission, generation, site,
7558
            ),
7559
        case Type::Slice { item, .. } =>
7560
            return try typeHasFormalCellPermission(
7561
                self, *item, permission, generation, site,
7562
            ),
7563
        case Type::Array(array) =>
7564
            return try typeHasFormalCellPermission(
7565
                self, *array.item, permission, generation, site,
7566
            ),
7567
        case Type::Optional(inner) =>
7568
            return try typeHasFormalCellPermission(
7569
                self, *inner, permission, generation, site,
7570
            ),
7571
        case Type::Range { start, end } => {
7572
            if let startType = start;
7573
                try typeHasFormalCellPermission(
7574
                    self, *startType, permission, generation, site,
7575
                )
7576
            {
7577
                return true;
7578
            }
7579
            if let endType = end {
7580
                return try typeHasFormalCellPermission(
7581
                    self, *endType, permission, generation, site,
7582
                );
7583
            }
7584
            return false;
7585
        }
7586
        case Type::Nominal(nominal) => {
7587
            let applied = nominalApplication(nominal) else return false;
7588
            try ensureNominalResolved(self, nominal, site);
7589
            if not visitNominalApplication(
7590
                applied, generation, RegionTypeRole::CellPermission,
7591
            ) {
7592
                return false;
7593
            }
7594
            match *nominal {
7595
                case NominalType::Record(recordType) => {
7596
                    for field in recordType.fields {
7597
                        if try typeHasFormalCellPermission(
7598
                            self, field.fieldType, permission, generation, site,
7599
                        ) {
7600
                            return true;
7601
                        }
7602
                    }
7603
                }
7604
                case NominalType::Union(unionType) => {
7605
                    for variant in unionType.variants {
7606
                        if try typeHasFormalCellPermission(
7607
                            self, variant.valueType, permission, generation, site,
7608
                        ) {
7609
                            return true;
7610
                        }
7611
                    }
7612
                }
7613
                case NominalType::Placeholder(_), NominalType::Resolving(_),
7614
                     NominalType::Application(_) =>
7615
                    panic "typeHasFormalCellPermission: unresolved application",
7616
            }
7617
            return false;
7618
        }
7619
        case Type::Fn(info) => {
7620
            for parameter in info.paramTypes {
7621
                if try typeHasFormalCellPermission(
7622
                    self, *parameter, permission, generation, site,
7623
                ) {
7624
                    return true;
7625
                }
7626
            }
7627
            for error in info.throwList {
7628
                if try typeHasFormalCellPermission(
7629
                    self, *error, permission, generation, site,
7630
                ) {
7631
                    return true;
7632
                }
7633
            }
7634
            return try typeHasFormalCellPermission(
7635
                self, *info.returnType, permission, generation, site,
7636
            );
7637
        }
7638
        else => return false,
7639
    }
7640
}
7641
7642
/// Return whether a function contract uses one formal cell permission.
7643
unsafe fn contractHasFormalCellPermission 'arena (
7644
    self: &mut Resolver 'arena,
7645
    info: *FnType,
7646
    receiver: ?Type,
7647
    permission: *unsafe types::Region,
7648
    generation: u32,
7649
    site: *ast::Node,
7650
) -> bool throws (ResolveError) {
7651
    if let receiverType = receiver;
7652
        try typeHasFormalCellPermission(
7653
            self, receiverType, permission, generation, site,
7654
        )
7655
    {
7656
        return true;
7657
    }
7658
    for parameter in info.paramTypes {
7659
        if try typeHasFormalCellPermission(
7660
            self, *parameter, permission, generation, site,
7661
        ) {
7662
            return true;
7663
        }
7664
    }
7665
    if try typeHasFormalCellPermission(
7666
        self, *info.returnType, permission, generation, site,
7667
    ) {
7668
        return true;
7669
    }
7670
    for error in info.throwList {
7671
        if try typeHasFormalCellPermission(
7672
            self, *error, permission, generation, site,
7673
        ) {
7674
            return true;
7675
        }
7676
    }
7677
    return false;
7678
}
7679
7680
/// Validate region parents and keep distinct formal cell permissions injective.
7681
unsafe fn validateRegionArguments 'arena (
7682
    self: &mut Resolver 'arena,
7683
    map: &RegionSubstitution,
7684
    contract: ?*FnType,
7685
    receiver: ?Type,
7686
    site: *ast::Node,
7687
) throws (ResolveError) {
7688
    for parameter, i in map.parameters.entries {
7689
        if map.arguments[i] == nil {
7690
            throw emitError(self, site, ErrorKind::RegionInference(parameter.name));
7691
        }
7692
    }
7693
    for parameter, i in map.parameters.entries {
7694
        let parent = parameter.parent else continue;
7695
        let index = regionIndex(map.parameters, parent.id)
7696
            else panic "validateRegionArguments: unknown parent";
7697
        let parentArgument = map.arguments[index]
7698
            else panic "validateRegionArguments: missing parent argument";
7699
        let argument = map.arguments[i]
7700
            else panic "validateRegionArguments: missing argument";
7701
        if not types::regionContains(parentArgument, argument) {
7702
            throw emitError(self, site, ErrorKind::RegionParent(parameter.name));
7703
        }
7704
    }
7705
    let info = contract else return;
7706
7707
    // Only colliding arguments need contract scans. Formal parameters bound the
7708
    // candidates, while generation marks keep recursive applications finite.
7709
    for parameter, i in map.parameters.entries {
7710
        let argument = map.arguments[i]
7711
            else panic "validateRegionArguments: missing argument";
7712
        let mut checked = false;
7713
        for j in 0..i {
7714
            let previousArgument = map.arguments[j]
7715
                else panic "validateRegionArguments: missing previous argument";
7716
            if previousArgument <> argument {
7717
                continue;
7718
            }
7719
            if not checked {
7720
                let generation = nextNominalTraversalGeneration(self);
7721
                set checked = true;
7722
                if not try contractHasFormalCellPermission(
7723
                    self, info, receiver, parameter, generation, site,
7724
                ) {
7725
                    break;
7726
                }
7727
            }
7728
            let previous = map.parameters.entries[j];
7729
            let generation = nextNominalTraversalGeneration(self);
7730
            if try contractHasFormalCellPermission(
7731
                self, info, receiver, previous, generation, site,
7732
            ) {
7733
                throw emitError(
7734
                    self, site, ErrorKind::RegionInference(parameter.name),
7735
                );
7736
            }
7737
        }
7738
    }
7739
}
7740
7741
/// Substitute a reference's region while preserving its ownership class.
7742
unsafe fn substituteRegionClass(map: &RegionSubstitution, class: types::PointerClass) -> types::PointerClass {
7743
    let case types::PointerClass::Region(region) = class else return class;
7744
    let index = regionIndex(map.parameters, region.id) else return class;
7745
    let argument = map.arguments[index] else panic "substituteRegionClass: missing argument";
7746
    return types::PointerClass::Region(argument);
7747
}
7748
7749
/// Substitute free region arguments in a type without changing its runtime layout.
7750
unsafe fn substituteRegions 'arena (self: &mut Resolver 'arena, map: &RegionSubstitution, ty: Type) -> Type {
7751
    match ty {
7752
        case Type::Cell { class, permission, payload } => {
7753
            let mut substitutedPermission = permission;
7754
            if let region = permission {
7755
                let case types::PointerClass::Region(argument) = substituteRegionClass(
7756
                    map, types::PointerClass::Region(region)
7757
                ) else panic "substituteRegions: invalid permission class";
7758
                set substitutedPermission = argument;
7759
            }
7760
            return Type::Cell {
7761
                class: substituteRegionClass(map, class),
7762
                permission: substitutedPermission,
7763
                payload: allocType(self, substituteRegions(self, map, *payload)),
7764
            };
7765
        }
7766
        case Type::Session(region) => {
7767
            let index = regionIndex(map.parameters, region.id) else return ty;
7768
            let argument = map.arguments[index] else panic "substituteRegions: missing session region";
7769
            return Type::Session(argument);
7770
        }
7771
        case Type::Pointer { class, target, mutable } => {
7772
            let targetType = substituteRegions(self, map, *target);
7773
            return Type::Pointer { class: substituteRegionClass(map, class), target: allocType(self, targetType), mutable };
7774
        }
7775
        case Type::Slice { class, item, mutable } => {
7776
            let itemType = substituteRegions(self, map, *item);
7777
            return Type::Slice { class: substituteRegionClass(map, class), item: allocType(self, itemType), mutable };
7778
        }
7779
        case Type::TraitObject { class, traitInfo, mutable } =>
7780
            return Type::TraitObject { class: substituteRegionClass(map, class), traitInfo, mutable },
7781
        case Type::Array(array) => {
7782
            let itemType = substituteRegions(self, map, *array.item);
7783
            return Type::Array(ArrayType { item: allocType(self, itemType), length: array.length });
7784
        }
7785
        case Type::Optional(inner) => {
7786
            let innerType = substituteRegions(self, map, *inner);
7787
            return Type::Optional(allocType(self, innerType));
7788
        }
7789
        case Type::Fn(info) => return Type::Fn(substituteFnRegions(self, map, info, info.regions)),
7790
        case Type::Nominal(info) => {
7791
            let applied = nominalApplication(info) else return ty;
7792
            let arguments = regionSubstitution(self, applied.parameters);
7793
            for region, i in applied.arguments {
7794
                let class = substituteRegionClass(map, types::PointerClass::Region(region));
7795
                let case types::PointerClass::Region(argument) = class else panic;
7796
                set arguments.arguments[i] = argument;
7797
            }
7798
            return Type::Nominal(internNominalApplication(self, applied.base, &arguments));
7799
        }
7800
        else => return ty,
7801
    }
7802
}
7803
7804
/// Create a substituted signature with the specified remaining region binder.
7805
unsafe fn substituteFnRegions 'arena (
7806
    self: &mut Resolver 'arena, map: &RegionSubstitution, info: *FnType, regions: ?*RegionScope
7807
) -> *FnType {
7808
    let a = alloc::arenaAllocator(self.arena);
7809
    let mut paramTypes: *mut [*Type] = &mut [];
7810
    let mut throwList: *mut [*Type] = &mut [];
7811
    for parameter in info.paramTypes {
7812
        let ty = substituteRegions(self, map, *parameter);
7813
        paramTypes.append(allocType(self, ty), a);
7814
    }
7815
    for error in info.throwList {
7816
        let ty = substituteRegions(self, map, *error);
7817
        throwList.append(allocType(self, ty), a);
7818
    }
7819
    let returnType = substituteRegions(self, map, *info.returnType);
7820
    return allocFnType(self, FnType {
7821
        regions,
7822
        paramTypes: &paramTypes[..],
7823
        returnType: allocType(self, returnType),
7824
        throwList: &throwList[..],
7825
        isUnsafe: info.isUnsafe,
7826
    });
7827
}
7828
7829
/// Preserve a call-scoped pointer class while inferring its region-bearing contents.
7830
/// Named reference regions are inferred from the source storage.
7831
unsafe fn regionInputHint 'arena (self: &mut Resolver 'arena, expected: Type) -> Type {
7832
    if let case Type::Optional(inner) = expected {
7833
        return regionInputHint(self, *inner);
7834
    }
7835
    match expected {
7836
        case Type::Pointer { class, mutable, .. } => {
7837
            if let case types::PointerClass::Region(_) = class {
7838
                return Type::Unknown;
7839
            }
7840
            return Type::Pointer { class, target: allocType(self, Type::Unknown), mutable };
7841
        }
7842
        case Type::Slice { class, mutable, .. } => {
7843
            if let case types::PointerClass::Region(_) = class {
7844
                return Type::Unknown;
7845
            }
7846
            return Type::Slice { class, item: allocType(self, Type::Unknown), mutable };
7847
        }
7848
        else => return Type::Unknown,
7849
    }
7850
}
7851
7852
/// Infer a source function's region arguments from its call inputs.
7853
unsafe fn instantiateCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType) -> *FnType
7854
    throws (ResolveError)
7855
{
7856
    let parameters = info.regions else return info;
7857
    if call.args.len <> info.paramTypes.len {
7858
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7859
            expected: info.paramTypes.len, actual: call.args.len,
7860
        }));
7861
    }
7862
    let map = regionSubstitution(self, parameters);
7863
    for argument, i in call.args {
7864
        let expected = *info.paramTypes[i];
7865
        if containsRegion(expected) {
7866
            let actual = try visit(self, argument, regionInputHint(self, expected));
7867
            try inferRegionArguments(self, &map, expected, actual, argument);
7868
        }
7869
    }
7870
    try validateRegionArguments(self, &map, info, nil, node);
7871
    return substituteFnRegions(self, &map, info, nil);
7872
}
7873
7874
/// Infer a method's region arguments from its receiver and call arguments.
7875
unsafe fn instantiateMethodCall 'arena (
7876
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call,
7877
    receiver: *ast::Node, receiverType: Type, method: *unsafe MethodEntry
7878
) -> *FnType throws (ResolveError) {
7879
    let parameters = method.fnType.regions else return method.fnType;
7880
    if call.args.len <> method.fnType.paramTypes.len {
7881
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7882
            expected: method.fnType.paramTypes.len, actual: call.args.len,
7883
        }));
7884
    }
7885
    let map = regionSubstitution(self, parameters);
7886
    try inferRegionArguments(self, &map, method.concreteType, receiverType, receiver);
7887
    for argument, i in call.args {
7888
        let expected = *method.fnType.paramTypes[i];
7889
        if containsRegion(expected) {
7890
            let actual = try visit(self, argument, regionInputHint(self, expected));
7891
            try inferRegionArguments(self, &map, expected, actual, argument);
7892
        }
7893
    }
7894
    try validateRegionArguments(
7895
        self, &map, method.fnType, method.concreteType, node,
7896
    );
7897
    return substituteFnRegions(self, &map, method.fnType, nil);
7898
}
7899
7900
/// Apply explicit current-scope regions to a function or nominal type.
7901
unsafe fn resolveRegionApply 'arena (
7902
    self: &mut Resolver 'arena, node: *ast::Node, value: *ast::Node, regions: *[*ast::Node]
7903
) -> Type throws (ResolveError) {
7904
    let ty = try infer(self, value);
7905
    if let case Type::Nominal(base) = ty {
7906
        let symbol = symbolFor(self, value) else throw emitError(self, node, ErrorKind::CannotInferType);
7907
        let case SymbolData::Type(_) = symbol.data else throw emitError(self, node, ErrorKind::CannotInferType);
7908
        let applied = try applyNominalRegions(self, base, regions, node);
7909
        try ensureNominalResolved(self, applied, node);
7910
        setNodeSymbol(self, node, symbol);
7911
        return setNodeType(self, node, Type::Nominal(applied));
7912
    }
7913
    let case Type::Fn(info) = ty else throw emitError(self, node, ErrorKind::CannotInferType);
7914
    let mut count: u32 = 0;
7915
    if let scope = info.regions {
7916
        set count = scope.entries.len;
7917
    }
7918
    if count <> regions.len {
7919
        throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch { expected: count, actual: regions.len }));
7920
    }
7921
    let scope = info.regions else panic "resolveRegionApply: empty region application";
7922
    let map = regionSubstitution(self, scope);
7923
    for region, i in regions {
7924
        set map.arguments[i] = try resolveRegion(self, region);
7925
    }
7926
    try validateRegionArguments(self, &map, info, nil, node);
7927
    let applied = substituteFnRegions(self, &map, info, nil);
7928
    if let symbol = symbolFor(self, value) {
7929
        setNodeSymbol(self, node, symbol);
7930
    }
7931
    return setNodeType(self, node, Type::Fn(applied));
7932
}
7933
7934
/// Validate call arguments against a function type: check argument count,
7935
/// type-check each argument, and verify that throwing functions use `try`.
7936
unsafe fn checkCallArgs 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, info: *FnType, ctx: CallCtx)
7937
    throws (ResolveError)
7938
{
7939
    if ctx == CallCtx::Normal and info.throwList.len > 0 {
7940
        throw emitError(self, node, ErrorKind::MissingTry);
7941
    }
7942
    if call.args.len <> info.paramTypes.len as u32 {
7943
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
7944
            expected: info.paramTypes.len as u32,
7945
            actual: call.args.len,
7946
        }));
7947
    }
7948
    for argNode, i in call.args {
7949
        let expectedTy = *info.paramTypes[i];
7950
7951
        try checkAssignable(self, argNode, expectedTy);
7952
    }
7953
}
7954
7955
/// Return whether a value can be discarded by bulk arena reclamation.
7956
/// Pointer lifetimes are checked when their values are constructed.
7957
unsafe fn isBulkDiscardable(ty: Type) -> bool {
7958
    match ty {
7959
        case Type::Void, Type::Bool, Type::U8, Type::U16, Type::U32, Type::U64,
7960
             Type::I8, Type::I16, Type::I32, Type::I64, Type::Fn(_) => return true,
7961
        case Type::Pointer { .. }, Type::Slice { .. }, Type::TraitObject { .. } => return true,
7962
        case Type::Cell { .. } => return true,
7963
        case Type::Array(array) => return isBulkDiscardable(*array.item),
7964
        case Type::Optional(inner) => return isBulkDiscardable(*inner),
7965
        case Type::Nominal(NominalType::Record(recordType)) => {
7966
            if recordType.declaredLinear {
7967
                return false;
7968
            }
7969
            for field in recordType.fields {
7970
                if not isBulkDiscardable(field.fieldType) {
7971
                    return false;
7972
                }
7973
            }
7974
            return true;
7975
        }
7976
        case Type::Nominal(NominalType::Union(unionType)) => {
7977
            if unionType.declaredLinear {
7978
                return false;
7979
            }
7980
            for variant in unionType.variants {
7981
                if not isBulkDiscardable(variant.valueType) {
7982
                    return false;
7983
                }
7984
            }
7985
            return true;
7986
        }
7987
        else => return false,
7988
    }
7989
}
7990
7991
/// Compute the end of an aligned layout within the allocator's byte-count range.
7992
fn allocationLayoutEnd(offset: u64, layout: Layout) -> ?u64 {
7993
    let mut aligned = offset;
7994
    if layout.alignment > 0 {
7995
        let mask = (layout.alignment - 1) as u64;
7996
        set aligned = (offset + mask) & ~mask;
7997
    }
7998
    let end = aligned + layout.size as u64;
7999
    if end > 4294967295 {
8000
        return nil;
8001
    }
8002
    return end;
8003
}
8004
8005
/// Check allocation layout arithmetic independently of the stored narrow offsets.
8006
unsafe fn hasAllocationLayout(ty: Type) -> bool {
8007
    let layout = getTypeLayout(ty);
8008
    match ty {
8009
        case Type::Cell { .. } => return true,
8010
        case Type::Array(array) => {
8011
            if not hasAllocationLayout(*array.item) {
8012
                return false;
8013
            }
8014
            let item = getTypeLayout(*array.item);
8015
            return item.size as u64 * array.length as u64 == layout.size as u64;
8016
        }
8017
        case Type::Optional(inner) => {
8018
            if not hasAllocationLayout(*inner) {
8019
                return false;
8020
            }
8021
            if isNullableType(*inner) {
8022
                return true;
8023
            }
8024
            let end = allocationLayoutEnd(1, getTypeLayout(*inner)) else return false;
8025
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment: layout.alignment }) else return false;
8026
            return total == layout.size as u64;
8027
        }
8028
        case Type::Nominal(NominalType::Record(recordType)) => {
8029
            let mut offset: u64 = 0;
8030
            for field in recordType.fields {
8031
                if not hasAllocationLayout(field.fieldType) {
8032
                    return false;
8033
                }
8034
                let fieldLayout = getTypeLayout(field.fieldType);
8035
                let end = allocationLayoutEnd(offset, fieldLayout) else return false;
8036
                let start = end - fieldLayout.size as u64;
8037
                if start > 2147483647 or field.offset < 0 or start <> field.offset as u64 {
8038
                    return false;
8039
                }
8040
                set offset = end;
8041
            }
8042
            let total = allocationLayoutEnd(offset, Layout { size: 0, alignment: layout.alignment }) else return false;
8043
            return total == layout.size as u64;
8044
        }
8045
        case Type::Nominal(NominalType::Union(unionType)) => {
8046
            let mut payloadSize: u32 = 0;
8047
            let mut alignment: u32 = 1;
8048
            for variant in unionType.variants {
8049
                if not hasAllocationLayout(variant.valueType) {
8050
                    return false;
8051
                }
8052
                let item = getTypeLayout(variant.valueType);
8053
                set payloadSize = max(payloadSize, item.size);
8054
                set alignment = max(alignment, item.alignment);
8055
            }
8056
            let end = allocationLayoutEnd(1, Layout { size: payloadSize, alignment }) else return false;
8057
            let total = allocationLayoutEnd(end, Layout { size: 0, alignment }) else return false;
8058
            return total == layout.size as u64 and end - payloadSize as u64 == unionType.valOffset as u64;
8059
        }
8060
        else => return true,
8061
    }
8062
}
8063
8064
/// Validate the reservation ABI used by typed session allocation.
8065
unsafe fn sessionRuntime 'arena (
8066
    self: &mut Resolver 'arena, node: *ast::Node, slice: bool
8067
) -> TraitMethod
8068
    throws (ResolveError)
8069
{
8070
    let allocTrait = allocationSymbol(self, "Alloc")
8071
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8072
    let case SymbolData::Trait(allocInfo) = allocTrait.data
8073
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8074
    let name = "reserveSlice" if slice else "reserve";
8075
    let method = findTraitMethod(&allocInfo.methods[..], name)
8076
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8077
    let error = allocationSymbol(self, "AllocError")
8078
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8079
    let case SymbolData::Type(errorType) = error.data
8080
        else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8081
    let count: u32 = 3 if slice else 2;
8082
    let info = method.fnType;
8083
    if info.regions <> nil or not info.isUnsafe or info.paramTypes.len <> count or info.throwList.len <> 1 {
8084
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8085
    }
8086
    if not typesEqual(*info.throwList[0], Type::Nominal(errorType)) {
8087
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8088
    }
8089
    for i in 0..count {
8090
        if *info.paramTypes[i] <> Type::U32 {
8091
            throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8092
        }
8093
    }
8094
    let expected = Type::Slice { class: types::PointerClass::Unsafe, item: allocType(self, Type::Opaque), mutable: true }
8095
        if slice else Type::Pointer {
8096
            class: types::PointerClass::Unsafe, target: allocType(self, Type::Opaque), mutable: true
8097
        };
8098
    if not typesEqual(*info.returnType, expected) {
8099
        throw emitError(self, node, ErrorKind::InvalidAllocationRuntime);
8100
    }
8101
    return method;
8102
}
8103
8104
/// Check initialized session allocation and retain its source region in the result.
8105
unsafe fn resolveSessionAllocation 'arena (
8106
    self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, access: ast::Access,
8107
    region: *unsafe types::Region, ctx: CallCtx, hint: Type
8108
) -> Type throws (ResolveError) {
8109
    let name = try nodeName(self, access.child);
8110
    let mut kind = SessionAllocationKind::New;
8111
    if mem::eq(name, "copy") {
8112
        set kind = SessionAllocationKind::Copy;
8113
    }
8114
    else if mem::eq(name, "fill") {
8115
        set kind = SessionAllocationKind::Fill;
8116
    }
8117
    else if not mem::eq(name, "new") {
8118
        throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(name));
8119
    }
8120
    let count: u32 = 2 if kind == SessionAllocationKind::Fill else 1;
8121
    if call.args.len <> count {
8122
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch { expected: count, actual: call.args.len }));
8123
    }
8124
    let slice = kind <> SessionAllocationKind::New;
8125
    let mut itemHint = Type::Unknown;
8126
    if kind == SessionAllocationKind::New {
8127
        if let case Type::Pointer { target, .. } = hint {
8128
            set itemHint = *target;
8129
        }
8130
    } else if kind == SessionAllocationKind::Fill {
8131
        if let case Type::Slice { item, .. } = hint {
8132
            set itemHint = *item;
8133
        }
8134
    } else {
8135
        set itemHint = Type::Slice {
8136
            class: types::PointerClass::Ref, item: allocType(self, Type::Unknown), mutable: false,
8137
        };
8138
    }
8139
    let valueType = try visit(self, call.args[0], itemHint);
8140
    let mut itemType = valueType;
8141
    let mut parameter = valueType;
8142
    if kind == SessionAllocationKind::Copy {
8143
        let case Type::Slice { item, .. } = valueType
8144
            else throw emitError(self, call.args[0], ErrorKind::ExpectedIndexable);
8145
        set itemType = *item;
8146
        set parameter = Type::Slice { class: types::PointerClass::Ref, item, mutable: false };
8147
    }
8148
    if not isTypeInferrable(itemType) or itemType == Type::Void or itemType == Type::Opaque {
8149
        throw emitError(self, call.args[0], ErrorKind::CannotInferType);
8150
    }
8151
    try ensureStorableType(self, call.args[0], itemType);
8152
    try ensureTypeResolved(self, itemType, call.args[0]);
8153
    try validateRegionStorage(self, call.args[0], itemType, region);
8154
    if not isBulkDiscardable(itemType) or (slice and not isCopy(itemType)) {
8155
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationValue);
8156
    }
8157
    if not hasAllocationLayout(itemType) {
8158
        throw emitError(self, call.args[0], ErrorKind::InvalidAllocationLayout);
8159
    }
8160
    let runtime = try sessionRuntime(self, node, slice);
8161
    let runtimeType = runtime.fnType;
8162
    let item = allocType(self, itemType);
8163
    let result = Type::Slice { class: types::PointerClass::Region(region), item, mutable: true }
8164
        if slice else Type::Pointer {
8165
            class: types::PointerClass::Region(region), target: item, mutable: true
8166
        };
8167
    let a = alloc::arenaAllocator(self.arena);
8168
    let mut parameters: *mut [*Type] = &mut [];
8169
    parameters.append(allocType(self, parameter), a);
8170
    if kind == SessionAllocationKind::Fill {
8171
        parameters.append(allocType(self, Type::U32), a);
8172
    }
8173
    let info = allocFnType(self, FnType {
8174
        regions: nil, paramTypes: &parameters[..], returnType: allocType(self, result),
8175
        throwList: runtimeType.throwList, isUnsafe: false,
8176
    });
8177
    try checkCallArgs(self, node, call, info, ctx);
8178
    setNodeType(self, call.callee, Type::Fn(info));
8179
    let allocTrait = allocationSymbol(self, "Alloc") else panic;
8180
    let case SymbolData::Trait(traitInfo) = allocTrait.data else panic;
8181
    set self.nodeData.entries[node.id].extra = NodeExtra::SessionAllocation(SessionAllocation {
8182
        kind, item, traitInfo, methodIndex: runtime.index,
8183
    });
8184
    return setNodeType(self, node, result);
8185
}
8186
8187
/// Require a complete, bulk-discardable cell payload, and Copy when unassociated.
8188
unsafe fn validateCellPayload 'arena (
8189
    self: &mut Resolver 'arena,
8190
    node: *ast::Node,
8191
    payload: Type,
8192
    permission: ?*unsafe types::Region,
8193
) throws (ResolveError) {
8194
    try ensureStorableType(self, node, payload);
8195
    try ensureTypeResolved(self, payload, node);
8196
    if not isTypeInferrable(payload) or payload == Type::Void or payload == Type::Opaque
8197
        or (permission == nil and not isCopy(payload)) or not isBulkDiscardable(payload)
8198
    {
8199
        throw emitError(self, node, ErrorKind::InvalidCellPayload);
8200
    }
8201
    if not hasAllocationLayout(payload) {
8202
        throw emitError(self, node, ErrorKind::InvalidAllocationLayout);
8203
    }
8204
}
8205
8206
/// Analyze a function call expression.
8207
unsafe fn resolveCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, ctx: CallCtx, hint: Type) -> Type
8208
    throws (ResolveError)
8209
{
8210
    // Intercept method calls on slices before inferring the callee.
8211
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
8212
        let parentTy = try infer(self, access.parent);
8213
        if isUnsafePointerType(parentTy) {
8214
            try requireUnsafe(self, access.parent);
8215
        }
8216
        let subjectTy = autoDeref(parentTy);
8217
        if let case Type::Session(region) = subjectTy {
8218
            return try resolveSessionAllocation(self, node, call, access, region, ctx, hint);
8219
        }
8220
8221
        if let case Type::Slice { item, mutable, .. } = subjectTy {
8222
            let methodName = try nodeName(self, access.child);
8223
            if methodName == "append" {
8224
                return try resolveSliceAppend(
8225
                    self, node, access.parent, parentTy, call.args, item, mutable
8226
                );
8227
            }
8228
            if methodName == "delete" {
8229
                return try resolveSliceDelete(
8230
                    self, node, access.parent, call.args, item, mutable
8231
                );
8232
            }
8233
        }
8234
    }
8235
    let calleeTy = try visit(self, call.callee, hint);
8236
    if let case Type::Fn(info) = calleeTy {
8237
        try checkUnsafeCall(self, call.callee, info);
8238
    }
8239
8240
    // Check if callee is a union variant and dispatch to constructor handler.
8241
    // TODO: Move this out. We should decide on this earlier, based on the callee.
8242
    if let calleeSym = symbolFor(self, call.callee) {
8243
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
8244
            // TODO: Don't pass the callee type, pass the union type by getting it from
8245
            // the symbol.
8246
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid variant type";
8247
            return try resolveUnionConstructorCall(self, node, call, ty);
8248
        }
8249
        // Check if callee is an unlabeled record type for constructor call syntax.
8250
        if let case SymbolData::Type(_) = calleeSym.data {
8251
            let case Type::Nominal(ty) = calleeTy else panic "resolveCall: invalid type callee";
8252
            try requireNominalArguments(self, ty, call.callee);
8253
            // Ensure the record body is resolved before checking if labeled.
8254
            try ensureNominalResolved(self, ty, call.callee);
8255
            if let case NominalType::Record(recInfo) = *ty {
8256
                if not recInfo.labeled {
8257
                    return try resolveRecordConstructorCall(self, node, call, ty);
8258
                }
8259
            }
8260
        }
8261
    }
8262
8263
    // Check if we have a trait method call, ie. callee is a trait object.
8264
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
8265
        let mut parentTy = Type::Unknown;
8266
        if let t = typeFor(self, access.parent) {
8267
            set parentTy = t;
8268
        }
8269
        let subjectTy = autoDeref(parentTy);
8270
8271
        if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy {
8272
            let methodName = try nodeName(self, access.child);
8273
            let method = findTraitMethod(&traitInfo.methods[..], methodName)
8274
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
8275
8276
            // Reject mutable-receiver methods called on immutable trait objects.
8277
            if method.mutable {
8278
                if not objMutable or not try canMutateThrough(self, access.parent) {
8279
                    throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
8280
                }
8281
            }
8282
            let applied = try instantiateCall(self, node, call, method.fnType);
8283
            try resolveInlineTypeViews(self, *applied.returnType, node);
8284
            try checkCallArgs(self, node, call, applied, ctx);
8285
            setNodeType(self, call.callee, Type::Fn(applied));
8286
            setTraitMethodCall(self, node, traitInfo, method.index);
8287
8288
            return setNodeType(self, node, *applied.returnType);
8289
        }
8290
8291
        // Check for a standalone method call on a concrete type.
8292
        if let case Type::Nominal(_) = subjectTy {
8293
            let methodName = try nodeName(self, access.child);
8294
            if let method = findMethod(self, subjectTy, methodName) {
8295
                // Reject mutable-receiver methods on immutable bindings.
8296
                // If the parent is already a mutable pointer, the receiver is fine.
8297
                // Otherwise, check that the parent can yield a mutable borrow.
8298
                if method.mutable {
8299
                    if not try canMutateThrough(self, access.parent) {
8300
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
8301
                    }
8302
                }
8303
                // Check arguments (excluding receiver).
8304
                let applied = try instantiateMethodCall(
8305
                    self, node, call, access.parent, subjectTy, method
8306
                );
8307
                try resolveInlineTypeViews(self, *applied.returnType, node);
8308
                try checkCallArgs(self, node, call, applied, ctx);
8309
                setNodeType(self, call.callee, Type::Fn(applied));
8310
                set self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
8311
8312
                return setNodeType(self, node, *applied.returnType);
8313
            }
8314
        }
8315
    }
8316
    let case Type::Fn(info) = calleeTy else {
8317
        throw emitError(self, call.callee, ErrorKind::TypeMismatch(TypeMismatch {
8318
            expected: Type::Unknown,
8319
            actual: calleeTy,
8320
        }));
8321
    };
8322
    let applied = try instantiateCall(self, node, call, info);
8323
    try resolveInlineTypeViews(self, *applied.returnType, node);
8324
    try checkCallArgs(self, node, call, applied, ctx);
8325
    // Associate function type to callee.
8326
    setNodeType(self, call.callee, Type::Fn(applied));
8327
8328
    // Associate return type to call.
8329
    return setNodeType(self, node, *applied.returnType);
8330
}
8331
8332
/// Check labeled record fields against the slice allocator layout and callback ABI.
8333
fn isSliceAllocator(fields: &[RecordField]) -> bool {
8334
    if fields.len <> 2 {
8335
        return false;
8336
    }
8337
    let func = fields[0];
8338
    let ctx = fields[1];
8339
    let funcName = func.name else return false;
8340
    let ctxName = ctx.name else return false;
8341
    if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or
8342
       func.offset <> 0 or ctx.offset <> 8
8343
    {
8344
        return false;
8345
    }
8346
    let case Type::Fn(callback) = func.fieldType else return false;
8347
    let case Type::Pointer { target, .. } = ctx.fieldType else return false;
8348
    if *target <> Type::Opaque or callback.paramTypes.len <> 3 or callback.throwList.len <> 0 {
8349
        return false;
8350
    }
8351
    if not typesEqual(*callback.paramTypes[0], ctx.fieldType) or
8352
       *callback.paramTypes[1] <> Type::U32 or *callback.paramTypes[2] <> Type::U32
8353
    {
8354
        return false;
8355
    }
8356
    let case Type::Pointer { class, target: result, mutable } = *callback.returnType
8357
        else return false;
8358
    return class == types::PointerClass::Owned and mutable and *result == Type::Opaque;
8359
}
8360
8361
/// Resolve `slice.append(val, allocator)`.
8362
unsafe fn resolveSliceAppend 'arena (
8363
    self: &mut Resolver 'arena,
8364
    node: *ast::Node,
8365
    parent: *ast::Node,
8366
    parentType: Type,
8367
    args: *[*ast::Node],
8368
    elemType: *Type,
8369
    mutable: bool
8370
) -> Type throws (ResolveError) {
8371
    if not mutable {
8372
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
8373
    }
8374
    if args.len <> 2 {
8375
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
8376
            expected: 2,
8377
            actual: args.len as u32,
8378
        }));
8379
    }
8380
    // First argument must be assignable to the element type.
8381
    try checkAssignable(self, args[0], *elemType);
8382
    // The allocator stores its callback and context at fixed offsets.
8383
    let allocatorTy = try infer(self, args[1]);
8384
    if let case Type::Nominal(info) = allocatorTy {
8385
        try ensureNominalResolved(self, info, args[1]);
8386
    }
8387
    let mut validAllocator = false;
8388
    if let case Type::Nominal(NominalType::Record(rec)) = allocatorTy; rec.labeled {
8389
        set validAllocator = isSliceAllocator(&rec.fields[..]);
8390
    }
8391
    if not validAllocator {
8392
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
8393
    }
8394
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
8395
8396
    // Return the parent's type so the caller can rebind:
8397
    return setNodeType(self, node, parentType);
8398
}
8399
8400
/// Resolve `slice.delete(index)`.
8401
unsafe fn resolveSliceDelete 'arena (
8402
    self: &mut Resolver 'arena,
8403
    node: *ast::Node,
8404
    parent: *ast::Node,
8405
    args: *[*ast::Node],
8406
    elemType: *Type,
8407
    mutable: bool
8408
) -> Type throws (ResolveError) {
8409
    if not mutable {
8410
        throw emitError(self, parent, ErrorKind::ImmutableBinding);
8411
    }
8412
    if args.len <> 1 {
8413
        throw emitError(self, node, ErrorKind::FnArgCountMismatch(CountMismatch {
8414
            expected: 1,
8415
            actual: args.len as u32,
8416
        }));
8417
    }
8418
    try checkAssignable(self, args[0], Type::U32);
8419
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceDelete { elemType };
8420
8421
    return setNodeType(self, node, Type::Void);
8422
}
8423
8424
/// Analyze an assignment expression.
8425
unsafe fn resolveAssign 'arena (self: &mut Resolver 'arena, node: *ast::Node, assign: ast::Assign) -> Type
8426
    throws (ResolveError)
8427
{
8428
    // Slice assignment: `slice[range] = value`.
8429
    if let case ast::NodeValue::Subscript { container, index } = assign.left.value {
8430
        if let case ast::NodeValue::Range(range) = index.value {
8431
            try infer(self, index);
8432
            let containerTy = try infer(self, container);
8433
            let cellTy = try inferCellPayload(self, container);
8434
            if cellTy == nil and not try canMutateThrough(self, container) {
8435
                throw emitError(self, container, ErrorKind::ImmutableBinding);
8436
            }
8437
            let subjectTy = autoDeref(containerTy);
8438
            try checkSliceRangeIndices(self, range);
8439
8440
            let info = sliceRangeInfo(subjectTy)
8441
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
8442
            if not info.mutable {
8443
                throw emitError(self, container, ErrorKind::ImmutableBinding);
8444
            }
8445
            if let capacity = info.capacity {
8446
                try validateArraySliceBounds(self, range, capacity, node);
8447
            }
8448
            let item = info.itemType;
8449
            // RHS is either a fill value or a source slice.
8450
            let rhsTy = try infer(self, assign.right);
8451
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
8452
                if *sourceItem <> *item {
8453
                    throw emitTypeMismatch(
8454
                        self,
8455
                        assign.right,
8456
                        TypeMismatch { expected: *item, actual: *sourceItem },
8457
                    );
8458
                }
8459
            } else {
8460
                try checkAssignable(self, assign.right, *item);
8461
            }
8462
            if let controlled = cellTy {
8463
                let case Type::Cell { class, .. } = controlled
8464
                    else panic "resolveAssign: invalid cell range payload";
8465
                if let case types::PointerClass::Region(region) = class {
8466
                    try validateRegionStorage(self, assign.right, *item, region);
8467
                } else if class == types::PointerClass::Owned
8468
                    and try containsStorageRegion(self, assign.right, *item)
8469
                {
8470
                    throw emitError(self, assign.right, ErrorKind::InvalidCellPayload);
8471
                }
8472
            } else {
8473
                try validateRegionalStore(self, assign.left, assign.right, *item);
8474
            }
8475
            setSliceRangeInfo(self, node, info);
8476
            setNodeType(self, assign.left, *item);
8477
8478
            return setNodeType(self, node, Type::Void);
8479
        }
8480
    }
8481
    let leftTy = try infer(self, assign.left);
8482
8483
    if let cellTy = try inferCellPayload(self, assign.left) {
8484
        let case Type::Cell { class, .. } = cellTy
8485
            else panic "resolveAssign: invalid cell payload";
8486
        try checkAssignable(self, assign.right, leftTy);
8487
        if let case types::PointerClass::Region(region) = class {
8488
            try validateRegionStorage(self, assign.right, leftTy, region);
8489
        } else if class == types::PointerClass::Owned
8490
            and try containsStorageRegion(self, assign.right, leftTy)
8491
        {
8492
            throw emitError(self, assign.right, ErrorKind::InvalidCellPayload);
8493
        }
8494
        return setNodeType(self, node, leftTy);
8495
    }
8496
8497
    // Check if the left-hand side can be assigned to by checking if it's a mutable location.
8498
    if not try canBorrowMutFrom(self, assign.left) {
8499
        throw emitError(self, assign.left, ErrorKind::ImmutableBinding);
8500
    }
8501
    try checkAssignable(self, assign.right, leftTy);
8502
    try validateRegionalStore(self, assign.left, assign.right, leftTy);
8503
8504
    return setNodeType(self, node, leftTy);
8505
}
8506
8507
/// Construct complete range metadata for an array or slice type.
8508
/// Callers check array place access and select the resulting borrow access.
8509
fn sliceRangeInfo(ty: Type) -> ?SliceRangeInfo {
8510
    match ty {
8511
        case Type::Slice { item, mutable, .. } =>
8512
            return SliceRangeInfo { itemType: item, mutable, capacity: nil },
8513
        case Type::Array(array) =>
8514
            return SliceRangeInfo { itemType: array.item, mutable: true, capacity: array.length },
8515
        else => return nil,
8516
    }
8517
}
8518
8519
/// Ensure slice range bounds are valid `u32` values.
8520
unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) {
8521
    if let start = range.start {
8522
        try checkIndex(self, start);
8523
    }
8524
    if let end = range.end {
8525
        try checkIndex(self, end);
8526
    }
8527
}
8528
8529
/// Emit an error when constant slice bounds exceed the array length or are reversed.
8530
fn validateArraySliceBounds 'arena (self: &mut Resolver 'arena, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) {
8531
    let mut startVal: ?u32 = nil;
8532
    let mut endVal: ?u32 = length;
8533
8534
    if let startNode = range.start {
8535
        if let val = constSliceIndex(self, startNode) {
8536
            set startVal = val;
8537
        }
8538
    }
8539
    if let endNode = range.end {
8540
        if let val = constSliceIndex(self, endNode) {
8541
            set endVal = val;
8542
        }
8543
    }
8544
    if let val = startVal; val > length {
8545
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
8546
    }
8547
    if let val = endVal; val > length {
8548
        throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
8549
    }
8550
    if let start = startVal {
8551
        if let end = endVal; start > end {
8552
            throw emitError(self, site, ErrorKind::SliceRangeOutOfBounds);
8553
        }
8554
    }
8555
}
8556
8557
/// Check that an index expression has an unsigned integer type.
8558
/// Accepts `u8`, `u16`, `u32` and unsuffixed integer literals.
8559
/// Smaller types are widened to `u32` via a numeric cast coercion.
8560
unsafe fn checkIndex 'arena (self: &mut Resolver 'arena, indexNode: *ast::Node) throws (ResolveError) {
8561
    let indexTy = try visit(self, indexNode, Type::U32);
8562
    if indexTy == Type::Int or indexTy == Type::U32 {
8563
        let _ = try expectAssignable(self, Type::U32, indexTy, indexNode);
8564
        return;
8565
    }
8566
    match indexTy {
8567
        case Type::U8, Type::U16 => {
8568
            setNodeCoercion(self, indexNode, Coercion::NumericCast {
8569
                from: indexTy, to: Type::U32,
8570
            });
8571
        }
8572
        else => {
8573
            throw emitTypeMismatch(self, indexNode, TypeMismatch {
8574
                expected: Type::U32,
8575
                actual: indexTy,
8576
            });
8577
        }
8578
    }
8579
}
8580
8581
/// Analyze an array or slice subscript expression.
8582
unsafe fn resolveSubscript 'arena (self: &mut Resolver 'arena, node: *ast::Node, container: *ast::Node, indexNode: *ast::Node) -> Type
8583
    throws (ResolveError)
8584
{
8585
    // Range subscripts always require `&` to form a slice.
8586
    if let case ast::NodeValue::Range(range) = indexNode.value {
8587
        let _ = try infer(self, indexNode);
8588
        let _ = try infer(self, container);
8589
        try checkSliceRangeIndices(self, range);
8590
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
8591
    }
8592
    let containerTy = try infer(self, container);
8593
    if isUnsafePointerType(containerTy) {
8594
        try requireUnsafe(self, container);
8595
    }
8596
    try checkIndex(self, indexNode);
8597
    let subjectTy = autoDeref(containerTy);
8598
    if let case Type::Slice { item, .. } = subjectTy {
8599
        return setNodeType(self, node, *item);
8600
    }
8601
8602
    match subjectTy {
8603
        case Type::Array(arrayInfo) => {
8604
            return setNodeType(self, node, *arrayInfo.item);
8605
        }
8606
        else => {
8607
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
8608
        }
8609
    }
8610
}
8611
8612
/// Find a record field by name.
8613
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
8614
    for field, i in fields {
8615
        if let name = field.name {
8616
            if name == fieldName {
8617
                return i;
8618
            }
8619
        }
8620
    }
8621
    return nil;
8622
}
8623
8624
/// Analyze a union constructor call with payload.
8625
unsafe fn resolveUnionConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, unionNominal: *unsafe NominalType) -> Type
8626
    throws (ResolveError)
8627
{
8628
    // Get the union nominal type.
8629
    let case NominalType::Union(unionType) = *unionNominal
8630
        else panic "resolveUnionConstructorCall: not a union type";
8631
8632
    // Callee was already visited; get the variant index it set.
8633
    let case NodeExtra::UnionVariant { ordinal: index, tag } = self.nodeData.entries[call.callee.id].extra else {
8634
        throw emitError(self, call.callee, ErrorKind::Internal);
8635
    };
8636
    let variant = &unionType.variants[index];
8637
8638
    // Associate variant index with `call` node for the lowerer.
8639
    setVariantInfo(self, node, index, tag);
8640
8641
    // Check if this variant expects a payload.
8642
    let payloadType = variant.valueType;
8643
    if payloadType <> Type::Void {
8644
        try ensureTypeResolved(self, payloadType, node);
8645
        let recInfo = getRecord(payloadType)
8646
            else panic "resolveUnionVariantConstructor: payload is not a record";
8647
        try checkRecordConstructorArgs(self, node, call.args, recInfo);
8648
    } else {
8649
        if call.args.len > 0 {
8650
            throw emitError(self, node, ErrorKind::UnionVariantPayloadUnexpected(variant.name));
8651
        }
8652
    }
8653
    return setNodeType(self, node, Type::Nominal(unionNominal));
8654
}
8655
8656
/// Analyze an unlabeled record constructor call.
8657
///
8658
/// Handles the syntax `R(a, b)` for unlabeled records, checking that the
8659
/// number of arguments matches the record's field count and that each argument
8660
/// is assignable to its corresponding field type.
8661
unsafe fn resolveRecordConstructorCall 'arena (self: &mut Resolver 'arena, node: *ast::Node, call: ast::Call, recordType: *unsafe NominalType) -> Type
8662
    throws (ResolveError)
8663
{
8664
    let case NominalType::Record(recInfo) = *recordType
8665
        else panic "resolveRecordConstructorCall: not a record type";
8666
8667
    try requireRecordAccess(self, node, recInfo);
8668
    try checkRecordConstructorArgs(self, node, call.args, recInfo);
8669
    return setNodeType(self, node, Type::Nominal(recordType));
8670
}
8671
8672
/// Resolve the type name of a record literal, handling both record types and
8673
/// union variant payloads like `Union::Variant { ... }`.
8674
unsafe fn resolveRecordLitType 'arena (
8675
    self: &mut Resolver 'arena, node: *ast::Node, typeIdent: *ast::Node, hint: Type
8676
) -> ResolvedRecordLitType
8677
    throws (ResolveError)
8678
{
8679
    if let case ast::NodeValue::RegionApply { .. } = typeIdent.value {
8680
        let ty = try infer(self, typeIdent);
8681
        let case Type::Nominal(info) = ty else throw emitError(self, node, ErrorKind::ExpectedRecord);
8682
        return ResolvedRecordLitType { recordType: info, resultType: ty };
8683
    }
8684
    // Check if this is a scope access that might be a union variant.
8685
    if let case ast::NodeValue::ScopeAccess(access) = typeIdent.value {
8686
        let scope = self.scope;
8687
        let sym = try resolveAccess(self, typeIdent, access, scope);
8688
8689
        // Check if resolved symbol is a union variant.
8690
        if let case SymbolData::Variant { type, decl, ordinal, index } = sym.data {
8691
            let sourceTy = typeFor(self, typeIdent) else panic "resolveRecordLitType: missing union type";
8692
            let case Type::Nominal(source) = sourceTy else panic "resolveRecordLitType: invalid union type";
8693
            let unionNominalType = hintedNominal(source, hint);
8694
            try requireNominalArguments(self, unionNominalType, typeIdent);
8695
            try ensureNominalResolved(self, unionNominalType, typeIdent);
8696
            let case NominalType::Union(body) = *unionNominalType else panic;
8697
            let case Type::Nominal(payloadInfo) = body.variants[ordinal].valueType
8698
                else throw emitError(self, node, ErrorKind::ExpectedRecord);
8699
8700
            // Store the variant index for the lowerer.
8701
            setVariantInfo(self, node, ordinal, index);
8702
8703
            return ResolvedRecordLitType {
8704
                recordType: payloadInfo,
8705
                resultType: Type::Nominal(unionNominalType),
8706
            };
8707
        }
8708
        // Not a variant, must be a type.
8709
        let case SymbolData::Type(ty) = sym.data
8710
            else throw emitError(self, node, ErrorKind::ExpectedRecord);
8711
        return ResolvedRecordLitType {
8712
            recordType: ty,
8713
            resultType: Type::Nominal(ty),
8714
        };
8715
    }
8716
    // Simple identifier, resolve as type name.
8717
    let tyInfo = try resolveTypeName(self, typeIdent);
8718
    return ResolvedRecordLitType {
8719
        recordType: tyInfo,
8720
        resultType: Type::Nominal(tyInfo),
8721
    };
8722
}
8723
8724
/// Analyze a record literal expression.
8725
unsafe fn resolveRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
8726
    throws (ResolveError)
8727
{
8728
    // If no type name, infer an anonymous tuple type.
8729
    let typeIdent = lit.typeName else {
8730
        return try resolveAnonRecordLit(self, node, lit, hint);
8731
    };
8732
    // Resolve the type name, handling both record types and union variants.
8733
    let resolved = try resolveRecordLitType(self, node, typeIdent, hint);
8734
    let mut tyInfo = resolved.recordType;
8735
    let mut resultType = resolved.resultType;
8736
    let mut target = hint;
8737
    if let case Type::Optional(inner) = target {
8738
        set target = *inner;
8739
    }
8740
    if nominalApplication(tyInfo) == nil {
8741
        if let case Type::Nominal(info) = target {
8742
            if let applied = nominalApplication(info); applied.base == tyInfo {
8743
                set tyInfo = info;
8744
                set resultType = target;
8745
            }
8746
        }
8747
    }
8748
    try requireNominalArguments(self, tyInfo, typeIdent);
8749
8750
    // Lazily resolve record body if not yet done.
8751
    try ensureNominalResolved(self, tyInfo, typeIdent);
8752
    let case NominalType::Record(recordType) = *tyInfo
8753
        else throw emitError(self, node, ErrorKind::ExpectedRecord);
8754
8755
    try requireRecordAccess(self, node, recordType);
8756
    // Unlabeled records must use constructor call syntax `R(...)`, not brace syntax.
8757
    if not recordType.labeled {
8758
        throw emitError(self, node, ErrorKind::RecordFieldStyleMismatch);
8759
    }
8760
    // Check field count. With `{ .. }` syntax, fewer fields are allowed.
8761
    if lit.fields.len > recordType.fields.len {
8762
        throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
8763
            expected: recordType.fields.len as u32,
8764
            actual: lit.fields.len,
8765
        }));
8766
    }
8767
    if not lit.ignoreRest and lit.fields.len < recordType.fields.len {
8768
        let missingName = recordType.fields[lit.fields.len].name else panic;
8769
        throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
8770
    }
8771
8772
    // Fields must be in declaration order.
8773
    for fieldNode, idx in lit.fields {
8774
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
8775
            else panic "resolveRecordLit: expected field node value";
8776
        let label = fieldArg.label
8777
            else panic "resolveRecordLit: expected labeled field";
8778
        let fieldName = try nodeName(self, label);
8779
        let expected = recordType.fields[idx];
8780
        let expectedName = expected.name else panic;
8781
8782
        if fieldName <> expectedName {
8783
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
8784
                field: fieldName,
8785
                prev: expectedName,
8786
            });
8787
        }
8788
        setRecordFieldIndex(self, fieldNode, idx);
8789
        try checkAssignable(self, fieldArg.value, expected.fieldType);
8790
        setNodeType(self, fieldNode, expected.fieldType);
8791
    }
8792
    return setNodeType(self, node, resultType);
8793
}
8794
8795
/// Analyze an anonymous record literal, checking fields against the hint type.
8796
unsafe fn resolveAnonRecordLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type
8797
    throws (ResolveError)
8798
{
8799
    // Unwrap optional hint to get the inner record type.
8800
    let mut innerHint = hint;
8801
    if let case Type::Optional(inner) = hint {
8802
        set innerHint = *inner;
8803
    }
8804
    let mut hintInfo: ?RecordType = nil;
8805
    if let case Type::Nominal(info) = innerHint {
8806
        try ensureNominalResolved(self, info, node);
8807
        if let case NominalType::Record(s) = *info {
8808
            set hintInfo = s;
8809
        }
8810
    }
8811
    let targetInfo = hintInfo else {
8812
        throw emitError(self, node, ErrorKind::CannotInferType);
8813
    };
8814
8815
    try requireRecordAccess(self, node, targetInfo);
8816
    // Check field count.
8817
    if lit.fields.len <> targetInfo.fields.len {
8818
        if lit.fields.len < targetInfo.fields.len {
8819
            let missingName = targetInfo.fields[lit.fields.len].name else panic;
8820
            throw emitError(self, node, ErrorKind::RecordFieldMissing(missingName));
8821
        } else {
8822
            throw emitError(self, node, ErrorKind::RecordFieldCountMismatch(CountMismatch {
8823
                expected: targetInfo.fields.len as u32,
8824
                actual: lit.fields.len,
8825
            }));
8826
        }
8827
    }
8828
8829
    // Fields must be in declaration order.
8830
    for fieldNode, idx in lit.fields {
8831
        let case ast::NodeValue::RecordLitField(fieldArg) = fieldNode.value
8832
            else panic "resolveAnonRecordLit: expected field node value";
8833
        let label = fieldArg.label
8834
            else panic "resolveAnonRecordLit: expected labeled field";
8835
        let fieldName = try nodeName(self, label);
8836
        let expected = targetInfo.fields[idx];
8837
        let expectedName = expected.name else panic;
8838
8839
        if fieldName <> expectedName {
8840
            throw emitError(self, fieldNode, ErrorKind::RecordFieldOutOfOrder {
8841
                field: fieldName,
8842
                prev: expectedName,
8843
            });
8844
        }
8845
        setRecordFieldIndex(self, fieldNode, idx);
8846
        let fieldType = try visit(self, fieldArg.value, expected.fieldType);
8847
8848
        try expectAssignable(self, expected.fieldType, fieldType, fieldArg.value);
8849
        setNodeType(self, fieldNode, fieldType);
8850
    }
8851
    return setNodeType(self, node, innerHint);
8852
}
8853
8854
/// Analyze an array literal expression.
8855
unsafe fn resolveArrayLit 'arena (self: &mut Resolver 'arena, node: *ast::Node, items: *[*ast::Node], hint: Type) -> Type
8856
    throws (ResolveError)
8857
{
8858
    let length = items.len;
8859
    let mut expectedTy: Type = Type::Unknown;
8860
8861
    if let case Type::Array(ary) = hint {
8862
        set expectedTy = *ary.item;
8863
    } else if let case Type::Optional(inner) = hint {
8864
        if let case Type::Array(ary) = *inner {
8865
            set expectedTy = *ary.item;
8866
        }
8867
    };
8868
    for itemNode in items {
8869
        let itemTy = try visit(self, itemNode, expectedTy);
8870
        assert itemTy <> Type::Unknown;
8871
8872
        // Set the expected type to the first type we encounter.
8873
        if expectedTy == Type::Unknown {
8874
            set expectedTy = itemTy;
8875
        } else {
8876
            try expectAssignable(self, expectedTy, itemTy, itemNode);
8877
        }
8878
    }
8879
    if expectedTy == Type::Unknown {
8880
        throw emitError(self, node, ErrorKind::CannotInferType);
8881
    };
8882
    let arrayTy = Type::Array(ArrayType { item: allocType(self, expectedTy), length });
8883
    return setNodeType(self, node, arrayTy);
8884
}
8885
8886
/// Analyze an array repeat literal expression.
8887
unsafe fn resolveArrayRepeat 'arena (self: &mut Resolver 'arena, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type
8888
    throws (ResolveError)
8889
{
8890
    let mut itemHint = hint;
8891
    if let case Type::Array(ary) = hint {
8892
        set itemHint = *ary.item;
8893
    } else if let case Type::Optional(inner) = hint {
8894
        if let case Type::Array(ary) = *inner {
8895
            set itemHint = *ary.item;
8896
        }
8897
    }
8898
    let valueTy = try visit(self, lit.item, itemHint);
8899
    let count = try checkSizeInt(self, lit.count);
8900
    let arrayTy = Type::Array(ArrayType {
8901
        item: allocType(self, valueTy),
8902
        length: count,
8903
    });
8904
    return setNodeType(self, node, arrayTy);
8905
}
8906
8907
/// Resolve union variant access.
8908
unsafe fn resolveUnionVariantAccess 'arena (
8909
    self: &mut Resolver 'arena,
8910
    node: *ast::Node,
8911
    access: ast::Access,
8912
    unionType: UnionType,
8913
    variantName: *[u8]
8914
) -> *unsafe mut Symbol throws (ResolveError) {
8915
    // Look up the variant in the union's nominal type.
8916
    for i in 0..unionType.variants.len {
8917
        let variant = &unionType.variants[i];
8918
        if variant.name == variantName {
8919
            let case SymbolData::Variant { ordinal, index, .. } = variant.symbol.data
8920
                else panic "resolveUnionVariantAccess: expected variant symbol";
8921
8922
            // Associate the variant symbol with the child node.
8923
            setNodeSymbol(self, access.child, variant.symbol);
8924
            setNodeSymbol(self, node, variant.symbol);
8925
8926
            // Store the variant index for the lowerer.
8927
            setVariantInfo(self, node, ordinal, index);
8928
8929
            return variant.symbol;
8930
        }
8931
    }
8932
    throw emitError(self, access.child, ErrorKind::UnresolvedSymbol(variantName));
8933
}
8934
8935
/// Analyze a scope access expression.
8936
unsafe fn resolveScopeAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access, hint: Type) -> Type
8937
    throws (ResolveError)
8938
{
8939
    let scope = self.scope;
8940
    let sym = try resolveAccess(self, node, access, scope);
8941
    try checkStaticAccess(self, node, sym);
8942
    let mut ty: Type = undefined;
8943
8944
    match sym.data {
8945
        case SymbolData::Value { type, .. } => {
8946
            setNodeSymbol(self, node, sym);
8947
            set ty = type;
8948
        }
8949
        case SymbolData::Constant { type, value } => {
8950
            // Propagate the constant value.
8951
            if let val = value {
8952
                setNodeConstValue(self, node, val);
8953
            }
8954
            setNodeSymbol(self, node, sym);
8955
            set ty = type;
8956
        }
8957
        case SymbolData::Type(t) => {
8958
            setNodeSymbol(self, node, sym);
8959
            set ty = Type::Nominal(hintedNominal(t, hint));
8960
        }
8961
        case SymbolData::Variant { index, .. } => {
8962
            let ty = typeFor(self, node)
8963
                else throw emitError(self, node, ErrorKind::Internal);
8964
            let case Type::Nominal(info) = ty else panic "resolveScopeAccess: invalid variant type";
8965
            let applied = hintedNominal(info, hint);
8966
            try requireNominalArguments(self, applied, node);
8967
            try ensureNominalResolved(self, applied, node);
8968
            let variantTy = Type::Nominal(applied);
8969
            // For unions without payload, store the variant index as a constant.
8970
            if isVoidUnion(variantTy) {
8971
                setNodeConstValue(self, node, ConstValue::Int(ConstInt {
8972
                    magnitude: index as u64,
8973
                    bits: 32,
8974
                    signed: false,
8975
                    negative: false,
8976
                }));
8977
            }
8978
            return setNodeType(self, node, variantTy);
8979
        }
8980
        case SymbolData::Module { .. } => {
8981
            throw emitError(self, node, ErrorKind::UnexpectedModuleName);
8982
        }
8983
        case SymbolData::Trait(_) => { // Trait names are not values.
8984
            throw emitError(self, node, ErrorKind::UnexpectedTraitName);
8985
        }
8986
    }
8987
    return setNodeType(self, node, ty);
8988
}
8989
8990
/// Analyze a field access expression.
8991
unsafe fn resolveFieldAccess 'arena (self: &mut Resolver 'arena, node: *ast::Node, access: ast::Access) -> Type
8992
    throws (ResolveError)
8993
{
8994
    let parentTy = try infer(self, access.parent);
8995
    if isUnsafePointerType(parentTy) {
8996
        try requireUnsafe(self, access.parent);
8997
    }
8998
    let subjectTy = autoDeref(parentTy);
8999
    try ensureTypeResolved(self, subjectTy, access.parent);
9000
9001
    if let case Type::Slice { class, item, mutable } = subjectTy {
9002
        let fieldNode = access.child;
9003
        let fieldName = try nodeName(self, fieldNode);
9004
        if mem::eq(fieldName, PTR_FIELD) {
9005
            try requireUnsafe(self, node);
9006
            setRecordFieldIndex(self, fieldNode, 0);
9007
            return setNodeType(
9008
                self,
9009
                node,
9010
                Type::Pointer { class, target: item, mutable },
9011
            );
9012
        }
9013
        if mem::eq(fieldName, LEN_FIELD) {
9014
            setRecordFieldIndex(self, fieldNode, 1);
9015
            return setNodeType(self, node, Type::U32);
9016
        }
9017
        if mem::eq(fieldName, CAP_FIELD) {
9018
            setRecordFieldIndex(self, fieldNode, 2);
9019
            return setNodeType(self, node, Type::U32);
9020
        }
9021
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
9022
    }
9023
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
9024
        let fieldName = try nodeName(self, access.child);
9025
        let method = findTraitMethod(&traitInfo.methods[..], fieldName)
9026
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
9027
        return setNodeType(self, node, Type::Fn(method.fnType));
9028
    }
9029
9030
    match subjectTy {
9031
        case Type::Nominal(NominalType::Record(recordType)) => {
9032
            let fieldNode = access.child;
9033
            let fieldName = try nodeName(self, fieldNode);
9034
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
9035
                try requireRecordAccess(self, node, recordType);
9036
                let fieldTy = recordType.fields[fieldIndex].fieldType;
9037
                setRecordFieldIndex(self, fieldNode, fieldIndex);
9038
                return setNodeType(self, node, fieldTy);
9039
            }
9040
            // Not a field: check for a standalone method.
9041
            if let method = findMethod(self, subjectTy, fieldName) {
9042
                return setNodeType(self, node, Type::Fn(method.fnType));
9043
            }
9044
            throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
9045
        }
9046
        case Type::Array(arrayInfo) => {
9047
            let fieldNode = access.child;
9048
            let fieldName = try nodeName(self, fieldNode);
9049
9050
            if mem::eq(fieldName, LEN_FIELD) {
9051
                let lengthConst = constInt(arrayInfo.length as u64, 32, false, false);
9052
                setNodeConstValue(self, node, lengthConst);
9053
9054
                return setNodeType(self, node, Type::U32);
9055
            }
9056
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
9057
        }
9058
9059
        else => {
9060
            // Check for standalone methods on any nominal type (e.g. unions).
9061
            if let case Type::Nominal(_) = subjectTy {
9062
                let fieldName = try nodeName(self, access.child);
9063
                if let method = findMethod(self, subjectTy, fieldName) {
9064
                    return setNodeType(self, node, Type::Fn(method.fnType));
9065
                }
9066
            }
9067
            throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
9068
        }
9069
    }
9070
}
9071
9072
/// Return whether a pointer-like value grants mutable access.
9073
fn isMutablePointerLike(ty: Type) -> bool {
9074
    match ty {
9075
        case Type::Pointer { mutable, .. } => return mutable,
9076
        case Type::Slice { mutable, .. } => return mutable,
9077
        case Type::TraitObject { mutable, .. } => return mutable,
9078
        else => return false,
9079
    }
9080
}
9081
9082
/// Check exclusive access to an element or field of a container.
9083
unsafe fn canAccessExclusiveProjection 'arena (self: &mut Resolver 'arena, container: *ast::Node) -> bool
9084
    throws (ResolveError)
9085
{
9086
    let ty = try infer(self, container);
9087
    if let case Type::Slice { mutable: false, .. } = autoDeref(ty) {
9088
        return false;
9089
    }
9090
    match ty {
9091
        case Type::Pointer { class, mutable, .. } => {
9092
            if not mutable {
9093
                return false;
9094
            }
9095
            if class == types::PointerClass::Unsafe {
9096
                return true;
9097
            }
9098
        }
9099
        case Type::Slice { class, mutable, .. } => {
9100
            if not mutable {
9101
                return false;
9102
            }
9103
            if class == types::PointerClass::Unsafe {
9104
                return true;
9105
            }
9106
        }
9107
        else => {
9108
        },
9109
    }
9110
    return try canAccessExclusiveHandle(self, container);
9111
}
9112
9113
/// Check that a stored exclusive handle is not reached through shared access.
9114
unsafe fn canAccessExclusiveHandle 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
9115
    throws (ResolveError)
9116
{
9117
    match node.value {
9118
        case ast::NodeValue::FieldAccess(access) =>
9119
            return try canAccessExclusiveProjection(self, access.parent),
9120
        case ast::NodeValue::Subscript { container, .. } =>
9121
            return try canAccessExclusiveProjection(self, container),
9122
        case ast::NodeValue::Deref(inner) =>
9123
            return try canAccessExclusiveProjection(self, inner),
9124
        case ast::NodeValue::As(expr) =>
9125
            return try canAccessExclusiveHandle(self, expr.value),
9126
        case ast::NodeValue::CondExpr(cond) => {
9127
            if not try canAccessExclusiveHandle(self, cond.thenExpr) {
9128
                return false;
9129
            }
9130
            return try canAccessExclusiveHandle(self, cond.elseExpr);
9131
        }
9132
        else => return true,
9133
    }
9134
}
9135
9136
/// Check target mutability for implicit pointer access.
9137
unsafe fn canMutateThrough 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
9138
    throws (ResolveError)
9139
{
9140
    let ty = try infer(self, node);
9141
    match ty {
9142
        case Type::Pointer { class, mutable, .. } => {
9143
            if not mutable {
9144
                return false;
9145
            }
9146
            if class == types::PointerClass::Unsafe {
9147
                return true;
9148
            }
9149
            return try canAccessExclusiveHandle(self, node);
9150
        }
9151
        case Type::Slice { class, mutable, .. } => {
9152
            if not mutable {
9153
                return false;
9154
            }
9155
            if class == types::PointerClass::Unsafe {
9156
                return true;
9157
            }
9158
            return try canAccessExclusiveHandle(self, node);
9159
        }
9160
        case Type::TraitObject { class, mutable, .. } => {
9161
            if not mutable {
9162
                return false;
9163
            }
9164
            if class == types::PointerClass::Unsafe {
9165
                return true;
9166
            }
9167
            return try canAccessExclusiveHandle(self, node);
9168
        }
9169
        else => return try canBorrowMutFrom(self, node),
9170
    }
9171
}
9172
9173
/// Determine whether an expression can yield a mutable location for borrowing.
9174
unsafe fn canBorrowMutFrom 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> bool
9175
    throws (ResolveError)
9176
{
9177
    match node.value {
9178
        case ast::NodeValue::Ident(name) => {
9179
            let sym = findValueSymbol(self.scope, name)
9180
                else return false;
9181
            let case SymbolData::Value { mutable, .. } = sym.data
9182
                else return false;
9183
            return mutable;
9184
        }
9185
        case ast::NodeValue::FieldAccess(access) => {
9186
            let parentTy = try infer(self, access.parent);
9187
            if let case Type::Slice { .. } = autoDeref(parentTy) {
9188
                try requireUnsafe(self, node);
9189
            }
9190
            return try canMutateThrough(self, access.parent);
9191
        }
9192
        case ast::NodeValue::ScopeAccess(_) => {
9193
            // Module-qualified access to a top-level symbol. A `static`
9194
            // binds as a mutable value; a `constant` does not.
9195
            let _ = try infer(self, node);
9196
            let sym = symbolFor(self, node)
9197
                else return false;
9198
9199
            if let case SymbolData::Value { mutable, .. } = sym.data {
9200
                return mutable;
9201
            }
9202
            return false;
9203
        }
9204
        case ast::NodeValue::Subscript { container, .. } => {
9205
            let containerTy = try infer(self, container);
9206
            // Subscript auto-derefs pointers, so check the actual indexed type.
9207
            let subjectTy = autoDeref(containerTy);
9208
9209
            if let case Type::Slice { mutable, .. } = subjectTy {
9210
                if not mutable {
9211
                    return false;
9212
                }
9213
                return try canMutateThrough(self, container);
9214
            }
9215
            if let case Type::Array(_) = subjectTy {
9216
                return try canMutateThrough(self, container);
9217
            }
9218
            return false;
9219
        }
9220
        case ast::NodeValue::ArrayLit(_),
9221
             ast::NodeValue::ArrayRepeatLit(_) =>
9222
        {
9223
            return true;
9224
        }
9225
        case ast::NodeValue::Call(_) => {
9226
            // A call returning `*mut T` (or `&mut [T]`) yields a
9227
            // mutable place. Non-pointer returns cannot be mutably borrowed.
9228
            let ty = try infer(self, node);
9229
            if let case Type::Pointer { mutable, .. } = ty {
9230
                return mutable;
9231
            }
9232
            if let case Type::Slice { mutable, .. } = ty {
9233
                return mutable;
9234
            }
9235
            return false;
9236
        }
9237
        case ast::NodeValue::Deref(inner) => {
9238
            let innerTy = try infer(self, inner);
9239
9240
            if let case Type::Pointer { .. } = innerTy {
9241
                return try canMutateThrough(self, inner);
9242
            }
9243
            if let case Type::Slice { .. } = innerTy {
9244
                return try canMutateThrough(self, inner);
9245
            }
9246
            // Record deref: mutability depends on the inner binding.
9247
            if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy {
9248
                if not recInfo.labeled and recInfo.fields.len == 1 {
9249
                    return try canBorrowMutFrom(self, inner);
9250
                }
9251
            }
9252
            return false;
9253
        }
9254
        else => {
9255
            return false;
9256
        }
9257
    }
9258
}
9259
9260
/// Restrict a pointee lifetime to the borrow of its exclusive owner.
9261
unsafe fn constrainAddressClass(storage: types::PointerClass, owner: types::PointerClass) -> types::PointerClass {
9262
    if owner == types::PointerClass::Owned or owner == types::PointerClass::Unsafe {
9263
        return storage;
9264
    }
9265
    if owner == types::PointerClass::Ref {
9266
        return owner;
9267
    }
9268
    let case types::PointerClass::Region(ownerRegion) = owner else panic;
9269
    if let case types::PointerClass::Region(storageRegion) = storage {
9270
        if types::regionContains(storageRegion, ownerRegion) {
9271
            return owner;
9272
        }
9273
        if types::regionContains(ownerRegion, storageRegion) {
9274
            return storage;
9275
        }
9276
        return types::PointerClass::Ref;
9277
    }
9278
    if storage == types::PointerClass::Owned {
9279
        return owner;
9280
    }
9281
    return storage;
9282
}
9283
9284
/// Get the usable pointee lifetime of a pointer or slice value.
9285
unsafe fn pointerAddressClass 'arena (
9286
    self: &Resolver 'arena, node: *ast::Node, class: types::PointerClass, mutable: bool
9287
) -> types::PointerClass {
9288
    if not mutable or class == types::PointerClass::Unsafe {
9289
        return class;
9290
    }
9291
    return constrainAddressClass(class, exclusiveOwnerClass(self, node));
9292
}
9293
9294
/// Get the lifetime of storage selected by a pointer or slice subscript.
9295
unsafe fn indexedPointerClass 'arena (self: &Resolver 'arena, container: *ast::Node) -> ?types::PointerClass {
9296
    let ty = typeFor(self, container) else return nil;
9297
    if let case Type::Pointer { class, target, mutable } = ty {
9298
        let parentClass = pointerAddressClass(self, container, class, mutable);
9299
        if let case Type::Slice { class: sliceClass, mutable: sliceMutable, .. } = *target {
9300
            if not sliceMutable or sliceClass == types::PointerClass::Unsafe {
9301
                return sliceClass;
9302
            }
9303
            return constrainAddressClass(sliceClass, parentClass);
9304
        }
9305
        return parentClass;
9306
    }
9307
    if let case Type::Slice { class, mutable, .. } = ty {
9308
        return pointerAddressClass(self, container, class, mutable);
9309
    }
9310
    return nil;
9311
}
9312
9313
/// Get the borrow that controls access to a stored exclusive handle.
9314
/// Directly owned values have no additional borrow restriction.
9315
unsafe fn exclusiveOwnerClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
9316
    match node.value {
9317
        case ast::NodeValue::FieldAccess(access) => {
9318
            if let ty = typeFor(self, access.parent) {
9319
                match ty {
9320
                    case Type::Pointer { class, mutable, .. } =>
9321
                        return pointerAddressClass(self, access.parent, class, mutable),
9322
                    case Type::Slice { class, mutable, .. } =>
9323
                        return pointerAddressClass(self, access.parent, class, mutable),
9324
                    else => {
9325
                    },
9326
                }
9327
            }
9328
            return exclusiveOwnerClass(self, access.parent);
9329
        }
9330
        case ast::NodeValue::Subscript { container, .. } => {
9331
            if let class = indexedPointerClass(self, container) {
9332
                return class;
9333
            }
9334
            return exclusiveOwnerClass(self, container);
9335
        }
9336
        case ast::NodeValue::Deref(target) => {
9337
            if let ty = typeFor(self, target) {
9338
                if let case Type::Pointer { class, mutable, .. } = ty {
9339
                    return pointerAddressClass(self, target, class, mutable);
9340
                }
9341
            }
9342
            return exclusiveOwnerClass(self, target);
9343
        }
9344
        case ast::NodeValue::As(expr) => return exclusiveOwnerClass(self, expr.value),
9345
        case ast::NodeValue::CondExpr(cond) =>
9346
            return constrainAddressClass(exclusiveOwnerClass(self, cond.thenExpr), exclusiveOwnerClass(self, cond.elseExpr)),
9347
        else => return types::PointerClass::Owned,
9348
    }
9349
}
9350
9351
/// Return the storage class of an addressed location.
9352
unsafe fn addressStorageClass 'arena (self: &Resolver 'arena, node: *ast::Node) -> types::PointerClass {
9353
    match node.value {
9354
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
9355
            if let sym = symbolFor(self, node) {
9356
                match sym.node.value {
9357
                    case ast::NodeValue::StaticDecl(_), ast::NodeValue::ConstDecl(_) =>
9358
                        return types::PointerClass::Owned,
9359
                    else => {}
9360
                }
9361
            }
9362
        }
9363
        case ast::NodeValue::FieldAccess(access) => {
9364
            if let ty = typeFor(self, access.parent) {
9365
                if let case Type::Pointer { class, mutable, .. } = ty {
9366
                    return pointerAddressClass(self, access.parent, class, mutable);
9367
                }
9368
            }
9369
            return addressStorageClass(self, access.parent);
9370
        }
9371
        case ast::NodeValue::Subscript { container, .. } => {
9372
            if let class = indexedPointerClass(self, container) {
9373
                return class;
9374
            }
9375
            return addressStorageClass(self, container);
9376
        }
9377
        case ast::NodeValue::Deref(target) => {
9378
            if let ty = typeFor(self, target) {
9379
                if let case Type::Pointer { class, mutable, .. } = ty {
9380
                    return pointerAddressClass(self, target, class, mutable);
9381
                }
9382
            }
9383
            return addressStorageClass(self, target);
9384
        }
9385
        else => {}
9386
    }
9387
    return types::PointerClass::Ref;
9388
}
9389
9390
/// Select an address type without extending the target storage lifetime.
9391
unsafe fn addressClass 'arena (self: &mut Resolver 'arena, target: *ast::Node, hint: Type) -> types::PointerClass
9392
    throws (ResolveError)
9393
{
9394
    if isUnsafePointerType(hint) {
9395
        try requireUnsafe(self, target);
9396
        return types::PointerClass::Unsafe;
9397
    }
9398
    if isRefType(hint) {
9399
        if referenceRegion(hint) <> nil {
9400
            return addressStorageClass(self, target);
9401
        }
9402
        return types::PointerClass::Ref;
9403
    }
9404
    match target.value {
9405
        case ast::NodeValue::ArrayLit(_), ast::NodeValue::ArrayRepeatLit(_) => {
9406
            if isConstExpr(self, target) {
9407
                return types::PointerClass::Owned;
9408
            }
9409
        }
9410
        else => {}
9411
    }
9412
    return addressStorageClass(self, target);
9413
}
9414
9415
/// Return the cell type whose payload contains a place during type analysis.
9416
unsafe fn inferCellPayload 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?Type
9417
    throws (ResolveError)
9418
{
9419
    match node.value {
9420
        case ast::NodeValue::Deref(target) => {
9421
            let ty = try infer(self, target);
9422
            if let case Type::Cell { .. } = ty {
9423
                return ty;
9424
            }
9425
        }
9426
        case ast::NodeValue::FieldAccess(access) =>
9427
            return try inferCellPayload(self, access.parent),
9428
        case ast::NodeValue::Subscript { container, .. } =>
9429
            return try inferCellPayload(self, container),
9430
        else => {}
9431
    }
9432
    return nil;
9433
}
9434
9435
/// Return whether a typed expression accesses a whole cell payload.
9436
export fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
9437
    if let case ast::NodeValue::Deref(target) = node.value {
9438
        if let ty = typeFor(self, target) {
9439
            if let case Type::Cell { .. } = ty {
9440
                return true;
9441
            }
9442
        }
9443
    }
9444
    return false;
9445
}
9446
9447
/// Return whether an expression creates shared mutable access to a source place.
9448
fn createsCellBorrow(node: *ast::Node) -> bool {
9449
    match node.value {
9450
        case ast::NodeValue::AddressOf(address) => return address.kind == ast::AddressKind::Cell,
9451
        case ast::NodeValue::As(expr) => return createsCellBorrow(expr.value),
9452
        case ast::NodeValue::RegionApply { value, .. } => return createsCellBorrow(value),
9453
        case ast::NodeValue::CondExpr(cond) =>
9454
            return createsCellBorrow(cond.thenExpr) or createsCellBorrow(cond.elseExpr),
9455
        else => return false,
9456
    }
9457
}
9458
9459
/// Find the exclusive handle that owns an addressed place.
9460
fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node {
9461
    let mut parent: ?*ast::Node = nil;
9462
    match node.value {
9463
        case ast::NodeValue::Deref(target) => set parent = target,
9464
        case ast::NodeValue::FieldAccess(access) => set parent = access.parent,
9465
        case ast::NodeValue::Subscript { container, .. } => set parent = container,
9466
        else => {}
9467
    }
9468
    let parentNode = parent else return nil;
9469
    if let ty = typeFor(self, parentNode) {
9470
        match ty {
9471
            case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parentNode,
9472
            else => {}
9473
        }
9474
    }
9475
    return addressOwner(self, parentNode);
9476
}
9477
9478
/// Analyze an address-of expression.
9479
unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
9480
    throws (ResolveError)
9481
{
9482
    let payloadCell = try inferCellPayload(self, addr.target);
9483
    if let cellTy = payloadCell {
9484
        let case Type::Cell { permission, .. } = cellTy
9485
            else panic "resolveAddressOf: invalid cell payload";
9486
        if permission == nil or addr.kind == ast::AddressKind::Cell {
9487
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
9488
        }
9489
    }
9490
    if addr.kind == ast::AddressKind::Cell and not ast::isPlaceExpr(addr.target) {
9491
        throw emitError(self, addr.target, ErrorKind::RefBinding);
9492
    }
9493
    if payloadCell == nil and ast::isExclusiveAddress(addr) {
9494
        if not try canBorrowMutFrom(self, addr.target) {
9495
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
9496
        }
9497
    }
9498
    if let case ast::NodeValue::Subscript { container, index } = addr.target.value {
9499
        if let case ast::NodeValue::Range(range) = index.value {
9500
            if addr.kind == ast::AddressKind::Cell {
9501
                throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
9502
            }
9503
            let containerTy = try infer(self, container);
9504
            let subjectTy = autoDeref(containerTy);
9505
9506
            try checkSliceRangeIndices(self, range);
9507
9508
            let mut info = sliceRangeInfo(subjectTy)
9509
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
9510
            if ast::isExclusiveAddress(addr) and not info.mutable {
9511
                throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
9512
            }
9513
            if let capacity = info.capacity {
9514
                try validateArraySliceBounds(self, range, capacity, node);
9515
            }
9516
            set info.mutable = addr.kind == ast::AddressKind::Mutable;
9517
            let class = try addressClass(self, addr.target, hint);
9518
            let sliceTy = Type::Slice { class, item: info.itemType, mutable: info.mutable };
9519
            let alloc = allocType(self, sliceTy);
9520
            setSliceRangeInfo(self, node, info);
9521
            setNodeType(self, addr.target, *alloc);
9522
            return setNodeType(self, node, *alloc);
9523
        }
9524
    }
9525
    // Derive a hint for the target type from the slice hint.
9526
    let mut targetHint: Type = Type::Unknown;
9527
    if let case Type::Slice { item, .. } = hint {
9528
        set targetHint = Type::Array(ArrayType { item, length: 0 });
9529
    }
9530
    let targetTy = try visit(self, addr.target, targetHint);
9531
    let class = try addressClass(self, addr.target, hint);
9532
9533
    // Mark local variable symbols as address-taken so the lowerer
9534
    // allocates a stack slot eagerly.
9535
    if let case ast::NodeValue::Ident(name) = addr.target.value {
9536
        if let sym = findValueSymbol(self.scope, name) {
9537
            match &mut sym.data {
9538
                case SymbolData::Value { addressTaken, .. } => {
9539
                    set *addressTaken = true;
9540
                }
9541
                else => {}
9542
            }
9543
        }
9544
    }
9545
9546
    if addr.kind == ast::AddressKind::Cell {
9547
        let mut permission: ?*unsafe types::Region = nil;
9548
        if let permissionNode = addr.permission {
9549
            set permission = try resolveRegion(self, permissionNode);
9550
        }
9551
        try validateCellPayload(self, node, targetTy, permission);
9552
        if let case types::PointerClass::Region(region) = class {
9553
            try validateRegionStorage(self, addr.target, targetTy, region);
9554
        } else if class == types::PointerClass::Owned
9555
            and try containsStorageRegion(self, addr.target, targetTy)
9556
        {
9557
            throw emitError(self, addr.target, ErrorKind::InvalidCellPayload);
9558
        }
9559
        return setNodeType(self, node, Type::Cell {
9560
            class, permission, payload: allocType(self, targetTy),
9561
        });
9562
    }
9563
    if let case Type::Array(arrayInfo) = targetTy {
9564
        match addr.target.value {
9565
            case ast::NodeValue::ArrayLit(_),
9566
                 ast::NodeValue::ArrayRepeatLit(_) =>
9567
            {
9568
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.kind == ast::AddressKind::Mutable };
9569
                return setNodeType(self, node, *allocType(self, sliceTy));
9570
            }
9571
            else => {}
9572
        }
9573
    }
9574
    let pointerTy = Type::Pointer {
9575
        class, target: allocType(self, targetTy), mutable: addr.kind == ast::AddressKind::Mutable,
9576
    };
9577
    return setNodeType(self, node, pointerTy);
9578
}
9579
9580
/// Analyze a dereference expression.
9581
unsafe fn resolveDeref 'arena (self: &mut Resolver 'arena, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
9582
    throws (ResolveError)
9583
{
9584
    let operandTy = try visit(self, targetNode, hint);
9585
    if let case Type::Cell { class, permission, payload } = operandTy {
9586
        if class == types::PointerClass::Unsafe {
9587
            try requireUnsafe(self, targetNode);
9588
        }
9589
        try validateCellPayload(self, node, *payload, permission);
9590
        return setNodeType(self, node, *payload);
9591
    }
9592
    if let case Type::Pointer { class, target, .. } = operandTy {
9593
        if class == types::PointerClass::Unsafe {
9594
            try requireUnsafe(self, targetNode);
9595
        }
9596
        // Disallow dereferencing opaque pointers.
9597
        if *target == Type::Opaque {
9598
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
9599
        }
9600
        return setNodeType(self, node, *target);
9601
    }
9602
    // Auto-deref for single-field unlabeled records.
9603
    if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy {
9604
        if not recInfo.labeled and recInfo.fields.len == 1 {
9605
            try requireRecordAccess(self, node, recInfo);
9606
            let fieldTy = recInfo.fields[0].fieldType;
9607
            setRecordFieldIndex(self, node, 0);
9608
            return setNodeType(self, node, fieldTy);
9609
        }
9610
    }
9611
    throw emitError(self, targetNode, ErrorKind::ExpectedPointer);
9612
}
9613
9614
/// Check if a type is a pointer to opaque.
9615
fn isOpaquePointer(ty: Type) -> bool {
9616
    if let case Type::Pointer { target, .. } = ty {
9617
        return *target == Type::Opaque;
9618
    }
9619
    return false;
9620
}
9621
9622
/// Check if a type is an opaque slice.
9623
fn isOpaqueSlice(ty: Type) -> bool {
9624
    if let case Type::Slice { item, .. } = ty {
9625
        return *item == Type::Opaque;
9626
    }
9627
    return false;
9628
}
9629
9630
/// Check if an `as` cast between two types is valid.
9631
unsafe fn isValidCast(source: Type, target: Type) -> bool {
9632
    // Allow identity casts.
9633
    if source == target {
9634
        return true;
9635
    }
9636
    // Allow numeric to numeric.
9637
    if isNumericType(source) and isNumericType(target) {
9638
        return true;
9639
    }
9640
    // Allow `void` union to numeric.
9641
    // TODO: Check that variant index fits in target type.
9642
    if isVoidUnion(source) and isNumericType(target) {
9643
        return true;
9644
    }
9645
    // Allow address to numeric.
9646
    if let case Type::Slice { .. } = source {
9647
        // Disallow slice to numeric; slices are fat pointers.
9648
    } else if isAddressType(source) and isNumericType(target) {
9649
        return true;
9650
    }
9651
    // Allow pointer casts if one side is `*opaque` or target types are castable.
9652
    if let case Type::Pointer {
9653
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
9654
    } = source {
9655
        if let case Type::Pointer {
9656
            class: targetClass, target: targetTarget, mutable: targetMutable,
9657
        } = target {
9658
            if sourceClass <> targetClass {
9659
                return false;
9660
            }
9661
            if targetMutable and not sourceMutable {
9662
                return false;
9663
            }
9664
            if isOpaquePointer(source) or isOpaquePointer(target) {
9665
                return true;
9666
            }
9667
            return isValidCast(*sourceTarget, *targetTarget);
9668
        }
9669
    }
9670
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
9671
    // or element types are castable.
9672
    if let case Type::Slice {
9673
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
9674
    } = source {
9675
        if let case Type::Slice {
9676
            class: targetClass, item: targetItem, mutable: targetMutable,
9677
        } = target {
9678
            if sourceClass <> targetClass {
9679
                return false;
9680
            }
9681
            if targetMutable and not sourceMutable {
9682
                return false;
9683
            }
9684
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
9685
                return true;
9686
            }
9687
            if *targetItem == Type::U8 {
9688
                return true;
9689
            }
9690
            return isValidCast(*sourceItem, *targetItem);
9691
        }
9692
    }
9693
    return false;
9694
}
9695
9696
/// Require casts into region-dependent storage to preserve its typed contents.
9697
fn regionalCastPreservesType(source: Type, target: Type) -> bool {
9698
    if typesEqual(source, target) {
9699
        return true;
9700
    }
9701
    if let case Type::Pointer { target: sourceItem, .. } = source {
9702
        let case Type::Pointer { target: targetItem, .. } = target else return false;
9703
        return typesEqual(*sourceItem, *targetItem);
9704
    }
9705
    if let case Type::Slice { item: sourceItem, .. } = source {
9706
        let case Type::Slice { item: targetItem, .. } = target else return false;
9707
        return typesEqual(*sourceItem, *targetItem);
9708
    }
9709
    return false;
9710
}
9711
9712
/// Analyze an `as` cast expression.
9713
unsafe fn resolveAs 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: ast::As) -> Type
9714
    throws (ResolveError)
9715
{
9716
    let targetTy = try infer(self, expr.type);
9717
    let sourceTy = try visit(self, expr.value, targetTy);
9718
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
9719
        try requireUnsafe(self, node);
9720
    }
9721
9722
    assert sourceTy <> Type::Unknown;
9723
    assert targetTy <> Type::Unknown;
9724
9725
    if let case Type::Cell { class, permission, payload } = targetTy {
9726
        if let case Type::Pointer { class: sourceClass, target, mutable: true } = sourceTy;
9727
            permission == nil and sourceClass == class
9728
            and class <> types::PointerClass::Unsafe and typesEqual(*target, *payload)
9729
        {
9730
            return setNodeType(self, node, targetTy);
9731
        }
9732
        if typesEqual(sourceTy, targetTy) {
9733
            return setNodeType(self, node, targetTy);
9734
        }
9735
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast { from: sourceTy, to: targetTy }));
9736
    }
9737
    if containsRegion(targetTy) and not regionalCastPreservesType(sourceTy, targetTy) {
9738
        throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
9739
            from: sourceTy, to: targetTy,
9740
        }));
9741
    }
9742
    let mut valid = isValidCast(sourceTy, targetTy);
9743
    if let case Type::Pointer {
9744
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
9745
    } = sourceTy {
9746
        if let case Type::Pointer {
9747
            class: targetClass, target: targetTarget, mutable: targetMutable,
9748
        } = targetTy {
9749
            if types::isReference(sourceClass) and
9750
               targetClass == types::PointerClass::Unsafe and
9751
               (not targetMutable or sourceMutable) and
9752
               isValidCast(*sourceTarget, *targetTarget)
9753
            {
9754
                set valid = true;
9755
            }
9756
        }
9757
    }
9758
    if let case Type::Slice {
9759
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
9760
    } = sourceTy {
9761
        if let case Type::Slice {
9762
            class: targetClass, item: targetItem, mutable: targetMutable,
9763
        } = targetTy {
9764
            if types::isReference(sourceClass) and
9765
               targetClass == types::PointerClass::Unsafe and
9766
               (not targetMutable or sourceMutable) and
9767
               isValidCast(*sourceItem, *targetItem)
9768
            {
9769
                set valid = true;
9770
            }
9771
        }
9772
    }
9773
    if valid {
9774
        if let case Type::Pointer { target: sourceTarget, .. } = sourceTy {
9775
            if let case Type::Pointer { target: targetTarget, .. } = targetTy {
9776
                if *targetTarget <> Type::Opaque and not typesEqual(*sourceTarget, *targetTarget) {
9777
                    try requireUnsafe(self, node);
9778
                }
9779
            }
9780
        }
9781
        if let case Type::Slice { item: sourceItem, .. } = sourceTy {
9782
            if let case Type::Slice { item: targetItem, .. } = targetTy {
9783
                if *targetItem <> Type::Opaque and not typesEqual(*sourceItem, *targetItem) {
9784
                    try requireUnsafe(self, node);
9785
                }
9786
            }
9787
        }
9788
        // Propagate the constant value after applying the cast's target-width
9789
        // truncation and signed interpretation.
9790
        if let value = constValueEntry(self, expr.value) {
9791
            if let case ConstValue::Int(i) = value {
9792
                setNodeConstValue(self, node, castConstInt(i, targetTy));
9793
            }
9794
        }
9795
        return setNodeType(self, node, targetTy);
9796
    }
9797
    throw emitError(self, node, ErrorKind::InvalidAsCast(InvalidAsCast {
9798
        from: sourceTy,
9799
        to: targetTy,
9800
    }));
9801
}
9802
9803
/// Analyze a range expression.
9804
unsafe fn resolveRange 'arena (self: &mut Resolver 'arena, node: *ast::Node, range: ast::Range) -> Type
9805
    throws (ResolveError)
9806
{
9807
    let mut start: ?*Type = nil;
9808
    let mut end: ?*Type = nil;
9809
9810
    if let s = range.start {
9811
        let startTy = try checkNumeric(self, s);
9812
9813
        if let e = range.end {
9814
            let endTy = try checkNumeric(self, e);
9815
            let mut resolvedTy = startTy;
9816
9817
            // Infer unsuffixed integer literals from the opposite bound.
9818
            if startTy == Type::Int and endTy <> Type::Int {
9819
                let _ = try checkAssignable(self, s, endTy);
9820
                set resolvedTy = endTy;
9821
            } else if endTy == Type::Int and startTy <> Type::Int {
9822
                let _ = try checkAssignable(self, e, startTy);
9823
                set resolvedTy = startTy;
9824
            } else {
9825
                let _ = try checkAssignable(self, e, startTy);
9826
            }
9827
            set start = allocType(self, resolvedTy);
9828
            set end = allocType(self, resolvedTy);
9829
        } else {
9830
            set start = allocType(self, startTy);
9831
        }
9832
    } else if let e = range.end {
9833
        set end = allocType(self, try checkNumeric(self, e));
9834
    }
9835
    return setNodeType(self, node, Type::Range { start, end });
9836
}
9837
9838
/// Analyze a `try` expression and its handlers.
9839
/// The `expected` type is used to determine if the value is discarded (`Void`)
9840
/// or if the catch expression needs type checking.
9841
unsafe fn resolveTry 'arena (self: &mut Resolver 'arena, node: *ast::Node, tryExpr: ast::Try, hint: Type) -> Type
9842
    throws (ResolveError)
9843
{
9844
    let call = tryExpr.expr;
9845
    let case ast::NodeValue::Call(callExpr) = call.value
9846
        else throw emitError(self, call, ErrorKind::TryNonThrowing);
9847
    let resultTy = try resolveCall(self, call, callExpr, CallCtx::Try, Type::Unknown);
9848
9849
    // TODO: It's annoying that we need to re-fetch the function type after
9850
    // analyzing the call.
9851
    let calleeTy = typeFor(self, callExpr.callee)
9852
        else return setNodeType(self, node, resultTy);
9853
    let case Type::Fn(calleeInfo) = calleeTy
9854
        else throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
9855
9856
    if calleeInfo.throwList.len == 0 {
9857
        throw emitError(self, callExpr.callee, ErrorKind::TryNonThrowing);
9858
    }
9859
    // If we're not catching the error, nor panicking on error, nor returning
9860
    // optional, then the current function must be able to propagate it.
9861
    let mut tryResultTy = resultTy;
9862
    if tryExpr.returnsOptional {
9863
        // `try?` converts errors to `nil` and wraps the result in an optional.
9864
        if let case Type::Optional(_) = resultTy {
9865
            // Already optional, no wrapping needed.
9866
        } else {
9867
            set tryResultTy = Type::Optional(allocType(self, resultTy));
9868
        }
9869
    } else if tryExpr.catches.len > 0 {
9870
        // `try ... catch` -- one or more catch clauses.
9871
        set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint);
9872
    } else if not tryExpr.shouldPanic {
9873
        let fnInfo = self.currentFn
9874
            else throw emitError(self, node, ErrorKind::TryRequiresThrows);
9875
        if fnInfo.throwList.len == 0 {
9876
            throw emitError(self, node, ErrorKind::TryRequiresThrows);
9877
        }
9878
        // Check that *all* thrown errors of the callee can be propagated by
9879
        // the caller.
9880
        for throwTy in calleeInfo.throwList {
9881
            let mut found = false;
9882
9883
            for callerThrowTy in fnInfo.throwList {
9884
                if callerThrowTy == throwTy {
9885
                    set found = true;
9886
                    break;
9887
                }
9888
            }
9889
            if not found {
9890
                throw emitError(self, node, ErrorKind::TryIncompatibleError);
9891
            }
9892
        }
9893
    }
9894
    return setNodeType(self, node, tryResultTy);
9895
}
9896
9897
/// Check that a `catch` body is assignable to the expected result type, but only
9898
/// in expression context (`hint` is neither `Unknown` nor `Void`).
9899
unsafe fn checkCatchBody 'arena (self: &mut Resolver 'arena, body: *ast::Node, resultTy: Type, hint: Type)
9900
    throws (ResolveError)
9901
{
9902
    if hint <> Type::Unknown and hint <> Type::Void {
9903
        try checkAssignable(self, body, resultTy);
9904
    }
9905
}
9906
9907
/// Resolve catch clauses for a `try ... catch` expression.
9908
///
9909
/// For a single untyped catch (with or without binding), resolves the catch
9910
/// body and returns the result type. Multi-error callees with inferred bindings
9911
/// are rejected; you must use typed catches.
9912
unsafe fn resolveTryCatches 'arena (
9913
    self: &mut Resolver 'arena,
9914
    node: *ast::Node,
9915
    catches: *[*ast::Node],
9916
    calleeInfo: *FnType,
9917
    resultTy: Type,
9918
    hint: Type
9919
) -> Type throws (ResolveError) {
9920
    let firstNode = catches[0];
9921
    let case ast::NodeValue::CatchClause(first) = firstNode.value else
9922
        throw emitError(self, node, ErrorKind::UnexpectedNode(firstNode));
9923
9924
    // Typed catches: dispatch to dedicated handler.
9925
    if first.typeNode <> nil {
9926
        return try resolveTypedCatches(self, node, catches, calleeInfo, resultTy, hint);
9927
    }
9928
    // Single untyped catch clause.
9929
    if let binding = first.binding {
9930
        if calleeInfo.throwList.len > 1 {
9931
            throw emitError(self, binding, ErrorKind::TryCatchMultiError);
9932
        }
9933
        enterScope(self, node);
9934
9935
        let errTy = *calleeInfo.throwList[0];
9936
        try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
9937
    }
9938
    let bodyTy = try visit(self, first.body, resultTy);
9939
9940
    if let _ = first.binding {
9941
        exitScope(self);
9942
    }
9943
    try checkCatchBody(self, first.body, resultTy, hint);
9944
9945
    return bodyTy if resultTy == Type::Never else resultTy;
9946
}
9947
9948
/// Resolve typed catch clauses (`catch e as T {..} catch e as S {..}`).
9949
///
9950
/// Validates that each type annotation is in the callee's throw list, that
9951
/// there are no duplicate catch types, and that the clauses are exhaustive.
9952
unsafe fn resolveTypedCatches 'arena (
9953
    self: &mut Resolver 'arena,
9954
    node: *ast::Node,
9955
    catches: *[*ast::Node],
9956
    calleeInfo: *FnType,
9957
    resultTy: Type,
9958
    hint: Type
9959
) -> Type throws (ResolveError) {
9960
    // Track which of the callee's throw types have been covered.
9961
    let mut covered: [bool; MAX_FN_THROWS] = [false; MAX_FN_THROWS];
9962
    let mut hasCatchAll = false;
9963
    let mut catchTy = Type::Never;
9964
9965
    for clauseNode in catches {
9966
        let case ast::NodeValue::CatchClause(clause) = clauseNode.value else
9967
            throw emitError(self, node, ErrorKind::UnexpectedNode(clauseNode));
9968
9969
        if let typeNode = clause.typeNode {
9970
            // Typed catch clause: validate against callee's throw list.
9971
            let errTy = try infer(self, typeNode);
9972
            let mut foundIdx: ?u32 = nil;
9973
9974
            for throwType, j in calleeInfo.throwList {
9975
                if errTy == *throwType {
9976
                    set foundIdx = j;
9977
                    break;
9978
                }
9979
            }
9980
            let idx = foundIdx else {
9981
                throw emitError(self, typeNode, ErrorKind::TryIncompatibleError);
9982
            };
9983
            if covered[idx] {
9984
                throw emitError(self, typeNode, ErrorKind::TryCatchDuplicateType);
9985
            }
9986
            set covered[idx] = true;
9987
9988
            // Bind the error variable if present.
9989
            if let binding = clause.binding {
9990
                enterScope(self, clauseNode);
9991
                try bindValueIdent(self, binding, binding, errTy, false, 0, 0);
9992
            }
9993
        } else {
9994
            // Catch-all clause with no type annotation or binding.
9995
            set hasCatchAll = true;
9996
        }
9997
        // Resolve the catch body and check assignability.
9998
        let bodyTy = try visit(self, clause.body, resultTy);
9999
        if bodyTy <> Type::Never { set catchTy = Type::Void; }
10000
        // Only typed clauses can have bindings.
10001
        if let _ = clause.binding {
10002
            exitScope(self);
10003
        }
10004
        try checkCatchBody(self, clause.body, resultTy, hint);
10005
    }
10006
10007
    // Check exhaustiveness: all callee error types must be covered.
10008
    if not hasCatchAll {
10009
        for i in 0..calleeInfo.throwList.len {
10010
            if not covered[i] {
10011
                throw emitError(self, node, ErrorKind::TryCatchNonExhaustive);
10012
            }
10013
        }
10014
    }
10015
    return catchTy if resultTy == Type::Never else resultTy;
10016
}
10017
10018
/// Analyze a `throw` statement.
10019
unsafe fn resolveThrow 'arena (self: &mut Resolver 'arena, node: *ast::Node, expr: *ast::Node) -> Type
10020
    throws (ResolveError)
10021
{
10022
    let fnInfo = self.currentFn
10023
        else throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
10024
    if fnInfo.throwList.len == 0 {
10025
        throw emitError(self, node, ErrorKind::ThrowRequiresThrows);
10026
    }
10027
    let throwTy = try infer(self, expr);
10028
    for errTy in fnInfo.throwList {
10029
        if let coerce = isAssignable(self, *errTy, throwTy, expr) {
10030
            setNodeCoercion(self, expr, coerce);
10031
            return setNodeType(self, node, Type::Never);
10032
        }
10033
    }
10034
    throw emitError(self, expr, ErrorKind::ThrowIncompatibleError);
10035
}
10036
10037
/// Analyze a `return` statement.
10038
unsafe fn resolveReturn 'arena (self: &mut Resolver 'arena, node: *ast::Node, retVal: ?*ast::Node) -> Type
10039
    throws (ResolveError)
10040
{
10041
    let f = self.currentFn
10042
        else throw emitError(self, node, ErrorKind::UnexpectedReturn);
10043
    let expected = *f.returnType;
10044
10045
    if let val = retVal {
10046
        let actualTy = try visit(self, val, expected);
10047
        if let source = referenceRegion(actualTy) {
10048
            if let destination = referenceRegion(expected);
10049
                not types::regionContains(source, destination)
10050
            {
10051
                let mut current = self.regionScope;
10052
                while let scope = current {
10053
                    let mut ownsSource = false;
10054
                    for region in scope.entries {
10055
                        if region.id == source.id {
10056
                            set ownsSource = true;
10057
                            break;
10058
                        }
10059
                    }
10060
                    if ownsSource {
10061
                        let mut lexical: ?*unsafe mut Scope = self.scope;
10062
                        while let localScope = lexical {
10063
                            if let owner = localScope.owner {
10064
                                if let case ast::NodeValue::RegionBlock { bindings, .. } =
10065
                                    owner.value
10066
                                {
10067
                                    if let case NodeExtra::Regions(blockRegions) =
10068
                                        self.nodeData.entries[owner.id].extra
10069
                                    {
10070
                                        if blockRegions.entries[0].id == source.id {
10071
                                            for bindingNode in bindings {
10072
                                                let case ast::NodeValue::RegionBinding(binding) =
10073
                                                    bindingNode.value
10074
                                                    else panic "resolveReturn: invalid region binding";
10075
                                                let case ast::NodeValue::AddressOf(address) =
10076
                                                    binding.value.value else continue;
10077
                                                if let cellTy =
10078
                                                    try inferCellPayload(self, address.target)
10079
                                                {
10080
                                                    if let case Type::Cell {
10081
                                                        permission: controlled,
10082
                                                        ..
10083
                                                    } = cellTy; controlled <> nil {
10084
                                                        throw emitError(
10085
                                                            self,
10086
                                                            val,
10087
                                                            ErrorKind::RegionEscape(source.name),
10088
                                                        );
10089
                                                    }
10090
                                                }
10091
                                            }
10092
                                            break;
10093
                                        }
10094
                                    }
10095
                                }
10096
                            }
10097
                            set lexical = localScope.parent;
10098
                        }
10099
                        break;
10100
                    }
10101
                    set current = scope.parent;
10102
                }
10103
            }
10104
        }
10105
        let _ = try expectAssignable(self, expected, actualTy, val);
10106
        if isRefType(expected) and isMutablePointerLike(expected) and
10107
            isMutablePointerLike(actualTy)
10108
        {
10109
            if not try canMutateThrough(self, val) {
10110
                throw emitError(self, val, ErrorKind::ImmutableBinding);
10111
            }
10112
        }
10113
    } else if expected <> Type::Void {
10114
        throw emitTypeMismatch(self, node, TypeMismatch { expected, actual: Type::Void });
10115
    }
10116
    // In throwing functions, return values are wrapped in the success variant.
10117
    if f.throwList.len > 0 {
10118
        setNodeCoercion(self, node, Coercion::ResultWrap);
10119
    }
10120
    return setNodeType(self, node, Type::Never);
10121
}
10122
10123
/// Convert a [`ConstInt`] to its two's-complement bit pattern.
10124
fn constIntToBits(c: ConstInt) -> u64 {
10125
    return (0 - c.magnitude) if c.negative else c.magnitude;
10126
}
10127
10128
/// Convert a [`ConstInt`] to its signed two's-complement representation.
10129
fn constIntToSigned(c: ConstInt) -> i64 {
10130
    return constIntToBits(c) as i64;
10131
}
10132
10133
/// Build a [`ConstInt`] from a signed result, preserving bit width and signedness.
10134
fn constIntFromSigned(value: i64, bits: u8, signed: bool) -> ConstInt {
10135
    if value < 0 {
10136
        // Compute magnitude without signed overflow.
10137
        let uval = value as u64;
10138
        return ConstInt {
10139
            magnitude: 0 - uval,
10140
            bits,
10141
            signed,
10142
            negative: true,
10143
        };
10144
    }
10145
    return ConstInt {
10146
        magnitude: value as u64,
10147
        bits,
10148
        signed,
10149
        negative: false,
10150
    };
10151
}
10152
10153
/// Build a [`ConstInt`] from a two's-complement bit pattern.
10154
fn constIntFromBits(raw: u64, bits: u8, signed: bool) -> ConstInt {
10155
    let mask = parser::U64_MAX if bits == 64 else parser::U64_MAX >> (64 - bits) as u64;
10156
    let truncated = raw & mask;
10157
10158
    if signed {
10159
        let signBit = (mask >> 1) + 1;
10160
        if (truncated & signBit) <> 0 {
10161
            return ConstInt {
10162
                magnitude: (0 - truncated) & mask,
10163
                bits,
10164
                signed,
10165
                negative: true,
10166
            };
10167
        }
10168
    }
10169
    return ConstInt { magnitude: truncated, bits, signed, negative: false };
10170
}
10171
10172
/// Try to fold a binary operation on two integer constants.
10173
/// Returns the resulting constant value if successful.
10174
fn foldIntBinOp(op: ast::BinaryOp, left: ConstInt, right: ConstInt) -> ?ConstValue {
10175
    // Use the wider bit width and propagate signedness.
10176
    let mut bits = left.bits;
10177
    if right.bits > bits {
10178
        set bits = right.bits;
10179
    }
10180
    let signed = left.signed or right.signed;
10181
    let l = constIntToSigned(left);
10182
    let r = constIntToSigned(right);
10183
10184
    match op {
10185
        // Shift counts are masked to the left operand's width, matching
10186
        // the runtime word instructions.
10187
        case ast::BinaryOp::Shl => {
10188
            let raw = constIntToBits(left);
10189
            let shamt = constIntToBits(right) % left.bits as u64;
10190
            return ConstValue::Int(constIntFromBits(raw << shamt, left.bits, left.signed));
10191
        },
10192
        case ast::BinaryOp::Shr => {
10193
            let shamt = constIntToBits(right) % left.bits as u64;
10194
            if left.signed {
10195
                let shifted = constIntToSigned(left) >> shamt as i64;
10196
                return ConstValue::Int(
10197
                    constIntFromBits(shifted as u64, left.bits, true)
10198
                );
10199
            }
10200
            return ConstValue::Int(
10201
                constIntFromBits(left.magnitude >> shamt, left.bits, false)
10202
            );
10203
        },
10204
        case ast::BinaryOp::Eq  => return ConstValue::Bool(l == r),
10205
        case ast::BinaryOp::Ne  => return ConstValue::Bool(l <> r),
10206
        case ast::BinaryOp::Lt =>
10207
            return ConstValue::Bool(l < r if signed else left.magnitude < right.magnitude),
10208
        case ast::BinaryOp::Gt =>
10209
            return ConstValue::Bool(l > r if signed else left.magnitude > right.magnitude),
10210
        case ast::BinaryOp::Lte =>
10211
            return ConstValue::Bool(l <= r if signed else left.magnitude <= right.magnitude),
10212
        case ast::BinaryOp::Gte =>
10213
            return ConstValue::Bool(l >= r if signed else left.magnitude >= right.magnitude),
10214
        case ast::BinaryOp::Add => return ConstValue::Int(constIntFromSigned(l + r, bits, signed)),
10215
        case ast::BinaryOp::Sub => return ConstValue::Int(constIntFromSigned(l - r, bits, signed)),
10216
        case ast::BinaryOp::Mul => return ConstValue::Int(constIntFromSigned(l * r, bits, signed)),
10217
        case ast::BinaryOp::Div => {
10218
            if signed {
10219
                if r == 0 {
10220
                    return nil;
10221
                }
10222
                return ConstValue::Int(constIntFromSigned(l / r, bits, true));
10223
            }
10224
            if right.magnitude == 0 {
10225
                return nil;
10226
            }
10227
            return constInt(left.magnitude / right.magnitude, bits, false, false);
10228
        },
10229
        case ast::BinaryOp::Mod => {
10230
            if signed {
10231
                if r == 0 {
10232
                    return nil;
10233
                }
10234
                return ConstValue::Int(constIntFromSigned(l % r, bits, true));
10235
            }
10236
            if right.magnitude == 0 {
10237
                return nil;
10238
            }
10239
            return constInt(left.magnitude % right.magnitude, bits, false, false);
10240
        },
10241
        case ast::BinaryOp::BitAnd => return ConstValue::Int(constIntFromSigned(l & r, bits, signed)),
10242
        case ast::BinaryOp::BitOr  => return ConstValue::Int(constIntFromSigned(l | r, bits, signed)),
10243
        case ast::BinaryOp::BitXor => return ConstValue::Int(constIntFromSigned(l ^ r, bits, signed)),
10244
        else => return nil,
10245
    }
10246
}
10247
10248
/// Try to constant-fold a binary operation on two resolved operands.
10249
/// Only folds when the result type is concrete.
10250
fn tryFoldBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp, resultTy: Type) {
10251
    let leftVal = constValueEntry(self, binop.left)
10252
        else return;
10253
    let rightVal = constValueEntry(self, binop.right)
10254
        else return;
10255
10256
    // Fold integer binary ops.
10257
    if let case ConstValue::Int(leftInt) = leftVal {
10258
        if let case ConstValue::Int(rightInt) = rightVal {
10259
            if let result = foldIntBinOp(binop.op, leftInt, rightInt) {
10260
                setNodeConstValue(self, node, result);
10261
            }
10262
            return;
10263
        }
10264
    }
10265
10266
    // Fold boolean binary ops.
10267
    if let case ConstValue::Bool(l) = leftVal {
10268
        if let case ConstValue::Bool(r) = rightVal {
10269
            match binop.op {
10270
                case ast::BinaryOp::And => setNodeConstValue(self, node, ConstValue::Bool(l and r)),
10271
                case ast::BinaryOp::Or => setNodeConstValue(self, node, ConstValue::Bool(l or r)),
10272
                case ast::BinaryOp::Eq => setNodeConstValue(self, node, ConstValue::Bool(l == r)),
10273
                case ast::BinaryOp::Ne,
10274
                     ast::BinaryOp::Xor => setNodeConstValue(self, node, ConstValue::Bool(l <> r)),
10275
                else => {}
10276
            }
10277
        }
10278
    }
10279
}
10280
10281
/// Analyze a binary expression.
10282
unsafe fn resolveBinOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, binop: ast::BinOp) -> Type
10283
    throws (ResolveError)
10284
{
10285
    let mut resultTy = Type::Unknown;
10286
10287
    match binop.op {
10288
        case ast::BinaryOp::And,
10289
             ast::BinaryOp::Or,
10290
             ast::BinaryOp::Xor =>
10291
        {
10292
            try checkBoolean(self, binop.left);
10293
            try checkBoolean(self, binop.right);
10294
10295
            set resultTy = Type::Bool;
10296
        },
10297
        case ast::BinaryOp::Eq,
10298
             ast::BinaryOp::Ne =>
10299
        {
10300
            let leftTy = try infer(self, binop.left);
10301
            let rightTy = try visit(self, binop.right, leftTy);
10302
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
10303
                try requireUnsafe(self, node);
10304
            }
10305
10306
            if not isComparable(leftTy, rightTy) {
10307
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
10308
                    expected: leftTy,
10309
                    actual: rightTy,
10310
                });
10311
            }
10312
            // When comparing `T == ?T`, record a coercion on the
10313
            // non-optional side so the lowerer lifts it before comparing.
10314
            // We use the already-optional type from the other side rather than
10315
            // constructing a new optional, so that e.g. `?u8 == 42` coerces
10316
            // `42` to `?u8` (not `?i32`). We also record OptionalLift directly
10317
            // rather than using expectAssignable, because comparisons should
10318
            // allow e.g. `?*mut T == *T` where mutability differs.
10319
            if let case Type::Optional(_) = leftTy {
10320
                if not isOptionalType(rightTy) {
10321
                    setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy));
10322
                }
10323
            } else if let case Type::Optional(_) = rightTy {
10324
                setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy));
10325
            }
10326
            set resultTy = Type::Bool;
10327
        },
10328
        else => {
10329
            // Check for pointer arithmetic before numeric check.
10330
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
10331
                let leftTy = try infer(self, binop.left);
10332
                let rightTy = try visit(self, binop.right, leftTy);
10333
10334
                // Allow arithmetic on owning pointers and unsafe pointers, but
10335
                // never on references.
10336
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
10337
                    if *leftTarget == Type::Opaque {
10338
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
10339
                    }
10340
                    if not types::isReference(leftClass)
10341
                        and isNumericType(rightTy)
10342
                    {
10343
                        try requireUnsafe(self, node);
10344
                        return setNodeType(self, node, leftTy);
10345
                    }
10346
                }
10347
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
10348
                    if *rightTarget == Type::Opaque {
10349
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
10350
                    }
10351
                    if binop.op == ast::BinaryOp::Add
10352
                        and not types::isReference(rightClass)
10353
                        and isNumericType(leftTy)
10354
                    {
10355
                        try requireUnsafe(self, node);
10356
                        return setNodeType(self, node, rightTy);
10357
                    }
10358
                }
10359
            }
10360
            let leftTy = try checkNumeric(self, binop.left);
10361
            let rightTy = try checkNumeric(self, binop.right);
10362
10363
            let mut operandTy = leftTy;
10364
            if leftTy <> rightTy {
10365
                if leftTy == Type::Int {
10366
                    set operandTy = rightTy;
10367
                } else if rightTy <> Type::Int {
10368
                    throw emitTypeMismatch(self, binop.right, TypeMismatch {
10369
                        expected: leftTy,
10370
                        actual: rightTy,
10371
                    });
10372
                }
10373
            }
10374
10375
            // Ordering comparisons return `bool`, not the operand type.
10376
            match binop.op {
10377
                case ast::BinaryOp::Lt, ast::BinaryOp::Gt,
10378
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
10379
                    set resultTy = Type::Bool,
10380
                else =>
10381
                    set resultTy = operandTy,
10382
            }
10383
10384
        }
10385
    };
10386
    // Try constant folding after both operands are resolved.
10387
    tryFoldBinOp(self, node, binop, resultTy);
10388
10389
    return setNodeType(self, node, resultTy);
10390
}
10391
10392
/// Analyze a unary expression.
10393
unsafe fn resolveUnOp 'arena (self: &mut Resolver 'arena, node: *ast::Node, unop: ast::UnOp) -> Type
10394
    throws (ResolveError)
10395
{
10396
    let mut resultTy = Type::Unknown;
10397
10398
    match unop.op {
10399
        case ast::UnaryOp::Not => {
10400
            set resultTy = try checkBoolean(self, unop.value);
10401
            if let value = constValueEntry(self, unop.value) {
10402
                if let case ConstValue::Bool(val) = value {
10403
                    setNodeConstValue(self, node, ConstValue::Bool(not val));
10404
                }
10405
            }
10406
        },
10407
        case ast::UnaryOp::Neg => {
10408
            // TODO: Check that we're allowed to use `-` here? Should negation
10409
            // only be valid for signed integers?
10410
            set resultTy = try checkNumeric(self, unop.value);
10411
            if let value = constValueEntry(self, unop.value) {
10412
                // Get the constant expression for the value, flip the sign,
10413
                // and store that new expression on the unary op node.
10414
                if let case ConstValue::Int(intVal) = value {
10415
                    setNodeConstValue(
10416
                        self,
10417
                        node,
10418
                        constInt(intVal.magnitude, intVal.bits, true, not intVal.negative)
10419
                    );
10420
                }
10421
            }
10422
        },
10423
        case ast::UnaryOp::BitNot => {
10424
            set resultTy = try checkNumeric(self, unop.value);
10425
            if let value = constValueEntry(self, unop.value) {
10426
                if let case ConstValue::Int(intVal) = value {
10427
                    let signed = constIntToSigned(intVal);
10428
                    let inverted = constIntFromSigned(-(signed + 1), intVal.bits, intVal.signed);
10429
                    setNodeConstValue(self, node, ConstValue::Int(inverted));
10430
                }
10431
            }
10432
        },
10433
    };
10434
    return setNodeType(self, node, resultTy);
10435
}
10436
10437
/// Resolve a type signature node and set its type.
10438
unsafe fn inferTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
10439
    throws (ResolveError)
10440
{
10441
    let resolved = try resolveTypeSig(self, node, sig);
10442
10443
    return setNodeType(self, node, resolved);
10444
}
10445
10446
/// Convert a parsed pointer qualifier to its semantic class.
10447
fn resolvePointerClass(class: ast::PointerClass) -> types::PointerClass {
10448
    match class {
10449
        case ast::PointerClass::Owned => return types::PointerClass::Owned,
10450
        case ast::PointerClass::Ref => return types::PointerClass::Ref,
10451
        case ast::PointerClass::Unsafe => return types::PointerClass::Unsafe,
10452
    }
10453
}
10454
10455
/// Convert a type signature node into a type value.
10456
unsafe fn resolveTypeSig 'arena (self: &mut Resolver 'arena, node: *ast::Node, sig: ast::TypeSig) -> Type
10457
    throws (ResolveError)
10458
{
10459
    match sig {
10460
        case ast::TypeSig::Cell { class, permission, payload } => {
10461
            let inner = try infer(self, payload);
10462
            try ensureStorableType(self, node, inner);
10463
            let mut permissionRegion: ?*unsafe types::Region = nil;
10464
            if let permissionNode = permission {
10465
                set permissionRegion = try resolveRegion(self, permissionNode);
10466
            }
10467
            let allocator = alloc::arenaAllocator(self.arena);
10468
            self.cellChecks.append(CellCheck {
10469
                node,
10470
                payload: inner,
10471
                permission: permissionRegion,
10472
                regions: self.regionScope,
10473
                moduleId: self.currentMod,
10474
            }, allocator);
10475
            return Type::Cell {
10476
                class: resolvePointerClass(class),
10477
                permission: permissionRegion,
10478
                payload: allocType(self, inner),
10479
            };
10480
        }
10481
        case ast::TypeSig::RegionRef { region, type } => {
10482
            let identity = try resolveRegion(self, region);
10483
            let base = try infer(self, type);
10484
            match base {
10485
                case Type::Cell { permission, payload, .. } =>
10486
                    return Type::Cell {
10487
                        class: types::PointerClass::Region(identity), permission, payload,
10488
                    },
10489
                case Type::Pointer { target, mutable, .. } =>
10490
                    return Type::Pointer { class: types::PointerClass::Region(identity), target, mutable },
10491
                case Type::Slice { item, mutable, .. } =>
10492
                    return Type::Slice { class: types::PointerClass::Region(identity), item, mutable },
10493
                case Type::TraitObject { traitInfo, mutable, .. } =>
10494
                    return Type::TraitObject { class: types::PointerClass::Region(identity), traitInfo, mutable },
10495
                else => throw emitError(self, node, ErrorKind::InvalidRefPosition),
10496
            }
10497
        }
10498
        case ast::TypeSig::Applied { name, regions } => {
10499
            if let case ast::NodeValue::Ident(spelling) = name.value;
10500
                mem::eq(spelling, "Session") and findTypeSymbol(self.scope, spelling) == nil
10501
            {
10502
                if regions.len <> 1 {
10503
                    throw emitError(self, node, ErrorKind::RegionArgumentCount(CountMismatch {
10504
                        expected: 1, actual: regions.len,
10505
                    }));
10506
                }
10507
                return Type::Session(try resolveRegion(self, regions[0]));
10508
            }
10509
            let base = try resolveTypeName(self, name);
10510
            return Type::Nominal(try applyNominalRegions(self, base, regions, node));
10511
        }
10512
        case ast::TypeSig::Void => {
10513
            return Type::Void;
10514
        }
10515
        case ast::TypeSig::Never => {
10516
            return Type::Never;
10517
        }
10518
        case ast::TypeSig::Opaque => {
10519
            return Type::Opaque;
10520
        }
10521
        case ast::TypeSig::Bool => {
10522
            return Type::Bool;
10523
        }
10524
        case ast::TypeSig::Integer { width, sign } => {
10525
            let u = sign == ast::Signedness::Unsigned;
10526
            match width {
10527
                case 1 => return Type::U8 if u else Type::I8,
10528
                case 2 => return Type::U16 if u else Type::I16,
10529
                case 4 => return Type::U32 if u else Type::I32,
10530
                case 8 => return Type::U64 if u else Type::I64,
10531
                else => {
10532
                    panic "resolveTypeSig: invalid integer width";
10533
                }
10534
            }
10535
        }
10536
        case ast::TypeSig::Array { itemType, length } => {
10537
            let item = try infer(self, itemType);
10538
            let length = try checkSizeInt(self, length);
10539
10540
            return Type::Array(ArrayType { item: allocType(self, item), length });
10541
        }
10542
        case ast::TypeSig::Slice { class, itemType, mutable } => {
10543
            let item = try infer(self, itemType);
10544
            return Type::Slice {
10545
                class: resolvePointerClass(class),
10546
                item: allocType(self, item),
10547
                mutable,
10548
            };
10549
        }
10550
        case ast::TypeSig::Pointer { class, valueType, mutable } => {
10551
            let target = try infer(self, valueType);
10552
            return Type::Pointer {
10553
                class: resolvePointerClass(class),
10554
                target: allocType(self, target),
10555
                mutable,
10556
            };
10557
        }
10558
        case ast::TypeSig::Optional { valueType } => {
10559
            let payload = try infer(self, valueType);
10560
            return Type::Optional(allocType(self, payload));
10561
        }
10562
        case ast::TypeSig::Nominal(name) => {
10563
            let ty = try resolveTypeName(self, name);
10564
            try requireNominalArguments(self, ty, node);
10565
            return Type::Nominal(ty);
10566
        }
10567
        case ast::TypeSig::Record { fields, labeled } => {
10568
            let mut recordType = try resolveRecordFields(self, node, fields, labeled);
10569
            set recordType.declaredCopy = true;
10570
            for field in recordType.fields {
10571
                if not isCopy(field.fieldType) {
10572
                    set recordType.declaredCopy = false;
10573
                }
10574
            }
10575
            set recordType.regions = self.regionScope;
10576
            let nominalTy = allocNominalType(self, NominalType::Record(recordType));
10577
            if let scope = self.regionScope {
10578
                let map = regionSubstitution(self, scope);
10579
                for parameter, i in scope.entries {
10580
                    set map.arguments[i] = parameter;
10581
                }
10582
                return Type::Nominal(internNominalApplication(self, nominalTy, &map));
10583
            }
10584
            return Type::Nominal(nominalTy);
10585
        }
10586
        case ast::TypeSig::Fn { sig: t, isUnsafe } => {
10587
            let a = alloc::arenaAllocator(self.arena);
10588
            let mut paramTypes: *mut [*Type] = &mut [];
10589
            let mut throwList: *mut [*Type] = &mut [];
10590
10591
            if t.params.len > MAX_FN_PARAMS {
10592
                throw emitError(self, node, ErrorKind::FnParamOverflow(CountMismatch {
10593
                    expected: MAX_FN_PARAMS,
10594
                    actual: t.params.len,
10595
                }));
10596
            }
10597
            if t.throwList.len > MAX_FN_THROWS {
10598
                throw emitError(self, node, ErrorKind::FnThrowOverflow(CountMismatch {
10599
                    expected: MAX_FN_THROWS,
10600
                    actual: t.throwList.len,
10601
                }));
10602
            }
10603
10604
            for paramNode in t.params {
10605
                let paramTy = try resolveValueType(self, paramNode);
10606
                paramTypes.append(allocType(self, paramTy), a);
10607
            }
10608
            for tyNode in t.throwList {
10609
                let throwTy = try resolveValueType(self, tyNode);
10610
                try ensureStorableType(self, tyNode, throwTy);
10611
                try validateErrorTag(self, tyNode, throwTy, &throwList[..]);
10612
                throwList.append(allocType(self, throwTy), a);
10613
            }
10614
            let mut retType = allocType(self, Type::Void);
10615
            if let ret = t.returnType {
10616
                let resolvedRet = try resolveValueType(self, ret);
10617
                try ensureStorableType(self, ret, resolvedRet);
10618
                set retType = allocType(self, resolvedRet);
10619
            }
10620
            let fnType = FnType {
10621
                regions: nil,
10622
                paramTypes: &paramTypes[..],
10623
                returnType: retType,
10624
                throwList: &throwList[..],
10625
                isUnsafe,
10626
            };
10627
            return Type::Fn(allocFnType(self, fnType));
10628
        }
10629
        // Resolve an opaque trait object signature.
10630
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
10631
            let sym = try resolveNamePath(self, traitName);
10632
            let case SymbolData::Trait(traitInfo) = sym.data
10633
                else throw emitError(self, traitName, ErrorKind::Internal);
10634
            setNodeSymbol(self, traitName, sym);
10635
10636
            return Type::TraitObject { class: resolvePointerClass(class), traitInfo, mutable };
10637
        }
10638
    }
10639
}
10640
10641
/// Check if a type can be used for inferrence.
10642
fn isTypeInferrable(type: Type) -> bool {
10643
    if let case Type::Pointer { target, .. } = type {
10644
        return isTypeInferrable(*target);
10645
    }
10646
    match type {
10647
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
10648
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
10649
        case Type::Optional(opt) => return isTypeInferrable(*opt),
10650
        else => return true,
10651
    }
10652
}
10653
10654
/// Analyze a standalone expression by wrapping it in a synthetic function.
10655
export unsafe fn resolveExpr 'arena (
10656
    self: &mut Resolver 'arena, expr: *ast::Node, arena: &mut ast::NodeArena
10657
) -> Diagnostics throws (ResolveError) {
10658
    let a = alloc::arenaAllocator(&mut arena.arena);
10659
    let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr));
10660
    let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a);
10661
    let module = ast::synthFnModule(arena, ANALYZE_EXPR_FN_NAME, bodyStmts);
10662
10663
    let case ast::NodeValue::Block(block) = module.modBody.value
10664
        else panic "resolveExpr: expected block for module body";
10665
    enterScope(self, module.modBody);
10666
    try resolveModuleDecls(self, &block) catch {
10667
        return diagnostics(self);
10668
    };
10669
    try resolveModuleDefs(self, &block) catch {
10670
        return diagnostics(self);
10671
    };
10672
    exitScope(self);
10673
10674
    return diagnostics(self);
10675
}
10676
10677
/// Analyze a parsed module root, ie. a block of top-level statements.
10678
export unsafe fn resolveModuleRoot 'arena (self: &mut Resolver 'arena, root: *ast::Node) -> Diagnostics throws (ResolveError) {
10679
    let case ast::NodeValue::Block(block) = root.value
10680
        else panic "resolveModuleRoot: expected block for module root";
10681
10682
    enterScope(self, root);
10683
    try resolveModuleDecls(self, &block) catch {
10684
        return diagnostics(self);
10685
    };
10686
    try resolveModuleDefs(self, &block) catch {
10687
        return diagnostics(self);
10688
    };
10689
    exitScope(self);
10690
    setNodeType(self, root, Type::Void);
10691
10692
    return diagnostics(self);
10693
}
10694
10695
/// Analyze the module graph. This pass processes `mod` statements, creating symbols
10696
/// and scopes for them, and also binds type names in each module so that cross-module
10697
/// type references work regardless of declaration order.
10698
unsafe fn resolveModuleGraph 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
10699
    try bindTypeNames(self, block);
10700
10701
    for node in block.statements {
10702
        if let case ast::NodeValue::Mod(decl) = node.value {
10703
            try resolveModGraph(self, node, decl);
10704
        }
10705
    }
10706
}
10707
10708
/// Bind all type names in a module.
10709
/// Skips declarations that have already been bound.
10710
unsafe fn bindTypeNames 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
10711
    for node in block.statements {
10712
        match node.value {
10713
            case ast::NodeValue::RecordDecl(decl) => {
10714
                if symbolFor(self, node) == nil {
10715
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
10716
                }
10717
            }
10718
            case ast::NodeValue::UnionDecl(decl) => {
10719
                if symbolFor(self, node) == nil {
10720
                    try bindTypeName(self, node, decl.name, decl.attrs) catch {};
10721
                }
10722
            }
10723
            case ast::NodeValue::TraitDecl { name, attrs, .. } => {
10724
                if symbolFor(self, node) == nil {
10725
                    try bindTraitName(self, node, name, attrs) catch {};
10726
                }
10727
            }
10728
            else => {}
10729
        }
10730
    }
10731
}
10732
10733
/// Resolve all type bodies in a module.
10734
unsafe fn resolveTypeBodies 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
10735
    for node in block.statements {
10736
        match node.value {
10737
            case ast::NodeValue::RecordDecl(decl) => {
10738
                try resolveRecordBody(self, node, decl) catch {
10739
                    // Continue resolving other types even if one fails.
10740
                };
10741
            }
10742
            case ast::NodeValue::UnionDecl(decl) => {
10743
                try resolveUnionBody(self, node, decl) catch {
10744
                    // Continue resolving other types even if one fails.
10745
                };
10746
            }
10747
            case ast::NodeValue::TraitDecl { supertraits, methods, .. } => {
10748
                try resolveTraitBody(self, node, supertraits, methods) catch {
10749
                    // Continue resolving other types even if one fails.
10750
                };
10751
            }
10752
            else => {
10753
                // Ignore other declarations.
10754
            }
10755
        }
10756
    }
10757
}
10758
10759
/// Analyze module declarations. This pass processes all top-level statements. When it hits
10760
/// a `mod` statement, it recurses inside the module, analyzing its statements. Module import
10761
/// statements (`use`) are processed here, and make use of the module graph established in the
10762
/// previous pass.
10763
///
10764
/// This function uses a two-phase approach:
10765
/// Phase 1: Bind all type names to allow forward references and mutual recursion.
10766
/// Phase 2: Resolve type bodies, ie. field types, variant types, etc.
10767
unsafe fn resolveModuleDecls 'arena (res: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
10768
    // Phase 1: Bind all type names as placeholders.
10769
    try bindTypeNames(res, block);
10770
    // Phase 2: Process imports so names available from the module graph can
10771
    // be used in function signatures.
10772
    for node in block.statements {
10773
        if let case ast::NodeValue::Use(decl) = node.value {
10774
            try resolveUse(res, node, decl);
10775
        }
10776
    }
10777
    // Phase 3: Bind function signatures so that function references are
10778
    // available in constant and static initializers.
10779
    for node in block.statements {
10780
        if let case ast::NodeValue::FnDecl(decl) = node.value {
10781
            try resolveFnDecl(res, node, decl);
10782
        }
10783
    }
10784
    // Phase 4: Process constants before submodules, so that child modules
10785
    // can reference parent constants via `super::`.
10786
    for node in block.statements {
10787
        if let case ast::NodeValue::ConstDecl(_) = node.value {
10788
            try infer(res, node);
10789
        }
10790
    }
10791
    // Phase 5: Process submodule declarations -- recurses into child modules.
10792
    // Child modules may trigger on-demand type resolution via
10793
    // [`ensureNominalResolved`] which switches to the declaring module's
10794
    // scope.
10795
    for node in block.statements {
10796
        if let case ast::NodeValue::Mod(decl) = node.value {
10797
            try resolveModDecl(res, node, decl);
10798
        }
10799
    }
10800
    // Phase 5b: Process wildcard imports after submodules are resolved,
10801
    // so that transitive re-exports (export use foo::*) are visible.
10802
    for node in block.statements {
10803
        if let case ast::NodeValue::Use(decl) = node.value {
10804
            if decl.wildcard {
10805
                try resolveUse(res, node, decl);
10806
            }
10807
        }
10808
    }
10809
    // Phase 6: Resolve type bodies (record fields, union variants).
10810
    try resolveTypeBodies(res, block);
10811
    // Phase 7: Process all other declarations (statics, etc.).
10812
    for stmt in block.statements {
10813
        try visitDecl(res, stmt);
10814
    }
10815
}
10816
10817
/// Create a place with no storage root or field projections.
10818
fn emptyBorrowPlace() -> BorrowPlace {
10819
    return BorrowPlace { root: nil, fields: [0; MAX_BORROW_FIELDS], len: 0, precise: true };
10820
}
10821
10822
/// Create initialized loan and loop scratch state for one function.
10823
fn linearChecker 'arena 'checking (
10824
    resolver: &'checking mut Resolver 'arena, regions: ?*RegionScope
10825
) -> LinearChecker 'arena 'checking where 'arena: 'checking {
10826
    let place = emptyBorrowPlace();
10827
    return LinearChecker 'arena 'checking {
10828
        resolver,
10829
        regional: [nil; MAX_REGIONAL_LOANS],
10830
        regionalLen: 0,
10831
        regions,
10832
        loopBackLoans: [0; MAX_LINEAR_LOOP_DEPTH],
10833
        loopExitLoans: [0; MAX_LINEAR_LOOP_DEPTH],
10834
        loopRegions: [nil; MAX_LINEAR_LOOP_DEPTH],
10835
        loans: [place; MAX_LINEAR_BINDINGS],
10836
        loanLen: 0,
10837
        locals: [LocalLoan {
10838
            binding: nil, place, exclusive: false, permission: nil, storage: nil,
10839
        }; MAX_LINEAR_BINDINGS],
10840
        localLen: 0,
10841
        authorityPermissions: [nil; MAX_LINEAR_BINDINGS],
10842
        authorityBindings: [nil; MAX_LINEAR_BINDINGS],
10843
        authorityExclusive: [false; MAX_LINEAR_BINDINGS],
10844
        authorityLen: 0,
10845
        payloadAddress: nil,
10846
        witnessPermission: nil,
10847
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
10848
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
10849
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
10850
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
10851
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
10852
        loopDepth: 0,
10853
    };
10854
}
10855
10856
/// Create an empty ownership environment with initialized binding slots.
10857
fn linearEnv() -> LinearEnv {
10858
    return LinearEnv {
10859
        regionalLoans: 0,
10860
        symbols: [nil; MAX_LINEAR_BINDINGS],
10861
        available: 0,
10862
        len: 0,
10863
        terminated: false,
10864
    };
10865
}
10866
10867
/// Read initialized binding metadata from the active prefix.
10868
fn linearSymbol(env: &LinearEnv, index: u32) -> TrackedSymbol {
10869
    assert index < env.len, "linearSymbol: binding index outside active prefix";
10870
    let symbol = env.symbols[index] else panic "linearSymbol: missing active binding";
10871
    return symbol;
10872
}
10873
10874
/// Find a tracked binding by symbol identity.
10875
fn findLinearBinding(env: &LinearEnv, symbolId: u32) -> ?u32 {
10876
    for i in 0..env.len {
10877
        if linearSymbol(env, i).id == symbolId {
10878
            return i;
10879
        }
10880
    }
10881
    return nil;
10882
}
10883
10884
/// Return whether a tracked binding is still available.
10885
fn linearBindingAvailable(env: &LinearEnv, index: u32) -> bool {
10886
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
10887
}
10888
10889
/// Find the newest live authority for a cell permission.
10890
unsafe fn findCellAuthority 'arena 'checking (
10891
    checker: &LinearChecker 'arena 'checking,
10892
    env: &LinearEnv,
10893
    permission: *unsafe types::Region,
10894
) -> ?u32 where 'arena: 'checking {
10895
    let mut i = checker.authorityLen;
10896
    while i > 0 {
10897
        set i -= 1;
10898
        let candidate = checker.authorityPermissions[i] else continue;
10899
        if candidate.id <> permission.id {
10900
            continue;
10901
        }
10902
        let binding = checker.authorityBindings[i]
10903
            else panic "findCellAuthority: missing authority binding";
10904
        if let bindingIndex = findLinearBinding(env, binding.id);
10905
            not linearBindingAvailable(env, bindingIndex)
10906
        {
10907
            continue;
10908
        }
10909
        return i;
10910
    }
10911
    return nil;
10912
}
10913
10914
/// Add a local binding when its resolved type moves by value.
10915
unsafe fn addLinearBinding 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
10916
    throws (ResolveError) where 'arena: 'checking
10917
{
10918
    let sym = symbolFor(checker.resolver, node) else return;
10919
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
10920
    if not isMoveOnly(ty) {
10921
        return;
10922
    }
10923
    if env.len >= MAX_LINEAR_BINDINGS {
10924
        throw emitError(checker.resolver, node, ErrorKind::Internal);
10925
    }
10926
    let usage = BindingUse::Linear if isLinear(ty) else BindingUse::Affine;
10927
    set env.symbols[env.len] = TrackedSymbol { id: sym.id, name: sym.name, node: sym.node, usage };
10928
    set env.available |= (1 as u64) << (env.len as u64);
10929
    set env.len += 1;
10930
}
10931
10932
/// Mark a tracked binding as uninitialized.
10933
fn markLinearBindingUnavailable 'arena (self: &mut Resolver 'arena, env: &mut LinearEnv, node: *ast::Node) {
10934
    let binding = self.nodeData.entries[node.id].binding else return;
10935
    let index = findLinearBinding(env, binding.id) else return;
10936
    set env.available &= ~((1 as u64) << (index as u64));
10937
}
10938
10939
/// Require exact-use bindings introduced after `start` to be consumed.
10940
fn finishLinearScope 'arena 'checking (
10941
    checker: &mut LinearChecker 'arena 'checking,
10942
    env: &mut LinearEnv,
10943
    start: u32,
10944
) throws (ResolveError) where 'arena: 'checking {
10945
    if not env.terminated {
10946
        for i in start..env.len {
10947
            if linearBindingAvailable(env, i) {
10948
                let sym = linearSymbol(env, i);
10949
                if sym.usage == BindingUse::Linear {
10950
                    throw emitError(
10951
                        checker.resolver,
10952
                        sym.node,
10953
                        ErrorKind::LinearNotConsumed(sym.name),
10954
                    );
10955
                }
10956
            }
10957
        }
10958
    }
10959
    set env.len = start;
10960
}
10961
10962
/// Require a tracked identifier to remain available for any access.
10963
fn checkLinearIdent 'arena 'checking (
10964
    checker: &mut LinearChecker 'arena 'checking,
10965
    env: &mut LinearEnv,
10966
    node: *ast::Node,
10967
) throws (ResolveError) where 'arena: 'checking {
10968
    let binding = checker.resolver.nodeData.entries[node.id].binding else return;
10969
    let index = findLinearBinding(env, binding.id) else return;
10970
    if not linearBindingAvailable(env, index) {
10971
        let sym = linearSymbol(env, index);
10972
        let kind = ErrorKind::LinearUseAfterConsume(sym.name) if sym.usage == BindingUse::Linear
10973
            else ErrorKind::AffineUseAfterMove(sym.name);
10974
        throw emitError(checker.resolver, node, kind);
10975
    }
10976
}
10977
10978
/// Move or consume a tracked identifier once.
10979
fn consumeLinearIdent 'arena 'checking (
10980
    checker: &mut LinearChecker 'arena 'checking,
10981
    env: &mut LinearEnv,
10982
    node: *ast::Node,
10983
) throws (ResolveError) where 'arena: 'checking {
10984
    try checkLinearIdent(checker, env, node);
10985
    let binding = checker.resolver.nodeData.entries[node.id].binding else return;
10986
    let index = findLinearBinding(env, binding.id) else return;
10987
    set env.available &= ~((1 as u64) << (index as u64));
10988
}
10989
10990
/// Merge ownership availability across two live branches.
10991
/// Validate both inputs before writing to an output that can alias either input.
10992
fn joinLinearBranches 'arena 'checking (
10993
    checker: &mut LinearChecker 'arena 'checking,
10994
    env: &mut LinearEnv,
10995
    left: &LinearEnv,
10996
    right: &LinearEnv,
10997
    node: *ast::Node,
10998
) throws (ResolveError) where 'arena: 'checking {
10999
    if left.terminated and right.terminated {
11000
        set *env = *left;
11001
        set env.terminated = true;
11002
        return;
11003
    }
11004
    if left.terminated {
11005
        set *env = *right;
11006
        return;
11007
    }
11008
    if right.terminated {
11009
        set *env = *left;
11010
        return;
11011
    }
11012
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
11013
    let mut available = left.available;
11014
    for i in 0..left.len {
11015
        if linearBindingAvailable(left, i) <> linearBindingAvailable(right, i) {
11016
            let sym = linearSymbol(left, i);
11017
            if sym.usage == BindingUse::Linear {
11018
                throw emitError(
11019
                    checker.resolver,
11020
                    node,
11021
                    ErrorKind::LinearBranchMismatch(sym.name),
11022
                );
11023
            }
11024
            set available &= ~((1 as u64) << (i as u64));
11025
        }
11026
    }
11027
    let regionalLoans = left.regionalLoans | right.regionalLoans;
11028
    set *env = *left;
11029
    set env.available = available;
11030
    set env.regionalLoans = regionalLoans;
11031
}
11032
11033
/// Require all available exact-use bindings to be consumed at a function exit.
11034
fn finishLinearExit 'arena 'checking (
11035
    checker: &mut LinearChecker 'arena 'checking,
11036
    env: &mut LinearEnv,
11037
) throws (ResolveError) where 'arena: 'checking {
11038
    if env.terminated {
11039
        return;
11040
    }
11041
    for i in 0..env.len {
11042
        if linearBindingAvailable(env, i) {
11043
            let sym = linearSymbol(env, i);
11044
            if sym.usage == BindingUse::Linear {
11045
                throw emitError(
11046
                    checker.resolver,
11047
                    sym.node,
11048
                    ErrorKind::LinearNotConsumed(sym.name),
11049
                );
11050
            }
11051
        }
11052
    }
11053
    set env.terminated = true;
11054
}
11055
11056
/// Find the local root borrowed or consumed by an argument expression.
11057
fn linearRootSymbol 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> ?*unsafe mut Symbol {
11058
    match node.value {
11059
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) =>
11060
            return symbolFor(self, node),
11061
        case ast::NodeValue::As(expr) => return linearRootSymbol(self, expr.value),
11062
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
11063
        case ast::NodeValue::FieldAccess(access) =>
11064
            return linearRootSymbol(self, access.parent),
11065
        case ast::NodeValue::Subscript { container, .. } =>
11066
            return linearRootSymbol(self, container),
11067
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
11068
        else => return nil,
11069
    }
11070
}
11071
11072
/// A projection loan that remains active until its named region ends.
11073
record RegionalLoan: Copy {
11074
    /// Address expression that supplies access to the loan.
11075
    source: *ast::Node,
11076
    /// Source identity of the projection's declared lifetime.
11077
    regionId: u32,
11078
    /// Storage protected by the projection.
11079
    place: BorrowPlace,
11080
    /// Whether accesses through other references are excluded.
11081
    exclusive: bool,
11082
}
11083
11084
/// Read an initialized regional loan from the active prefix.
11085
fn regionalLoan 'arena 'checking (
11086
    checker: &LinearChecker 'arena 'checking, index: u32
11087
) -> RegionalLoan where 'arena: 'checking {
11088
    assert index < checker.regionalLen, "regionalLoan: index outside active prefix";
11089
    let loan = checker.regional[index] else panic "regionalLoan: missing active loan";
11090
    return loan;
11091
}
11092
11093
/// Return whether a region identity is visible in a lexical environment.
11094
fn regionInScope(scope: ?*RegionScope, regionId: u32) -> bool {
11095
    let mut cursor = scope;
11096
    while let current = cursor {
11097
        if regionIndex(current, regionId) <> nil {
11098
            return true;
11099
        }
11100
        set cursor = current.parent;
11101
    }
11102
    return false;
11103
}
11104
11105
/// Retain only loans whose regions remain active at a control-flow destination.
11106
fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking {
11107
    let mut result: u64 = 0;
11108
    for i in 0..checker.regionalLen {
11109
        let bit = (1 as u64) << (i as u64);
11110
        if (mask & bit) <> 0 and regionInScope(scope, regionalLoan(checker, i).regionId) {
11111
            set result |= bit;
11112
        }
11113
    }
11114
    return result;
11115
}
11116
11117
/// Remap one loan mask after the regional loan table is compacted.
11118
fn remapRegionalLoans(mask: u64, mapping: &[u64]) -> u64 {
11119
    let mut result: u64 = 0;
11120
    for replacement, i in mapping {
11121
        if (mask & ((1 as u64) << (i as u64))) <> 0 {
11122
            set result |= replacement;
11123
        }
11124
    }
11125
    return result;
11126
}
11127
11128
/// Reclaim ended-region entries and preserve loans for enclosing regions.
11129
fn compactRegionalLoans 'arena 'checking (
11130
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
11131
    scope: ?*RegionScope
11132
) where 'arena: 'checking {
11133
    let oldLen = checker.regionalLen;
11134
    let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS];
11135
    let mut next: u32 = 0;
11136
    for i in 0..oldLen {
11137
        let loan = regionalLoan(checker, i);
11138
        if regionInScope(scope, loan.regionId) {
11139
            set checker.regional[next] = loan;
11140
            set mapping[i] = (1 as u64) << (next as u64);
11141
            set next += 1;
11142
        }
11143
    }
11144
    set env.regionalLoans = remapRegionalLoans(env.regionalLoans, &mapping[..oldLen]);
11145
    for i in 0..checker.loopDepth {
11146
        set checker.loopBackLoans[i] = remapRegionalLoans(checker.loopBackLoans[i], &mapping[..oldLen]);
11147
        set checker.loopExitLoans[i] = remapRegionalLoans(checker.loopExitLoans[i], &mapping[..oldLen]);
11148
    }
11149
    set checker.regionalLen = next;
11150
}
11151
11152
/// Check whether an access comes from the reference created by a projection.
11153
unsafe fn usesRegionalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, source: *ast::Node) -> bool {
11154
    if node.id == source.id {
11155
        return true;
11156
    }
11157
    let root = linearRootSymbol(self, node) else return false;
11158
    let origin = localReferenceSource(root) else return false;
11159
    return usesRegionalLoan(self, origin, source);
11160
}
11161
11162
/// Retain a full-region projection independently of its local binding scope.
11163
unsafe fn addRegionalLoan 'arena 'checking (
11164
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node, address: ast::AddressOf
11165
) throws (ResolveError) where 'arena: 'checking {
11166
    let ty = typeFor(checker.resolver, node) else return;
11167
    let mut class = types::PointerClass::Ref;
11168
    match ty {
11169
        case Type::Cell { class: cellClass, .. } => set class = cellClass,
11170
        case Type::Pointer { class: pointerClass, .. } => set class = pointerClass,
11171
        case Type::Slice { class: sliceClass, .. } => set class = sliceClass,
11172
        else => return,
11173
    }
11174
    let case types::PointerClass::Region(region) = class else return;
11175
    let storage = addressStorageClass(checker.resolver, address.target);
11176
    let case types::PointerClass::Region(parent) = storage else return;
11177
    if parent.id <> region.id {
11178
        return;
11179
    }
11180
    let place = borrowPlace(checker.resolver, address.target);
11181
    if place.root == nil {
11182
        return;
11183
    }
11184
    for i in 0..checker.regionalLen {
11185
        let loan = regionalLoan(checker, i);
11186
        if loan.source.id == node.id {
11187
            set env.regionalLoans |= (1 as u64) << (i as u64);
11188
            return;
11189
        }
11190
    }
11191
    if checker.regionalLen >= MAX_REGIONAL_LOANS {
11192
        throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow);
11193
    }
11194
    let index = checker.regionalLen;
11195
    set checker.regional[index] = RegionalLoan { source: node, regionId: region.id, place, exclusive: ast::isExclusiveAddress(address) };
11196
    set checker.regionalLen += 1;
11197
    set env.regionalLoans |= (1 as u64) << (index as u64);
11198
}
11199
11200
/// Return whether an initializer copies a shared reference with a named region.
11201
/// Address expressions and casts retain a loan on their source storage.
11202
fn copiesRegionalReference(ty: Type, value: *ast::Node) -> bool {
11203
    if referenceRegion(ty) == nil or isMutablePointerLike(ty) {
11204
        return false;
11205
    }
11206
    match value.value {
11207
        case ast::NodeValue::AddressOf(_), ast::NodeValue::As(_) => return false,
11208
        else => return true,
11209
    }
11210
}
11211
11212
/// Return the initializer that supplies a local reference's storage.
11213
fn localReferenceSource(sym: &Symbol) -> ?*ast::Node {
11214
    let case SymbolData::Value { type: ty, .. } = sym.data else return nil;
11215
    if let case Type::Session(_) = ty {
11216
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
11217
            return binding.value;
11218
        }
11219
    }
11220
    if isRefType(ty) {
11221
        if let case ast::NodeValue::Let(binding) = sym.node.value {
11222
            if copiesRegionalReference(ty, binding.value) {
11223
                return nil;
11224
            }
11225
            return binding.value;
11226
        }
11227
        if let case ast::NodeValue::RegionBinding(binding) = sym.node.value {
11228
            return binding.value;
11229
        }
11230
    }
11231
    return nil;
11232
}
11233
11234
/// Resolve a place through reference locals without extending its storage lifetime.
11235
unsafe fn borrowPlace 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> BorrowPlace {
11236
    let mut place = emptyBorrowPlace();
11237
    match node.value {
11238
        case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_) => {
11239
            let sym = symbolFor(self, node) else return place;
11240
            if let source = localReferenceSource(sym) {
11241
                let origin = borrowPlace(self, source);
11242
                if origin.root <> nil {
11243
                    return origin;
11244
                }
11245
            }
11246
            set place.root = sym;
11247
        }
11248
        case ast::NodeValue::AddressOf(addr) => return borrowPlace(self, addr.target),
11249
        case ast::NodeValue::As(expr) => return borrowPlace(self, expr.value),
11250
        case ast::NodeValue::FieldAccess(access) => {
11251
            set place = borrowPlace(self, access.parent);
11252
            if let ty = typeFor(self, access.parent) {
11253
                if let case Type::Pointer { .. } = ty; not isRefType(ty) and place.len > 0 {
11254
                    set place.len = 0;
11255
                    set place.precise = false;
11256
                }
11257
                if let case Type::Nominal(NominalType::Record(_)) = autoDeref(ty);
11258
                    place.precise and place.len < MAX_BORROW_FIELDS
11259
                {
11260
                    if let index = recordFieldIndexFor(self, access.child) {
11261
                        set place.fields[place.len] = index;
11262
                        set place.len += 1;
11263
                        return place;
11264
                    }
11265
                }
11266
            }
11267
            set place.precise = false;
11268
        }
11269
        case ast::NodeValue::Subscript { container, .. } => {
11270
            set place = borrowPlace(self, container);
11271
            if let ty = typeFor(self, container) {
11272
                if let case Type::Slice { class, .. } = autoDeref(ty); not types::isReference(class) {
11273
                    set place.len = 0;
11274
                }
11275
            }
11276
            set place.precise = false;
11277
        }
11278
        case ast::NodeValue::Deref(target) => {
11279
            set place = borrowPlace(self, target);
11280
            if let ty = typeFor(self, target); not isRefType(ty) and place.len > 0 {
11281
                set place.len = 0;
11282
                set place.precise = false;
11283
            }
11284
        }
11285
        else => {}
11286
    }
11287
    return place;
11288
}
11289
11290
/// Two places overlap unless distinct inline fields prove separation.
11291
fn placesOverlap(left: &BorrowPlace, right: &BorrowPlace) -> bool {
11292
    if left.root == nil or left.root <> right.root {
11293
        return false;
11294
    }
11295
    let count = left.len if left.len < right.len else right.len;
11296
    for i in 0..count {
11297
        if left.fields[i] <> right.fields[i] {
11298
            return false;
11299
        }
11300
    }
11301
    return true;
11302
}
11303
11304
/// Check whether access uses a reference or one of its lexical reborrows.
11305
unsafe fn usesLocalLoan 'arena (self: &mut Resolver 'arena, node: *ast::Node, binding: *unsafe mut Symbol) -> bool {
11306
    let root = linearRootSymbol(self, node) else return false;
11307
    if root == binding {
11308
        return true;
11309
    }
11310
    let source = localReferenceSource(root) else return false;
11311
    return usesLocalLoan(self, source, binding);
11312
}
11313
/// Reject accesses that conflict with a reference in an active lexical scope.
11314
unsafe fn checkLocalLoans 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, exclusive: bool)
11315
    throws (ResolveError) where 'arena: 'checking
11316
{
11317
    let place = borrowPlace(checker.resolver, node);
11318
    let root = place.root;
11319
    if let placeRoot = root {
11320
        for i in 0..checker.regionalLen {
11321
            if (env.regionalLoans & ((1 as u64) << (i as u64))) == 0 {
11322
                continue;
11323
            }
11324
            let loan = regionalLoan(checker, i);
11325
            if (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
11326
                and not usesRegionalLoan(checker.resolver, node, loan.source)
11327
            {
11328
                throw emitError(
11329
                    checker.resolver, node, ErrorKind::BorrowConflict(placeRoot.name),
11330
                );
11331
            }
11332
        }
11333
    }
11334
    let mut accessRegion: ?*unsafe types::Region = nil;
11335
    if let ty = typeFor(checker.resolver, node) {
11336
        set accessRegion = referenceRegion(ty);
11337
    }
11338
    if let cellTy = try explicitCellPayload(checker.resolver, node) {
11339
        let case Type::Cell { permission, .. } = cellTy
11340
            else panic "checkLocalLoans: invalid cell payload";
11341
        set accessRegion = permission;
11342
    } else if let cellTy = try inferCellPayload(checker.resolver, node) {
11343
        let case Type::Cell { permission, .. } = cellTy
11344
            else panic "checkLocalLoans: invalid cell payload";
11345
        set accessRegion = permission;
11346
    }
11347
    for i in 0..checker.localLen {
11348
        let loan = checker.locals[i];
11349
        let mut throughBinding = false;
11350
        if let binding = loan.binding {
11351
            set throughBinding = usesLocalLoan(checker.resolver, node, binding);
11352
        }
11353
        if let placeRoot = root;
11354
            (exclusive or loan.exclusive) and placesOverlap(&place, &loan.place)
11355
            and not throughBinding
11356
        {
11357
            throw emitError(
11358
                checker.resolver, node, ErrorKind::BorrowConflict(placeRoot.name),
11359
            );
11360
        }
11361
        let permission = loan.permission else continue;
11362
        let region = accessRegion else continue;
11363
        let mut identityMatches = permission.id == region.id;
11364
        if let storage = loan.storage; storage.id == region.id {
11365
            set identityMatches = true;
11366
        }
11367
        if not identityMatches or not (exclusive or loan.exclusive) or throughBinding {
11368
            continue;
11369
        }
11370
        let mut name = permission.name;
11371
        if let placeRoot = root {
11372
            set name = placeRoot.name;
11373
        }
11374
        throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(name));
11375
    }
11376
}
11377
11378
/// Return cell metadata for an explicit payload borrow through transparent wrappers.
11379
unsafe fn explicitCellPayload 'arena (
11380
    self: &mut Resolver 'arena, node: *ast::Node
11381
) -> ?Type throws (ResolveError) {
11382
    match node.value {
11383
        case ast::NodeValue::AddressOf(address) =>
11384
            return try inferCellPayload(self, address.target),
11385
        case ast::NodeValue::As(cast) =>
11386
            return try explicitCellPayload(self, cast.value),
11387
        case ast::NodeValue::RegionApply { value, .. } =>
11388
            return try explicitCellPayload(self, value),
11389
        else => return nil,
11390
    }
11391
}
11392
11393
/// Retain source storage for local borrows and region headers.
11394
unsafe fn addLocalLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv, node: *ast::Node, binding: ast::Let)
11395
    throws (ResolveError) where 'arena: 'checking
11396
{
11397
    let ty = typeFor(checker.resolver, binding.ident) else return;
11398
    if not isRefType(ty) {
11399
        let case Type::Session(_) = ty else return;
11400
        let case ast::NodeValue::RegionBinding(_) = node.value else return;
11401
    }
11402
    if let case ast::NodeValue::Let(_) = node.value;
11403
        copiesRegionalReference(ty, binding.value)
11404
    {
11405
        return;
11406
    }
11407
    let place = borrowPlace(checker.resolver, binding.value);
11408
    if place.root == nil {
11409
        if referenceRegion(ty) <> nil {
11410
            return;
11411
        }
11412
        throw emitError(checker.resolver, node, ErrorKind::RefBinding);
11413
    }
11414
    if checker.localLen >= MAX_LINEAR_BINDINGS {
11415
        throw emitError(checker.resolver, node, ErrorKind::Internal);
11416
    }
11417
    let sym = symbolFor(checker.resolver, node) else panic "reference without binding";
11418
    let mut exclusive = isExclusiveArgument(ty) or createsCellBorrow(binding.value);
11419
    if let case Type::Cell { .. } = ty {
11420
        if let case ast::NodeValue::As(expr) = binding.value.value {
11421
            if let source = typeFor(checker.resolver, expr.value) {
11422
                if let case Type::Pointer { mutable: true, .. } = source {
11423
                    set exclusive = true;
11424
                }
11425
            }
11426
        }
11427
    }
11428
    try checkLocalLoans(checker, env, binding.value, exclusive);
11429
    let mut permission: ?*unsafe types::Region = nil;
11430
    let mut storage: ?*unsafe types::Region = nil;
11431
    if let cellTy = try explicitCellPayload(checker.resolver, binding.value) {
11432
        let case Type::Cell {
11433
            class, permission: cellPermission, ..
11434
        } = cellTy else panic "addLocalLoan: invalid cell payload";
11435
        set permission = cellPermission;
11436
        if let case types::PointerClass::Region(region) = class {
11437
            set storage = region;
11438
        }
11439
    }
11440
    set checker.locals[checker.localLen] = LocalLoan {
11441
        binding: sym, place, exclusive, permission, storage,
11442
    };
11443
    set checker.localLen += 1;
11444
}
11445
11446
/// Protect storage borrowed by a pointer pattern until its bindings leave scope.
11447
unsafe fn addPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, subject: *ast::Node)
11448
    throws (ResolveError) where 'arena: 'checking
11449
{
11450
    let ty = typeFor(checker.resolver, subject) else return;
11451
    if unwrapMatchSubject(ty).by == MatchBy::Value {
11452
        return;
11453
    }
11454
    let place = borrowPlace(checker.resolver, subject);
11455
    if place.root == nil {
11456
        return;
11457
    }
11458
    if checker.loanLen >= MAX_LINEAR_BINDINGS {
11459
        throw emitError(checker.resolver, subject, ErrorKind::Internal);
11460
    }
11461
    set checker.loans[checker.loanLen] = place;
11462
    set checker.loanLen += 1;
11463
}
11464
11465
/// Reject a write, mutable loan, or ownership transfer of a pattern source.
11466
unsafe fn checkPatternLoan 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, node: *ast::Node)
11467
    throws (ResolveError) where 'arena: 'checking
11468
{
11469
    let place = borrowPlace(checker.resolver, node);
11470
    let root = place.root else return;
11471
    for i in 0..checker.loanLen {
11472
        if placesOverlap(&checker.loans[i], &place) {
11473
            throw emitError(checker.resolver, node, ErrorKind::BorrowConflict(root.name));
11474
        }
11475
    }
11476
}
11477
11478
/// Return whether a parameter borrows its argument only for the call.
11479
fn isBorrowedReferenceParameter(ty: Type) -> bool {
11480
    if not isRefType(ty) {
11481
        return false;
11482
    }
11483
    match ty {
11484
        case Type::Cell { class, .. } => return class == types::PointerClass::Ref,
11485
        case Type::Pointer { class, .. } => return class == types::PointerClass::Ref,
11486
        case Type::Slice { class, .. } => return class == types::PointerClass::Ref,
11487
        case Type::TraitObject { class, .. } => return class == types::PointerClass::Ref,
11488
        else => return false,
11489
    }
11490
}
11491
11492
/// Return whether a type carries one region in the requested compile-time role.
11493
/// Applied nominal visits are generation- and role-marked; unapplied nominals
11494
/// cannot carry the queried free region and require no member traversal.
11495
unsafe fn typeHasRegionRole 'arena (
11496
    self: &mut Resolver 'arena,
11497
    ty: Type,
11498
    region: *unsafe types::Region,
11499
    role: RegionTypeRole,
11500
    generation: u32,
11501
) -> bool {
11502
    if role == RegionTypeRole::Reference {
11503
        if let dependency = referenceRegion(ty); dependency == region {
11504
            return true;
11505
        }
11506
    }
11507
    match ty {
11508
        case Type::Cell { permission, payload, .. } => {
11509
            if role == RegionTypeRole::Retention {
11510
                return typeHasRegionRole(
11511
                    self, *payload, region, RegionTypeRole::Reference, generation,
11512
                ) or typeHasRegionRole(
11513
                    self, *payload, region, RegionTypeRole::Retention, generation,
11514
                );
11515
            }
11516
            if role == RegionTypeRole::CellPermission {
11517
                if let identity = permission; identity == region {
11518
                    return true;
11519
                }
11520
            }
11521
            return typeHasRegionRole(
11522
                self, *payload, region, role, generation,
11523
            );
11524
        }
11525
        case Type::Session(identity) =>
11526
            return role == RegionTypeRole::Retention
11527
                and types::regionContains(region, identity),
11528
        case Type::Pointer { target, .. } =>
11529
            return typeHasRegionRole(
11530
                self, *target, region, role, generation,
11531
            ),
11532
        case Type::Slice { item, .. } =>
11533
            return typeHasRegionRole(
11534
                self, *item, region, role, generation,
11535
            ),
11536
        case Type::Array(array) =>
11537
            return typeHasRegionRole(
11538
                self, *array.item, region, role, generation,
11539
            ),
11540
        case Type::Optional(inner) =>
11541
            return typeHasRegionRole(
11542
                self, *inner, region, role, generation,
11543
            ),
11544
        case Type::Range { start, end } => {
11545
            if let startType = start;
11546
                typeHasRegionRole(
11547
                    self, *startType, region, role, generation,
11548
                )
11549
            {
11550
                return true;
11551
            }
11552
            if let endType = end {
11553
                return typeHasRegionRole(
11554
                    self, *endType, region, role, generation,
11555
                );
11556
            }
11557
            return false;
11558
        }
11559
        case Type::Nominal(info) => {
11560
            let applied = nominalApplication(info) else return false;
11561
            if not visitNominalApplication(applied, generation, role) {
11562
                // This application and role have no unvisited members.
11563
                return false;
11564
            }
11565
            match *info {
11566
                case NominalType::Record(recordType) => {
11567
                    for field in recordType.fields {
11568
                        if typeHasRegionRole(
11569
                            self, field.fieldType, region, role, generation,
11570
                        ) {
11571
                            return true;
11572
                        }
11573
                    }
11574
                }
11575
                case NominalType::Union(unionType) => {
11576
                    for variant in unionType.variants {
11577
                        if typeHasRegionRole(
11578
                            self, variant.valueType, region, role, generation,
11579
                        ) {
11580
                            return true;
11581
                        }
11582
                    }
11583
                }
11584
                // Linear checking normally sees completed applications. Deny
11585
                // reuse without treating an unknown view as permission proof.
11586
                case NominalType::Placeholder(_), NominalType::Resolving(_),
11587
                     NominalType::Application(_) =>
11588
                    return role <> RegionTypeRole::CellPermission,
11589
            }
11590
            return false;
11591
        }
11592
        case Type::Fn(info) => {
11593
            for parameter in info.paramTypes {
11594
                if typeHasRegionRole(
11595
                    self, *parameter, region, role, generation,
11596
                ) {
11597
                    return true;
11598
                }
11599
            }
11600
            for error in info.throwList {
11601
                if typeHasRegionRole(
11602
                    self, *error, region, role, generation,
11603
                ) {
11604
                    return true;
11605
                }
11606
            }
11607
            return typeHasRegionRole(
11608
                self, *info.returnType, region, role, generation,
11609
            );
11610
        }
11611
        else => return false,
11612
    }
11613
}
11614
11615
/// Classify one call argument from the instantiated function contract.
11616
/// Regional mutable authority is call-borrowed only when the contract uses its
11617
/// region as cell permission and no result, error, other input, or mutable
11618
/// receiver can retain a reference carrying that region.
11619
unsafe fn callParameterUse 'arena (
11620
    self: &mut Resolver 'arena,
11621
    info: *FnType,
11622
    parameterIndex: u32,
11623
    receiver: ?Type,
11624
    receiverMutable: bool,
11625
) -> LinearUse {
11626
    let parameter = *info.paramTypes[parameterIndex];
11627
    if isBorrowedReferenceParameter(parameter) {
11628
        return LinearUse::Borrow;
11629
    }
11630
    let case Type::Pointer {
11631
        class: types::PointerClass::Region(region), mutable: true, ..
11632
    } = parameter else return LinearUse::Consume;
11633
11634
    let permissionGeneration = nextNominalTraversalGeneration(self);
11635
    let mut hasPermission = false;
11636
    if let receiverType = receiver {
11637
        set hasPermission = typeHasRegionRole(
11638
            self, receiverType, region, RegionTypeRole::CellPermission,
11639
            permissionGeneration,
11640
        );
11641
    }
11642
    if not hasPermission {
11643
        for candidate in info.paramTypes {
11644
            if typeHasRegionRole(
11645
                self, *candidate, region, RegionTypeRole::CellPermission,
11646
                permissionGeneration,
11647
            ) {
11648
                set hasPermission = true;
11649
                break;
11650
            }
11651
        }
11652
    }
11653
    if not hasPermission {
11654
        for error in info.throwList {
11655
            if typeHasRegionRole(
11656
                self, *error, region, RegionTypeRole::CellPermission,
11657
                permissionGeneration,
11658
            ) {
11659
                set hasPermission = true;
11660
                break;
11661
            }
11662
        }
11663
    }
11664
    if not hasPermission {
11665
        set hasPermission = typeHasRegionRole(
11666
            self, *info.returnType, region, RegionTypeRole::CellPermission,
11667
            permissionGeneration,
11668
        );
11669
    }
11670
    if not hasPermission {
11671
        return LinearUse::Consume;
11672
    }
11673
11674
    // Results and thrown values outlive the call directly.
11675
    let resultGeneration = nextNominalTraversalGeneration(self);
11676
    if typeHasRegionRole(
11677
        self, *info.returnType, region, RegionTypeRole::Reference,
11678
        resultGeneration,
11679
    ) {
11680
        return LinearUse::Consume;
11681
    }
11682
    for error in info.throwList {
11683
        if typeHasRegionRole(
11684
            self, *error, region, RegionTypeRole::Reference, resultGeneration,
11685
        ) {
11686
            return LinearUse::Consume;
11687
        }
11688
    }
11689
11690
    // A mutable input can store the authority reference, while cells and
11691
    // sessions provide interior retention even through an otherwise shared
11692
    // contract position. Exclude the authority parameter itself.
11693
    for candidate, i in info.paramTypes {
11694
        if i == parameterIndex {
11695
            continue;
11696
        }
11697
        let retentionGeneration = nextNominalTraversalGeneration(self);
11698
        if typeHasRegionRole(
11699
            self, *candidate, region, RegionTypeRole::Retention,
11700
            retentionGeneration,
11701
        ) {
11702
            return LinearUse::Consume;
11703
        }
11704
        let referenceGeneration = nextNominalTraversalGeneration(self);
11705
        if typeHasRegionRole(
11706
            self, *candidate, region, RegionTypeRole::Reference,
11707
            referenceGeneration,
11708
        ) and isExclusiveArgument(*candidate) {
11709
            return LinearUse::Consume;
11710
        }
11711
    }
11712
11713
    if let receiverType = receiver {
11714
        let retentionGeneration = nextNominalTraversalGeneration(self);
11715
        if typeHasRegionRole(
11716
            self, receiverType, region, RegionTypeRole::Retention,
11717
            retentionGeneration,
11718
        ) {
11719
            return LinearUse::Consume;
11720
        }
11721
        if receiverMutable {
11722
            let referenceGeneration = nextNominalTraversalGeneration(self);
11723
            if typeHasRegionRole(
11724
                self, receiverType, region, RegionTypeRole::Reference,
11725
                referenceGeneration,
11726
            ) {
11727
                return LinearUse::Consume;
11728
            }
11729
        }
11730
    }
11731
    return LinearUse::Borrow;
11732
}
11733
11734
/// Return whether a parameter can mutate or consume its argument's storage.
11735
unsafe fn isExclusiveArgument(ty: Type) -> bool {
11736
    match ty {
11737
        case Type::Pointer { mutable, .. } => return mutable,
11738
        case Type::Slice { mutable, .. } => return mutable,
11739
        case Type::TraitObject { mutable, .. } => return mutable,
11740
        else => return isMoveOnly(ty),
11741
    }
11742
}
11743
11744
/// Add the value identifiers introduced by a pattern.
11745
/// Return whether the pattern introduces references to its source storage.
11746
unsafe fn addLinearPatternBindings 'arena 'checking (
11747
    checker: &mut LinearChecker 'arena 'checking,
11748
    env: &mut LinearEnv,
11749
    pattern: *ast::Node,
11750
) -> bool throws (ResolveError) where 'arena: 'checking {
11751
    let mut hasReferences = false;
11752
    match pattern.value {
11753
        case ast::NodeValue::Ident(_) => {
11754
            try addLinearBinding(checker, env, pattern);
11755
            if let ty = typeFor(checker.resolver, pattern) {
11756
                return isRefType(ty);
11757
            }
11758
        }
11759
        case ast::NodeValue::Call(call) => {
11760
            for arg in call.args {
11761
                if try addLinearPatternBindings(checker, env, arg) {
11762
                    set hasReferences = true;
11763
                }
11764
            }
11765
        }
11766
        case ast::NodeValue::RecordLit(lit) => {
11767
            for fieldNode in lit.fields {
11768
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
11769
                    else panic "addLinearPatternBindings: expected field";
11770
                if try addLinearPatternBindings(checker, env, field.value) {
11771
                    set hasReferences = true;
11772
                }
11773
            }
11774
        }
11775
        case ast::NodeValue::ArrayLit(items) => {
11776
            for item in items {
11777
                if try addLinearPatternBindings(checker, env, item) {
11778
                    set hasReferences = true;
11779
                }
11780
            }
11781
        }
11782
        else => {}
11783
    }
11784
    return hasReferences;
11785
}
11786
11787
/// Check a lexical block and exact-use of locals introduced in it.
11788
unsafe fn checkLinearBlock 'arena 'checking (
11789
    checker: &mut LinearChecker 'arena 'checking,
11790
    env: &mut LinearEnv,
11791
    node: *ast::Node,
11792
) throws (ResolveError) where 'arena: 'checking {
11793
    let start = env.len;
11794
    let localStart = checker.localLen;
11795
    let authorityStart = checker.authorityLen;
11796
    let case ast::NodeValue::Block(block) = node.value
11797
        else panic "checkLinearBlock: expected block";
11798
    for stmt in block.statements {
11799
        if env.terminated {
11800
            break;
11801
        }
11802
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
11803
    }
11804
    try finishLinearScope(checker, env, start);
11805
    set checker.localLen = localStart;
11806
    set checker.authorityLen = authorityStart;
11807
}
11808
11809
/// Push a repeated-control-flow boundary.
11810
/// Initialize all loop state at this depth before increasing `loopDepth`.
11811
fn enterLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
11812
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
11813
    let depth = checker.loopDepth;
11814
    set checker.loopBackLoans[depth] = 0;
11815
    set checker.loopExitLoans[depth] = 0;
11816
    set checker.loopRegions[depth] = checker.regions;
11817
    set checker.loopMarks[depth] = env.len;
11818
    set checker.loopAvailable[depth] = env.available;
11819
    set checker.loopExitAvailable[depth] = env.available;
11820
    set checker.loopHasNaturalExit[depth] = false;
11821
    set checker.loopBreakSeen[depth] = false;
11822
    set checker.loopDepth += 1;
11823
}
11824
11825
/// Require a repeated body's outer bindings to match its entry state.
11826
fn checkLinearLoopBackEdge 'arena 'checking (
11827
    checker: &mut LinearChecker 'arena 'checking,
11828
    env: &LinearEnv,
11829
    node: *ast::Node,
11830
) throws (ResolveError) where 'arena: 'checking {
11831
    if env.terminated {
11832
        return;
11833
    }
11834
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
11835
    let depth = checker.loopDepth - 1;
11836
    set checker.loopBackLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
11837
    let mark = checker.loopMarks[depth];
11838
    let entryAvailable = checker.loopAvailable[depth];
11839
    for i in 0..mark {
11840
        let bit = (1 as u64) << (i as u64);
11841
        if (env.available & bit) <> (entryAvailable & bit) {
11842
            let sym = linearSymbol(env, i);
11843
            throw emitError(
11844
                checker.resolver,
11845
                node,
11846
                ErrorKind::LinearBranchMismatch(sym.name),
11847
            );
11848
        }
11849
    }
11850
}
11851
11852
/// Record the ownership state of a loop's condition-false exit.
11853
fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking {
11854
    assert checker.loopDepth > 0, "linear loop exit outside loop";
11855
    let depth = checker.loopDepth - 1;
11856
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
11857
    set checker.loopExitAvailable[depth] = env.available;
11858
    set checker.loopHasNaturalExit[depth] = true;
11859
}
11860
11861
/// Require a break exit to agree with every other exit from this loop.
11862
fn checkLinearLoopBreak 'arena 'checking (
11863
    checker: &mut LinearChecker 'arena 'checking,
11864
    env: &LinearEnv,
11865
    node: *ast::Node,
11866
) throws (ResolveError) where 'arena: 'checking {
11867
    assert checker.loopDepth > 0, "linear loop break outside loop";
11868
    let depth = checker.loopDepth - 1;
11869
    set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]);
11870
    let mark = checker.loopMarks[depth];
11871
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
11872
        let expected = checker.loopExitAvailable[depth];
11873
        for i in 0..mark {
11874
            let bit = (1 as u64) << (i as u64);
11875
            if (env.available & bit) <> (expected & bit) {
11876
                let sym = linearSymbol(env, i);
11877
                throw emitError(
11878
                    checker.resolver,
11879
                    node,
11880
                    ErrorKind::LinearBranchMismatch(sym.name),
11881
                );
11882
            }
11883
        }
11884
    } else {
11885
        set checker.loopExitAvailable[depth] = env.available;
11886
    }
11887
    set checker.loopBreakSeen[depth] = true;
11888
}
11889
11890
/// Pop a repeated-control-flow boundary.
11891
fn exitLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking) where 'arena: 'checking {
11892
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
11893
    set checker.loopDepth -= 1;
11894
}
11895
11896
/// Check a conditional and merge its ownership states.
11897
unsafe fn checkLinearIf 'arena 'checking (
11898
    checker: &mut LinearChecker 'arena 'checking,
11899
    env: &mut LinearEnv,
11900
    node: *ast::Node,
11901
    conditional: ast::If,
11902
) throws (ResolveError) where 'arena: 'checking {
11903
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
11904
    let base = *env;
11905
    let mut thenEnv = base;
11906
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
11907
    let mut elseEnv = base;
11908
    if let branch = conditional.elseBranch {
11909
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
11910
    }
11911
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
11912
}
11913
11914
/// Check an expression conditional and merge its ownership states.
11915
unsafe fn checkLinearCondExpr 'arena 'checking (
11916
    checker: &mut LinearChecker 'arena 'checking,
11917
    env: &mut LinearEnv,
11918
    node: *ast::Node,
11919
    conditional: ast::CondExpr,
11920
    usage: LinearUse,
11921
) throws (ResolveError) where 'arena: 'checking {
11922
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
11923
    let base = *env;
11924
    let mut thenEnv = base;
11925
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
11926
    let mut elseEnv = base;
11927
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
11928
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
11929
}
11930
11931
/// Pointer patterns borrow their subject; value patterns consume it.
11932
fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse {
11933
    if let ty = typeFor(self, subject) {
11934
        if let case Type::Pointer { .. } = ty {
11935
            return LinearUse::Borrow;
11936
        }
11937
    }
11938
    return LinearUse::Consume;
11939
}
11940
11941
/// Check a match expression, including ownership transferred into patterns.
11942
unsafe fn checkLinearMatch 'arena 'checking (
11943
    checker: &mut LinearChecker 'arena 'checking,
11944
    env: &mut LinearEnv,
11945
    node: *ast::Node,
11946
    matchExpr: ast::Match,
11947
) throws (ResolveError) where 'arena: 'checking {
11948
    try checkLinearNode(checker, env, matchExpr.subject, patternSubjectUse(checker.resolver, matchExpr.subject));
11949
    let base = *env;
11950
    let mut haveResult = false;
11951
    let mut result = base;
11952
    for prongNode in matchExpr.prongs {
11953
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
11954
            else panic "checkLinearMatch: expected prong";
11955
        let mut branch = base;
11956
        let bindingsStart = branch.len;
11957
        let loanStart = checker.loanLen;
11958
        match prong.arm {
11959
            case ast::ProngArm::Case(patterns) => {
11960
                for pattern in patterns {
11961
                    if try addLinearPatternBindings(checker, &mut branch, pattern) {
11962
                        try addPatternLoan(checker, matchExpr.subject);
11963
                    }
11964
                }
11965
            }
11966
            case ast::ProngArm::Binding(binding) => {
11967
                if try addLinearPatternBindings(checker, &mut branch, binding) {
11968
                    try addPatternLoan(checker, matchExpr.subject);
11969
                }
11970
            }
11971
            case ast::ProngArm::Else => {}
11972
        }
11973
        if prong.guard <> nil {
11974
            for i in bindingsStart..branch.len {
11975
                let sym = linearSymbol(&branch, i);
11976
                if sym.usage == BindingUse::Linear {
11977
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
11978
                }
11979
            }
11980
        }
11981
        if let guard = prong.guard {
11982
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
11983
        }
11984
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
11985
        try finishLinearScope(checker, &mut branch, bindingsStart);
11986
        set checker.loanLen = loanStart;
11987
        if haveResult {
11988
            let previous = result;
11989
        try joinLinearBranches(checker, &mut result, &previous, &branch, node);
11990
        } else {
11991
            set result = branch;
11992
            set haveResult = true;
11993
        }
11994
    }
11995
    if haveResult {
11996
        set *env = result;
11997
    }
11998
}
11999
12000
/// Check call-scoped loans and argument ownership transfers.
12001
unsafe fn checkLinearCall 'arena 'checking (
12002
    checker: &mut LinearChecker 'arena 'checking,
12003
    env: &mut LinearEnv,
12004
    node: *ast::Node,
12005
    call: ast::Call,
12006
) throws (ResolveError) where 'arena: 'checking {
12007
    let localStart = checker.localLen;
12008
    match checker.resolver.nodeData.entries[node.id].extra {
12009
        case NodeExtra::SliceAppend { .. }, NodeExtra::SliceDelete { .. } => {
12010
            let case ast::NodeValue::FieldAccess(access) = call.callee.value
12011
                else panic "slice mutation without receiver";
12012
            try checkPatternLoan(checker, access.parent);
12013
            try checkLocalLoans(checker, env, access.parent, true);
12014
        }
12015
        else => {}
12016
    }
12017
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
12018
    let mut fnInfo: ?*FnType = nil;
12019
    if let calleeTy = typeFor(checker.resolver, call.callee) {
12020
        if let case Type::Fn(info) = calleeTy {
12021
            set fnInfo = info;
12022
        }
12023
    }
12024
    if fnInfo == nil {
12025
        match checker.resolver.nodeData.entries[node.id].extra {
12026
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } =>
12027
                set fnInfo = traitInfo.methods[methodIndex].fnType,
12028
            case NodeExtra::MethodCall { method } => set fnInfo = method.fnType,
12029
            else => {}
12030
        }
12031
    }
12032
    let info = fnInfo else {
12033
        for arg in call.args {
12034
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
12035
        }
12036
        return;
12037
    };
12038
    let mut arguments: [?CallArgument; MAX_FN_PARAMS + 1] = [nil; MAX_FN_PARAMS + 1];
12039
    let mut argumentsLen: u32 = 0;
12040
    let mut contractReceiver: ?Type = nil;
12041
    let mut contractReceiverMutable = false;
12042
12043
    // Method function types exclude their implicit receiver. Account for it
12044
    // explicitly so owning receivers are consumed and reference receivers
12045
    // participate in call-scoped loan conflict checks.
12046
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
12047
        let mut receiverClass = types::PointerClass::Unsafe;
12048
        let mut receiverMutable = false;
12049
        let mut haveReceiver = false;
12050
        match checker.resolver.nodeData.entries[node.id].extra {
12051
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
12052
                let method = &traitInfo.methods[methodIndex];
12053
                set receiverClass = method.receiverClass;
12054
                set receiverMutable = method.mutable;
12055
                set haveReceiver = true;
12056
            }
12057
            case NodeExtra::MethodCall { method } => {
12058
                set receiverClass = method.receiverClass;
12059
                set receiverMutable = method.mutable;
12060
                set haveReceiver = true;
12061
            }
12062
            else => {}
12063
        }
12064
        if haveReceiver {
12065
            if let receiverType = typeFor(checker.resolver, access.parent) {
12066
                set contractReceiver = autoDeref(receiverType);
12067
            }
12068
            set contractReceiverMutable = receiverMutable;
12069
            try checkLocalLoans(checker, env, access.parent,
12070
                receiverMutable or receiverClass == types::PointerClass::Owned);
12071
            if receiverMutable or receiverClass == types::PointerClass::Owned {
12072
                try checkPatternLoan(checker, access.parent);
12073
            }
12074
            if receiverClass <> types::PointerClass::Unsafe {
12075
                set arguments[argumentsLen] = CallArgument {
12076
                    node: access.parent,
12077
                    exclusive: receiverClass == types::PointerClass::Owned or receiverMutable,
12078
                };
12079
                set argumentsLen += 1;
12080
            }
12081
            if types::isReference(receiverClass) {
12082
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
12083
                if createsExplicitBorrow(access.parent) {
12084
                    try retainCallLoan(checker, access.parent, receiverMutable);
12085
                }
12086
            } else if receiverClass == types::PointerClass::Owned {
12087
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
12088
            }
12089
        }
12090
    }
12091
12092
    for arg, i in call.args {
12093
        let expected = *info.paramTypes[i];
12094
        let argExclusive = isExclusiveArgument(expected) or createsCellBorrow(arg);
12095
        if argExclusive {
12096
            try checkPatternLoan(checker, arg);
12097
        }
12098
        if not isUnsafePointerType(expected) {
12099
            for j in 0..argumentsLen {
12100
                let previous = arguments[j] else panic "checkLinearCall: missing active argument";
12101
                if previous.exclusive or argExclusive {
12102
                    if let name = callArgumentConflict(checker.resolver, previous.node, arg) {
12103
                        throw emitError(checker.resolver, arg, ErrorKind::BorrowConflict(name));
12104
                    }
12105
                }
12106
            }
12107
            set arguments[argumentsLen] = CallArgument { node: arg, exclusive: argExclusive };
12108
            set argumentsLen += 1;
12109
        }
12110
        try checkLocalLoans(checker, env, arg, argExclusive);
12111
        let argumentUse = callParameterUse(
12112
            checker.resolver, info, i, contractReceiver, contractReceiverMutable,
12113
        );
12114
        try checkLinearNode(checker, env, arg, argumentUse);
12115
        if isRefType(expected) and createsExplicitBorrow(arg) {
12116
            try retainCallLoan(checker, arg, argExclusive);
12117
        }
12118
    }
12119
    set checker.localLen = localStart;
12120
    if *info.returnType == Type::Never and info.throwList.len == 0 {
12121
        set env.terminated = true;
12122
    }
12123
}
12124
12125
/// Return the storage name when two call arguments can address the same place.
12126
unsafe fn callArgumentConflict 'arena (
12127
    self: &mut Resolver 'arena, left: *ast::Node, right: *ast::Node
12128
) -> ?*[u8] {
12129
    match left.value {
12130
        case ast::NodeValue::CondExpr(cond) => {
12131
            if let name = callArgumentConflict(self, cond.thenExpr, right) {
12132
                return name;
12133
            }
12134
            return callArgumentConflict(self, cond.elseExpr, right);
12135
        }
12136
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, cast.value, right),
12137
        case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, value, right),
12138
        else => {}
12139
    }
12140
    match right.value {
12141
        case ast::NodeValue::CondExpr(cond) => {
12142
            if let name = callArgumentConflict(self, left, cond.thenExpr) {
12143
                return name;
12144
            }
12145
            return callArgumentConflict(self, left, cond.elseExpr);
12146
        }
12147
        case ast::NodeValue::As(cast) => return callArgumentConflict(self, left, cast.value),
12148
        case ast::NodeValue::RegionApply { value, .. } => return callArgumentConflict(self, left, value),
12149
        else => {}
12150
    }
12151
    let leftPlace = borrowPlace(self, left);
12152
    let rightPlace = borrowPlace(self, right);
12153
    if placesOverlap(&leftPlace, &rightPlace) {
12154
        let root = rightPlace.root else panic "callArgumentConflict: overlap without root";
12155
        return root.name;
12156
    }
12157
    return nil;
12158
}
12159
12160
/// Return whether evaluating an argument creates an explicit address borrow.
12161
fn createsExplicitBorrow(node: *ast::Node) -> bool {
12162
    match node.value {
12163
        case ast::NodeValue::AddressOf(_) => return true,
12164
        case ast::NodeValue::As(cast) => return createsExplicitBorrow(cast.value),
12165
        case ast::NodeValue::RegionApply { value, .. } => return createsExplicitBorrow(value),
12166
        case ast::NodeValue::CondExpr(cond) =>
12167
            return createsExplicitBorrow(cond.thenExpr) or createsExplicitBorrow(cond.elseExpr),
12168
        else => return false,
12169
    }
12170
}
12171
12172
/// Protect explicit address arguments until their call begins.
12173
unsafe fn retainCallLoan 'arena 'checking (
12174
    checker: &mut LinearChecker 'arena 'checking, node: *ast::Node, exclusive: bool
12175
) throws (ResolveError) where 'arena: 'checking {
12176
    match node.value {
12177
        case ast::NodeValue::CondExpr(cond) => {
12178
            try retainCallLoan(checker, cond.thenExpr, exclusive);
12179
            try retainCallLoan(checker, cond.elseExpr, exclusive);
12180
            return;
12181
        }
12182
        case ast::NodeValue::As(cast) => {
12183
            try retainCallLoan(checker, cast.value, exclusive);
12184
            return;
12185
        }
12186
        case ast::NodeValue::RegionApply { value, .. } => {
12187
            try retainCallLoan(checker, value, exclusive);
12188
            return;
12189
        }
12190
        else => {}
12191
    }
12192
    let place = borrowPlace(checker.resolver, node);
12193
    if place.root == nil {
12194
        return;
12195
    }
12196
    if checker.localLen >= MAX_LINEAR_BINDINGS {
12197
        throw emitError(checker.resolver, node, ErrorKind::Internal);
12198
    }
12199
    let mut permission: ?*unsafe types::Region = nil;
12200
    let mut storage: ?*unsafe types::Region = nil;
12201
    if let cellTy = try explicitCellPayload(checker.resolver, node) {
12202
        let case Type::Cell {
12203
            class, permission: cellPermission, ..
12204
        } = cellTy else panic "retainCallLoan: invalid cell payload";
12205
        set permission = cellPermission;
12206
        if let case types::PointerClass::Region(region) = class {
12207
            set storage = region;
12208
        }
12209
    }
12210
    set checker.locals[checker.localLen] = LocalLoan {
12211
        binding: nil, place, exclusive, permission, storage,
12212
    };
12213
    set checker.localLen += 1;
12214
}
12215
12216
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
12217
unsafe fn checkLinearIfLet 'arena 'checking (
12218
    checker: &mut LinearChecker 'arena 'checking,
12219
    env: &mut LinearEnv,
12220
    node: *ast::Node,
12221
    conditional: ast::IfLet,
12222
) throws (ResolveError) where 'arena: 'checking {
12223
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
12224
        isLinear(subjectTy)
12225
    {
12226
        throw emitError(
12227
            checker.resolver,
12228
            conditional.pattern.scrutinee,
12229
            ErrorKind::LinearPartialMove,
12230
        );
12231
    }
12232
    try checkLinearNode(
12233
        checker,
12234
        env,
12235
        conditional.pattern.scrutinee,
12236
        patternSubjectUse(checker.resolver, conditional.pattern.scrutinee),
12237
    );
12238
    let base = *env;
12239
    let mut thenEnv = base;
12240
    let bindingsStart = thenEnv.len;
12241
    let loanStart = checker.loanLen;
12242
    if try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern) {
12243
        try addPatternLoan(checker, conditional.pattern.scrutinee);
12244
    }
12245
    expireAllocationLoans(checker, &mut thenEnv, conditional.pattern.pattern, conditional.pattern.scrutinee);
12246
    if let guard = conditional.pattern.guard {
12247
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
12248
    }
12249
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
12250
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
12251
    set checker.loanLen = loanStart;
12252
    let mut elseEnv = base;
12253
    if let branch = conditional.elseBranch {
12254
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
12255
    }
12256
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
12257
}
12258
12259
/// Check a repeated region-loan flow until its loop-entry mask is stable.
12260
/// Each additional pass must add a bit from the bounded regional loan table.
12261
unsafe fn checkLinearLoop 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
12262
    throws (ResolveError) where 'arena: 'checking
12263
{
12264
    if let case ast::NodeValue::For(forStmt) = node.value {
12265
        try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
12266
    }
12267
    let base = *env;
12268
    let depth = checker.loopDepth;
12269
    let mut entryLoans = base.regionalLoans;
12270
    loop {
12271
        let mut pass = base;
12272
        set pass.regionalLoans = entryLoans;
12273
        try checkLinearLoopPass(checker, &mut pass, node);
12274
        let next = entryLoans | checker.loopBackLoans[depth];
12275
        if next == entryLoans {
12276
            set *env = pass;
12277
            return;
12278
        }
12279
        set entryLoans = next;
12280
    }
12281
}
12282
12283
/// Check one pass through a loop with the current loop-entry loan state.
12284
unsafe fn checkLinearLoopPass 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, node: *ast::Node)
12285
    throws (ResolveError) where 'arena: 'checking
12286
{
12287
    match node.value {
12288
        case ast::NodeValue::While(whileStmt) => {
12289
            enterLinearLoop(checker, env);
12290
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
12291
            let conditionExit = *env;
12292
            setLinearLoopNaturalExit(checker, &conditionExit);
12293
            let mut bodyEnv = conditionExit;
12294
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
12295
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
12296
            exitLinearLoop(checker);
12297
            set *env = conditionExit;
12298
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
12299
            if let elseBranch = whileStmt.elseBranch {
12300
                let mut elseEnv = conditionExit;
12301
                try checkLinearNode(
12302
                    checker,
12303
                    &mut elseEnv,
12304
                    elseBranch,
12305
                    LinearUse::Discard,
12306
                );
12307
                let exits = *env;
12308
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
12309
            }
12310
        }
12311
        case ast::NodeValue::WhileLet(whileStmt) => {
12312
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
12313
                isLinear(subjectTy)
12314
            {
12315
                throw emitError(
12316
                    checker.resolver,
12317
                    whileStmt.pattern.scrutinee,
12318
                    ErrorKind::LinearPartialMove,
12319
                );
12320
            }
12321
            let base = *env;
12322
            enterLinearLoop(checker, env);
12323
            let mut bodyEnv = base;
12324
            try checkLinearNode(
12325
                checker,
12326
                &mut bodyEnv,
12327
                whileStmt.pattern.scrutinee,
12328
                patternSubjectUse(checker.resolver, whileStmt.pattern.scrutinee),
12329
            );
12330
            let mut conditionExit = bodyEnv;
12331
            let start = bodyEnv.len;
12332
            let loanStart = checker.loanLen;
12333
            if try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern) {
12334
                try addPatternLoan(checker, whileStmt.pattern.scrutinee);
12335
            }
12336
            expireAllocationLoans(checker, &mut bodyEnv, whileStmt.pattern.pattern, whileStmt.pattern.scrutinee);
12337
            if let guard = whileStmt.pattern.guard {
12338
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
12339
                let mut guardExit = bodyEnv;
12340
                try finishLinearScope(checker, &mut guardExit, start);
12341
                let previous = conditionExit;
12342
                try joinLinearBranches(
12343
                    checker,
12344
                    &mut conditionExit,
12345
                    &previous,
12346
                    &guardExit,
12347
                    guard,
12348
                );
12349
            }
12350
            setLinearLoopNaturalExit(checker, &conditionExit);
12351
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
12352
            try finishLinearScope(checker, &mut bodyEnv, start);
12353
            set checker.loanLen = loanStart;
12354
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
12355
            exitLinearLoop(checker);
12356
            set *env = conditionExit;
12357
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
12358
            if let elseBranch = whileStmt.elseBranch {
12359
                let mut elseEnv = conditionExit;
12360
                try checkLinearNode(
12361
                    checker,
12362
                    &mut elseEnv,
12363
                    elseBranch,
12364
                    LinearUse::Discard,
12365
                );
12366
                let exits = *env;
12367
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
12368
            }
12369
        }
12370
        case ast::NodeValue::For(forStmt) => {
12371
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
12372
                if isLinear(iterableTy) {
12373
                    throw emitError(
12374
                        checker.resolver,
12375
                        forStmt.iterable,
12376
                        ErrorKind::LinearPartialMove,
12377
                    );
12378
                }
12379
            }
12380
            let base = *env;
12381
            enterLinearLoop(checker, env);
12382
            setLinearLoopNaturalExit(checker, &base);
12383
            let mut bodyEnv = base;
12384
            let start = bodyEnv.len;
12385
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
12386
            if let index = forStmt.index {
12387
                try addLinearBinding(checker, &mut bodyEnv, index);
12388
            }
12389
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
12390
            try finishLinearScope(checker, &mut bodyEnv, start);
12391
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
12392
            exitLinearLoop(checker);
12393
            set *env = base;
12394
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
12395
            if let elseBranch = forStmt.elseBranch {
12396
                let mut elseEnv = base;
12397
                try checkLinearNode(
12398
                    checker,
12399
                    &mut elseEnv,
12400
                    elseBranch,
12401
                    LinearUse::Discard,
12402
                );
12403
                let exits = *env;
12404
                try joinLinearBranches(checker, env, &exits, &elseEnv, node);
12405
            }
12406
        }
12407
        case ast::NodeValue::Loop { body } => {
12408
            let base = *env;
12409
            enterLinearLoop(checker, env);
12410
            let mut bodyEnv = base;
12411
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
12412
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
12413
            let depth = checker.loopDepth - 1;
12414
            let breakSeen = checker.loopBreakSeen[depth];
12415
            let exitAvailable = checker.loopExitAvailable[depth];
12416
            exitLinearLoop(checker);
12417
            set *env = base;
12418
            set env.regionalLoans = checker.loopExitLoans[checker.loopDepth];
12419
            if breakSeen {
12420
                set env.available = exitAvailable;
12421
            } else {
12422
                set env.terminated = true;
12423
            }
12424
        }
12425
        else => panic "checkLinearLoopPass: expected loop",
12426
    }
12427
}
12428
12429
/// Transfer a cell's owning handle and check each address operand once.
12430
unsafe fn checkCellAddressOwner 'arena 'checking (
12431
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
12432
    node: *ast::Node, owner: *ast::Node
12433
) throws (ResolveError) where 'arena: 'checking {
12434
    if node == owner {
12435
        try checkLinearNode(checker, env, node, LinearUse::Consume);
12436
        return;
12437
    }
12438
    match node.value {
12439
        case ast::NodeValue::Deref(target) => try checkCellAddressOwner(checker, env, target, owner),
12440
        case ast::NodeValue::FieldAccess(access) => try checkCellAddressOwner(checker, env, access.parent, owner),
12441
        case ast::NodeValue::Subscript { container, index } => {
12442
            try checkCellAddressOwner(checker, env, container, owner);
12443
            try checkLinearNode(checker, env, index, LinearUse::Consume);
12444
        }
12445
        else => panic "cell address owner must belong to its place",
12446
    }
12447
}
12448
12449
/// Fresh allocation storage has no loans from earlier loop iterations.
12450
unsafe fn expireAllocationLoans 'arena 'checking (
12451
    checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv,
12452
    node: *ast::Node, value: *ast::Node
12453
) where 'arena: 'checking {
12454
    let case ast::NodeValue::Try(allocation) = value.value else return;
12455
    if allocation.catches.len <> 0 {
12456
        return;
12457
    }
12458
    let case NodeExtra::SessionAllocation(_) = checker.resolver.nodeData.entries[allocation.expr.id].extra
12459
        else return;
12460
    let symbol = checker.resolver.nodeData.entries[node.id].binding else return;
12461
    for i in 0..checker.regionalLen {
12462
        if regionalLoan(checker, i).place.root == symbol.symbol {
12463
            set env.regionalLoans &= ~((1 as u64) << (i as u64));
12464
        }
12465
    }
12466
}
12467
12468
/// Check one expression or statement under an ownership-use context.
12469
unsafe fn checkLinearNode 'arena 'checking (
12470
    checker: &mut LinearChecker 'arena 'checking,
12471
    env: &mut LinearEnv,
12472
    node: *ast::Node,
12473
    usage: LinearUse,
12474
) throws (ResolveError) where 'arena: 'checking {
12475
    if env.terminated {
12476
        return;
12477
    }
12478
    if usage <> LinearUse::Locate {
12479
        match node.value {
12480
            case ast::NodeValue::Ident(_), ast::NodeValue::ScopeAccess(_),
12481
                 ast::NodeValue::FieldAccess(_), ast::NodeValue::Subscript { .. },
12482
                 ast::NodeValue::Deref(_) => {
12483
                let mut exclusive = usage == LinearUse::Place
12484
                    and not isCellDeref(checker.resolver, node);
12485
                if usage == LinearUse::Consume {
12486
                    if let ty = typeFor(checker.resolver, node) {
12487
                        set exclusive = isExclusiveArgument(ty);
12488
                    }
12489
                }
12490
                if let cellTy = try inferCellPayload(checker.resolver, node) {
12491
                    let case Type::Cell { permission: controlled, .. } = cellTy
12492
                        else panic "checkLinearNode: invalid cell payload";
12493
                    if let permission = controlled {
12494
                        let permissionExclusive = usage == LinearUse::Place;
12495
                        for i in 0..checker.localLen {
12496
                            let loan = checker.locals[i];
12497
                            if let loanPermission = loan.permission;
12498
                                loanPermission.id == permission.id
12499
                                and (permissionExclusive or loan.exclusive)
12500
                            {
12501
                                throw emitError(
12502
                                    checker.resolver, node,
12503
                                    ErrorKind::BorrowConflict(permission.name),
12504
                                );
12505
                            }
12506
                        }
12507
                        let index = findCellAuthority(checker, env, permission)
12508
                            else throw emitError(
12509
                                checker.resolver, node,
12510
                                ErrorKind::CellPermissionRequired(permission.name),
12511
                            );
12512
                        if permissionExclusive and not checker.authorityExclusive[index] {
12513
                            throw emitError(
12514
                                checker.resolver, node,
12515
                                ErrorKind::CellPermissionRequired(permission.name),
12516
                            );
12517
                        }
12518
                    }
12519
                }
12520
                try checkLocalLoans(checker, env, node, exclusive);
12521
            }
12522
            else => {}
12523
        }
12524
    }
12525
    match node.value {
12526
        case ast::NodeValue::Ident(_) => {
12527
            if usage <> LinearUse::Place {
12528
                try checkLinearIdent(checker, env, node);
12529
            }
12530
            if usage == LinearUse::Consume {
12531
                if let ty = typeFor(checker.resolver, node); isExclusiveArgument(ty) {
12532
                    try checkPatternLoan(checker, node);
12533
                }
12534
                try consumeLinearIdent(checker, env, node);
12535
            }
12536
        }
12537
        case ast::NodeValue::ExprStmt(expr) => {
12538
            if let exprTy = typeFor(checker.resolver, expr) {
12539
                if isLinear(exprTy) {
12540
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
12541
                }
12542
            }
12543
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
12544
        }
12545
        case ast::NodeValue::RegionBlock { bindings, body, .. } => {
12546
            let previousRegions = checker.regions;
12547
            let case NodeExtra::Regions(scope) = checker.resolver.nodeData.entries[node.id].extra else {
12548
                if checker.resolver.errors.len > 0 {
12549
                    return;
12550
                }
12551
                panic "checkLinearNode: missing region scope";
12552
            };
12553
            let start = env.len;
12554
            let localStart = checker.localLen;
12555
            let authorityStart = checker.authorityLen;
12556
            let mut reborrowSources: [?*ast::Node; MAX_LINEAR_BINDINGS] =
12557
                [nil; MAX_LINEAR_BINDINGS];
12558
            let mut reborrowPermissions: [?*unsafe types::Region; MAX_LINEAR_BINDINGS] =
12559
                [nil; MAX_LINEAR_BINDINGS];
12560
            let mut reborrowExclusive: [bool; MAX_LINEAR_BINDINGS] =
12561
                [false; MAX_LINEAR_BINDINGS];
12562
            let mut reborrowLen: u32 = 0;
12563
            for bindingNode in bindings {
12564
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
12565
                    else panic "checkLinearNode: invalid borrow binding";
12566
                let case ast::NodeValue::AddressOf(address) = binding.value.value
12567
                    else continue;
12568
                if address.kind == ast::AddressKind::Cell {
12569
                    continue;
12570
                }
12571
                let case ast::NodeValue::Deref(source) = address.target.value
12572
                    else continue;
12573
                let sourceTy = typeFor(checker.resolver, source) else continue;
12574
                let case Type::Pointer {
12575
                    class: types::PointerClass::Region(permission),
12576
                    mutable: true,
12577
                    ..
12578
                } = sourceTy else continue;
12579
                let authorityIndex = findCellAuthority(checker, env, permission)
12580
                    else continue;
12581
                if not checker.authorityExclusive[authorityIndex] {
12582
                    continue;
12583
                }
12584
                assert reborrowLen < MAX_LINEAR_BINDINGS,
12585
                    "checkLinearNode: permission reborrow overflow";
12586
                set reborrowSources[reborrowLen] = binding.value;
12587
                set reborrowPermissions[reborrowLen] = permission;
12588
                set reborrowExclusive[reborrowLen] =
12589
                    address.kind == ast::AddressKind::Mutable;
12590
                set reborrowLen += 1;
12591
            }
12592
            set checker.regions = scope;
12593
            for bindingNode in bindings {
12594
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
12595
                    else panic "checkLinearNode: invalid borrow binding";
12596
                let case ast::NodeValue::AddressOf(address) = binding.value.value
12597
                    else panic "checkLinearNode: invalid borrow source";
12598
                if let cellTy = try inferCellPayload(checker.resolver, address.target) {
12599
                    let case Type::Cell { permission: controlled, .. } = cellTy
12600
                        else panic "checkLinearNode: invalid cell payload";
12601
                    if let permission = controlled {
12602
                        let exclusive = address.kind == ast::AddressKind::Mutable;
12603
                        let mut matched = false;
12604
                        for i in 0..reborrowLen {
12605
                            let candidate = reborrowPermissions[i]
12606
                                else panic "checkLinearNode: missing reborrow permission";
12607
                            if candidate.id == permission.id
12608
                                and reborrowExclusive[i] == exclusive
12609
                            {
12610
                                set matched = true;
12611
                                break;
12612
                            }
12613
                        }
12614
                        if not matched {
12615
                            throw emitError(
12616
                                checker.resolver, binding.value,
12617
                                ErrorKind::CellPermissionRequired(permission.name),
12618
                            );
12619
                        }
12620
                        set checker.payloadAddress = binding.value;
12621
                    }
12622
                }
12623
                for i in 0..reborrowLen {
12624
                    let source = reborrowSources[i]
12625
                        else panic "checkLinearNode: missing authority reborrow";
12626
                    if source == binding.value {
12627
                        set checker.witnessPermission = reborrowPermissions[i];
12628
                        break;
12629
                    }
12630
                }
12631
                try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
12632
                if env.terminated {
12633
                    break;
12634
                }
12635
                try addLinearBinding(checker, env, bindingNode);
12636
                let lexical = ast::borrowBinding(binding);
12637
                try addLocalLoan(checker, env, bindingNode, lexical);
12638
                let symbol = symbolFor(checker.resolver, bindingNode)
12639
                    else panic "checkLinearNode: missing region binding";
12640
                let mut permission = checker.witnessPermission;
12641
                let mut exclusive = address.kind == ast::AddressKind::Mutable;
12642
                if permission == nil {
12643
                    if let case SymbolData::Value {
12644
                        type: Type::Pointer {
12645
                            class: types::PointerClass::Region(region),
12646
                            mutable: true,
12647
                            ..
12648
                        },
12649
                        ..
12650
                    } = symbol.data {
12651
                        set permission = region;
12652
                        set exclusive = true;
12653
                    }
12654
                }
12655
                if let controlled = permission {
12656
                    if checker.authorityLen >= MAX_LINEAR_BINDINGS {
12657
                        throw emitError(checker.resolver, bindingNode, ErrorKind::Internal);
12658
                    }
12659
                    let index = checker.authorityLen;
12660
                    set checker.authorityPermissions[index] = controlled;
12661
                    set checker.authorityBindings[index] = symbol;
12662
                    set checker.authorityExclusive[index] = exclusive;
12663
                    set checker.authorityLen += 1;
12664
                }
12665
                set checker.payloadAddress = nil;
12666
                set checker.witnessPermission = nil;
12667
            }
12668
            try checkLinearBlock(checker, env, body);
12669
            try finishLinearScope(checker, env, start);
12670
            set checker.localLen = localStart;
12671
            set checker.authorityLen = authorityStart;
12672
            set checker.regions = previousRegions;
12673
            compactRegionalLoans(checker, env, previousRegions);
12674
        }
12675
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
12676
        case ast::NodeValue::Let(binding) => {
12677
            let mut isUndefined = false;
12678
            if let case ast::NodeValue::Undef = binding.value.value {
12679
                set isUndefined = true;
12680
            }
12681
            if isUndefined {
12682
                if let bindingTy = typeFor(checker.resolver, binding.ident);
12683
                    isLinear(bindingTy)
12684
                {
12685
                    throw emitError(
12686
                        checker.resolver,
12687
                        binding.value,
12688
                        ErrorKind::LinearUndefined,
12689
                    );
12690
                }
12691
            }
12692
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
12693
            if env.terminated {
12694
                return;
12695
            }
12696
            try addLinearBinding(checker, env, node);
12697
            try addLocalLoan(checker, env, node, binding);
12698
            if let symbol = symbolFor(checker.resolver, node) {
12699
                if let case SymbolData::Value {
12700
                    type: Type::Pointer {
12701
                        class: types::PointerClass::Region(permission),
12702
                        mutable: true,
12703
                        ..
12704
                    },
12705
                    ..
12706
                } = symbol.data {
12707
                    if checker.authorityLen >= MAX_LINEAR_BINDINGS {
12708
                        throw emitError(checker.resolver, node, ErrorKind::Internal);
12709
                    }
12710
                    let index = checker.authorityLen;
12711
                    set checker.authorityPermissions[index] = permission;
12712
                    set checker.authorityBindings[index] = symbol;
12713
                    set checker.authorityExclusive[index] = true;
12714
                    set checker.authorityLen += 1;
12715
                }
12716
            }
12717
            expireAllocationLoans(checker, env, node, binding.value);
12718
            if isUndefined {
12719
                markLinearBindingUnavailable(checker.resolver, env, node);
12720
            }
12721
        }
12722
        case ast::NodeValue::Assign(assign) => {
12723
            if not isCellDeref(checker.resolver, assign.left) {
12724
                try checkPatternLoan(checker, assign.left);
12725
            }
12726
            let mut target: ?u32 = nil;
12727
            let mut targetLinear = false;
12728
            if let leftTy = typeFor(checker.resolver, assign.left) {
12729
                if isMoveOnly(leftTy) {
12730
                    set targetLinear = isLinear(leftTy);
12731
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
12732
                        if let sym = symbolFor(checker.resolver, assign.left) {
12733
                            set target = findLinearBinding(env, sym.id);
12734
                        }
12735
                    }
12736
                    if targetLinear and target == nil {
12737
                        throw emitError(
12738
                            checker.resolver,
12739
                            assign.left,
12740
                            ErrorKind::LinearOverwrite,
12741
                        );
12742
                    }
12743
                }
12744
            }
12745
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
12746
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
12747
            if let index = target {
12748
                if targetLinear and linearBindingAvailable(env, index) {
12749
                    throw emitError(
12750
                        checker.resolver,
12751
                        assign.left,
12752
                        ErrorKind::LinearOverwrite,
12753
                    );
12754
                }
12755
                set env.available |= (1 as u64) << (index as u64);
12756
            }
12757
        }
12758
        case ast::NodeValue::RegionApply { value, .. } =>
12759
            try checkLinearNode(checker, env, value, usage),
12760
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
12761
        case ast::NodeValue::AddressOf(addr) => {
12762
            if addr.kind == ast::AddressKind::Cell {
12763
                if let resultTy = typeFor(checker.resolver, node) {
12764
                    if let case Type::Cell { permission: controlled, .. } = resultTy {
12765
                        if let permission = controlled {
12766
                            let index = findCellAuthority(checker, env, permission)
12767
                                else throw emitError(
12768
                                    checker.resolver, node,
12769
                                    ErrorKind::CellPermissionRequired(permission.name),
12770
                                );
12771
                            if not checker.authorityExclusive[index] {
12772
                                throw emitError(
12773
                                    checker.resolver, node,
12774
                                    ErrorKind::CellPermissionRequired(permission.name),
12775
                                );
12776
                            }
12777
                        }
12778
                    }
12779
                }
12780
            }
12781
            if let cellTy = try inferCellPayload(checker.resolver, addr.target) {
12782
                let case Type::Cell { permission: controlled, .. } = cellTy
12783
                    else panic "checkLinearNode: invalid cell payload address";
12784
                if let permission = controlled {
12785
                    let mut headerValidated = false;
12786
                    if let allowed = checker.payloadAddress; allowed == node {
12787
                        set headerValidated = true;
12788
                    }
12789
                    if not headerValidated {
12790
                        let index = findCellAuthority(checker, env, permission)
12791
                            else throw emitError(
12792
                                checker.resolver, node,
12793
                                ErrorKind::CellPermissionRequired(permission.name),
12794
                            );
12795
                        if addr.kind == ast::AddressKind::Mutable
12796
                            and not checker.authorityExclusive[index]
12797
                        {
12798
                            throw emitError(
12799
                                checker.resolver, node,
12800
                                ErrorKind::CellPermissionRequired(permission.name),
12801
                            );
12802
                        }
12803
                    }
12804
                    let permissionExclusive = addr.kind == ast::AddressKind::Mutable;
12805
                    for i in 0..checker.localLen {
12806
                        let loan = checker.locals[i];
12807
                        if let loanPermission = loan.permission;
12808
                            loanPermission.id == permission.id
12809
                            and (permissionExclusive or loan.exclusive)
12810
                        {
12811
                            throw emitError(
12812
                                checker.resolver, node,
12813
                                ErrorKind::BorrowConflict(permission.name),
12814
                            );
12815
                        }
12816
                    }
12817
                }
12818
            }
12819
            try checkLocalLoans(checker, env, addr.target, ast::isExclusiveAddress(addr));
12820
            if ast::isExclusiveAddress(addr) {
12821
                try checkPatternLoan(checker, addr.target);
12822
            }
12823
            let mut owner: ?*ast::Node = nil;
12824
            if addr.kind == ast::AddressKind::Cell {
12825
                if let ty = typeFor(checker.resolver, node) {
12826
                    if let case Type::Cell { class: types::PointerClass::Owned, .. } = ty {
12827
                        set owner = addressOwner(checker.resolver, addr.target);
12828
                    }
12829
                }
12830
            }
12831
            if let source = owner {
12832
                try checkCellAddressOwner(checker, env, addr.target, source);
12833
            } else {
12834
                try checkLinearNode(checker, env, addr.target, LinearUse::Locate);
12835
            }
12836
            try addRegionalLoan(checker, env, node, addr);
12837
        }
12838
        case ast::NodeValue::Deref(target) => {
12839
            if let resultTy = typeFor(checker.resolver, node) {
12840
                let cellObservation = usage == LinearUse::Observe
12841
                    and (try inferCellPayload(checker.resolver, node)) <> nil;
12842
                if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) {
12843
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
12844
                }
12845
            }
12846
            try checkLinearNode(checker, env, target, LinearUse::Locate);
12847
        }
12848
        case ast::NodeValue::FieldAccess(access) => {
12849
            if let resultTy = typeFor(checker.resolver, node) {
12850
                let cellObservation = usage == LinearUse::Observe
12851
                    and (try inferCellPayload(checker.resolver, node)) <> nil;
12852
                if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) {
12853
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
12854
                }
12855
            }
12856
            try checkLinearNode(checker, env, access.parent, LinearUse::Locate);
12857
        }
12858
        case ast::NodeValue::ScopeAccess(_) => {}
12859
        case ast::NodeValue::Subscript { container, index } => {
12860
            if let resultTy = typeFor(checker.resolver, node) {
12861
                let cellObservation = usage == LinearUse::Observe
12862
                    and (try inferCellPayload(checker.resolver, node)) <> nil;
12863
                if isMoveOnly(resultTy) and (usage == LinearUse::Consume or cellObservation) {
12864
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
12865
                }
12866
            }
12867
            try checkLinearNode(checker, env, container, LinearUse::Locate);
12868
            try checkLinearNode(checker, env, index, LinearUse::Consume);
12869
        }
12870
        case ast::NodeValue::RecordLit(lit) => {
12871
            for fieldNode in lit.fields {
12872
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
12873
                    else panic "checkLinearNode: expected field";
12874
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
12875
            }
12876
        }
12877
        case ast::NodeValue::ArrayLit(items) => {
12878
            for item in items {
12879
                try checkLinearNode(checker, env, item, LinearUse::Consume);
12880
            }
12881
        }
12882
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
12883
            if let itemTy = typeFor(checker.resolver, repeat.item) {
12884
                if not isCopy(itemTy) {
12885
                    throw emitError(
12886
                        checker.resolver,
12887
                        repeat.item,
12888
                        ErrorKind::LinearDiscard,
12889
                    );
12890
                }
12891
            }
12892
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
12893
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
12894
        }
12895
        case ast::NodeValue::BinOp(op) => {
12896
            if op.op == ast::BinaryOp::And or op.op == ast::BinaryOp::Or {
12897
                try checkLinearNode(checker, env, op.left, LinearUse::Consume);
12898
                let skipped = *env;
12899
                let mut evaluated = skipped;
12900
                try checkLinearNode(checker, &mut evaluated, op.right, LinearUse::Consume);
12901
                try joinLinearBranches(checker, env, &skipped, &evaluated, node);
12902
                return;
12903
            }
12904
            let mut operandUse = LinearUse::Consume;
12905
            match op.op {
12906
                case ast::BinaryOp::Eq, ast::BinaryOp::Ne,
12907
                     ast::BinaryOp::Lt, ast::BinaryOp::Gt,
12908
                     ast::BinaryOp::Lte, ast::BinaryOp::Gte =>
12909
                    set operandUse = LinearUse::Observe,
12910
                else => {}
12911
            }
12912
            try checkLinearNode(checker, env, op.left, operandUse);
12913
            try checkLinearNode(checker, env, op.right, operandUse);
12914
        }
12915
        case ast::NodeValue::UnOp(op) => {
12916
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
12917
        }
12918
        case ast::NodeValue::As(expr) => {
12919
            let mut castUse = usage;
12920
            if let targetTy = typeFor(checker.resolver, node) {
12921
                if let case Type::Cell { .. } = targetTy {
12922
                    set castUse = LinearUse::Consume;
12923
                }
12924
            }
12925
            if let targetTy = typeFor(checker.resolver, node); isNumericType(targetTy) {
12926
                set castUse = LinearUse::Observe;
12927
            }
12928
            try checkLinearNode(checker, env, expr.value, castUse);
12929
        }
12930
        case ast::NodeValue::Range(range) => {
12931
            if let start = range.start {
12932
                try checkLinearNode(checker, env, start, LinearUse::Consume);
12933
            }
12934
            if let end = range.end {
12935
                try checkLinearNode(checker, env, end, LinearUse::Consume);
12936
            }
12937
        }
12938
        case ast::NodeValue::BuiltinCall { args, .. } => {
12939
            for arg in args {
12940
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
12941
            }
12942
        }
12943
        case ast::NodeValue::If(conditional) => {
12944
            try checkLinearIf(checker, env, node, conditional);
12945
        }
12946
        case ast::NodeValue::CondExpr(conditional) => {
12947
            try checkLinearCondExpr(checker, env, node, conditional, usage);
12948
        }
12949
        case ast::NodeValue::IfLet(conditional) => {
12950
            try checkLinearIfLet(checker, env, node, conditional);
12951
        }
12952
        case ast::NodeValue::LetElse(binding) => {
12953
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
12954
                isLinear(subjectTy)
12955
            {
12956
                throw emitError(
12957
                    checker.resolver,
12958
                    binding.pattern.scrutinee,
12959
                    ErrorKind::LinearPartialMove,
12960
                );
12961
            }
12962
            try checkLinearNode(
12963
                checker,
12964
                env,
12965
                binding.pattern.scrutinee,
12966
                patternSubjectUse(checker.resolver, binding.pattern.scrutinee),
12967
            );
12968
            let base = *env;
12969
            let mut guardedEnv = base;
12970
            try addLinearPatternBindings(checker, &mut guardedEnv, binding.pattern.pattern);
12971
            expireAllocationLoans(checker, &mut guardedEnv, binding.pattern.pattern, binding.pattern.scrutinee);
12972
            if let guard = binding.pattern.guard {
12973
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
12974
            }
12975
            let mut successEnv = guardedEnv;
12976
            let mut fallbackEnv = base;
12977
            try checkLinearNode(
12978
                checker,
12979
                &mut fallbackEnv,
12980
                binding.elseBranch,
12981
                LinearUse::Consume,
12982
            );
12983
            if binding.pattern.guard <> nil {
12984
                let mut guardFallbackEnv = guardedEnv;
12985
                try checkLinearNode(
12986
                    checker,
12987
                    &mut guardFallbackEnv,
12988
                    binding.elseBranch,
12989
                    LinearUse::Consume,
12990
                );
12991
                try finishLinearScope(checker, &mut guardFallbackEnv, base.len);
12992
                let previous = fallbackEnv;
12993
                try joinLinearBranches(
12994
                    checker,
12995
                    &mut fallbackEnv,
12996
                    &previous,
12997
                    &guardFallbackEnv,
12998
                    binding.elseBranch,
12999
                );
13000
            }
13001
            if let case ast::PatternKind::Binding = binding.pattern.kind {
13002
                try addLinearPatternBindings(
13003
                    checker,
13004
                    &mut fallbackEnv,
13005
                    binding.pattern.pattern,
13006
                );
13007
            }
13008
            try joinLinearBranches(checker, env, &successEnv, &fallbackEnv, node);
13009
        }
13010
        case ast::NodeValue::Match(matchExpr) => {
13011
            try checkLinearMatch(checker, env, node, matchExpr);
13012
        }
13013
        case ast::NodeValue::Try(tryExpr) => {
13014
            try checkLinearNode(checker, env, tryExpr.expr, usage);
13015
            let success = *env;
13016
            if let resultTy = typeFor(checker.resolver, tryExpr.expr); resultTy == Type::Never {
13017
                if not tryExpr.returnsOptional and (tryExpr.catches.len > 0 or tryExpr.shouldPanic) {
13018
                    set env.terminated = true;
13019
                }
13020
            }
13021
            for catchNode in tryExpr.catches {
13022
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
13023
                    else panic "checkLinearNode: expected catch";
13024
                let mut branch = success;
13025
                let start = branch.len;
13026
                if let binding = catchClause.binding {
13027
                    try addLinearBinding(checker, &mut branch, binding);
13028
                }
13029
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
13030
                try finishLinearScope(checker, &mut branch, start);
13031
                let previous = *env;
13032
                try joinLinearBranches(checker, env, &previous, &branch, node);
13033
            }
13034
        }
13035
        case ast::NodeValue::While(_), ast::NodeValue::WhileLet(_),
13036
             ast::NodeValue::For(_), ast::NodeValue::Loop { .. } =>
13037
            try checkLinearLoop(checker, env, node),
13038
        case ast::NodeValue::Break => {
13039
            assert checker.loopDepth > 0, "linear loop control outside loop";
13040
            let start = checker.loopMarks[checker.loopDepth - 1];
13041
            try finishLinearScope(checker, env, start);
13042
            try checkLinearLoopBreak(checker, env, node);
13043
            set env.terminated = true;
13044
        }
13045
        case ast::NodeValue::Continue => {
13046
            assert checker.loopDepth > 0, "linear loop control outside loop";
13047
            let start = checker.loopMarks[checker.loopDepth - 1];
13048
            try finishLinearScope(checker, env, start);
13049
            try checkLinearLoopBackEdge(checker, env, node);
13050
            set env.terminated = true;
13051
        }
13052
        case ast::NodeValue::Return { value } => {
13053
            if let expr = value {
13054
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
13055
            }
13056
            try finishLinearExit(checker, env);
13057
        }
13058
        case ast::NodeValue::Throw { expr } => {
13059
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
13060
            try finishLinearExit(checker, env);
13061
        }
13062
        case ast::NodeValue::Panic { message } => {
13063
            if let expr = message {
13064
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
13065
            }
13066
            set env.terminated = true;
13067
        }
13068
        case ast::NodeValue::Assert { condition, message } => {
13069
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
13070
            if let expr = message {
13071
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
13072
            }
13073
        }
13074
        else => {}
13075
    }
13076
}
13077
13078
/// Check exact-use ownership for one resolved function.
13079
unsafe fn checkLinearFn 'arena (
13080
    self: &mut Resolver 'arena,
13081
    receiver: ?*ast::Node,
13082
    params: *[*ast::Node],
13083
    body: *ast::Node,
13084
) throws (ResolveError) {
13085
    try resolveNominalApplications(self, body);
13086
    let regions = self.regionScope;
13087
    let resolved: 'checking = &mut *self where 'arena: 'checking in {
13088
        let mut checker = linearChecker(resolved, regions);
13089
        let mut env = linearEnv();
13090
        if let receiverNode = receiver {
13091
            try addLinearBinding(&mut checker, &mut env, receiverNode);
13092
            if let symbol = symbolFor(checker.resolver, receiverNode) {
13093
                if let case SymbolData::Value {
13094
                    type: Type::Pointer {
13095
                        class: types::PointerClass::Region(permission),
13096
                        mutable: true,
13097
                        ..
13098
                    },
13099
                    ..
13100
                } = symbol.data {
13101
                    let index = checker.authorityLen;
13102
                    set checker.authorityPermissions[index] = permission;
13103
                    set checker.authorityBindings[index] = symbol;
13104
                    set checker.authorityExclusive[index] = true;
13105
                    set checker.authorityLen += 1;
13106
                }
13107
            }
13108
        }
13109
        for paramNode in params {
13110
            let case ast::NodeValue::FnParam(_) = paramNode.value
13111
                else panic "checkLinearFn: expected parameter";
13112
            try addLinearBinding(&mut checker, &mut env, paramNode);
13113
            if let symbol = symbolFor(checker.resolver, paramNode) {
13114
                if let case SymbolData::Value {
13115
                    type: Type::Pointer {
13116
                        class: types::PointerClass::Region(permission),
13117
                        mutable: true,
13118
                        ..
13119
                    },
13120
                    ..
13121
                } = symbol.data {
13122
                    if checker.authorityLen >= MAX_LINEAR_BINDINGS {
13123
                        throw emitError(checker.resolver, paramNode, ErrorKind::Internal);
13124
                    }
13125
                    let index = checker.authorityLen;
13126
                    set checker.authorityPermissions[index] = permission;
13127
                    set checker.authorityBindings[index] = symbol;
13128
                    set checker.authorityExclusive[index] = true;
13129
                    set checker.authorityLen += 1;
13130
                }
13131
            }
13132
        }
13133
        try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
13134
        try finishLinearScope(&mut checker, &mut env, 0);
13135
    }
13136
}
13137
13138
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
13139
unsafe fn resolveModuleDefs 'arena (self: &mut Resolver 'arena, block: &ast::Block) throws (ResolveError) {
13140
    for stmt in block.statements {
13141
        try resolveNominalApplications(self, stmt);
13142
        try visitDef(self, stmt);
13143
        try resolveNominalApplications(self, stmt);
13144
    }
13145
}
13146
13147
/// Resolve all packages.
13148
/// Module entries retain their identity throughout resolution and diagnostics.
13149
export unsafe fn resolve 'arena 'permission (self: &mut Resolver 'arena, graph: &module::ModuleGraph 'permission, packages: &[Pkg]) -> Diagnostics throws (ResolveError) {
13150
    assert module::entryCount(graph) <= self.moduleEntries.len, "resolve: module registry capacity exceeded";
13151
    for i in 0..self.moduleEntries.len {
13152
        let entry = module::get(graph, i as u16);
13153
        set self.moduleEntries[i] = entry;
13154
        if let present = entry {
13155
            set self.moduleRoots[i] = module::astFor(graph, present);
13156
        } else {
13157
            set self.moduleRoots[i] = nil;
13158
        }
13159
    }
13160
13161
    // 1. Bind all package roots to enable cross-package references.
13162
    for i in 0..packages.len {
13163
        let pkg = packages[i];
13164
        // Enter a new scope for the module.
13165
        let enter = enterModuleScope(self, pkg.rootAst, pkg.rootEntry);
13166
        // Bind the package root module name in the global package scope.
13167
        let scope = self.pkgScope;
13168
        try bindModuleIdent(self, pkg.rootEntry, enter.newScope, pkg.rootAst, 0, scope);
13169
13170
        exitModuleScope(self, enter);
13171
    }
13172
    // 2. Resolve each package's contents.
13173
    for i in 0..packages.len {
13174
        let pkg = packages[i];
13175
        let diags = try resolvePackage(self, pkg.rootEntry, pkg.rootAst);
13176
        if not success(&diags) {
13177
            return diags;
13178
        }
13179
    }
13180
    return diagnostics(self);
13181
}
13182
13183
/// Resolve a package.
13184
unsafe fn resolvePackage 'arena (self: &mut Resolver 'arena, rootEntry: *module::ModuleEntry, node: *ast::Node) -> Diagnostics throws (ResolveError) {
13185
    let rootId = rootEntry.id;
13186
    let scope = self.moduleScopes[rootId as u32]
13187
        else panic "resolvePackage: module scope not found";
13188
13189
    // Set up the module scope for this package.
13190
    set self.scope = scope;
13191
    set self.currentMod = rootId;
13192
13193
    let case ast::NodeValue::Block(block) = node.value
13194
        else panic "resolvePackage: expected block for module root";
13195
13196
    // Module graph analysis phase: bind all module name symbols and scopes.
13197
    try resolveModuleGraph(self, &block) catch {
13198
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
13199
        return diagnostics(self);
13200
    };
13201
13202
    // Declaration phase: bind all names and analyze top-level declarations.
13203
    try resolveModuleDecls(self, &block) catch {
13204
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
13205
    };
13206
    if self.errors.len > 0 {
13207
        return diagnostics(self);
13208
    }
13209
13210
    // Definition phase: analyze function bodies and sub-module definitions.
13211
    try resolveModuleDefs(self, &block) catch {
13212
        assert self.errors.len > 0, "resolvePackage: failure should have diagnostics";
13213
    };
13214
    setNodeType(self, node, Type::Void);
13215
13216
    return diagnostics(self);
13217
}