lang: Prepare linear ownership

d45ecf680cc1334a65f235c26e2f358d20ae5b77156d8731d90c11a04e8f0b66
Alexis Sellier committed ago 1 parent 8ed9e8cc
compiler/radiance.rad +2 -2
37 37
38 38
/// AST arena size (32 MB) - retains parsed nodes throughout compilation.
39 39
constant TEMP_ARENA_SIZE: u32 = 33554432;
40 40
/// Per-function lowering and register-allocation arena size (16 MB).
41 41
constant FN_ARENA_SIZE: u32 = 16777216;
42 -
/// Main arena size (64 MB) - lives throughout compilation.
42 +
/// Main arena size (96 MB) - lives throughout compilation.
43 43
/// Used for: resolver data, types, symbols, global IL data, and codegen output.
44 -
constant MAIN_ARENA_SIZE: u32 = 67108864;
44 +
constant MAIN_ARENA_SIZE: u32 = 100663296;
45 45
46 46
/// AST storage arena.
47 47
static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = undefined;
48 48
/// Scratch storage reclaimed after each generated function.
49 49
static FN_ARENA: [u8; FN_ARENA_SIZE] = undefined;
lib/std/lang/resolver.rad +1665 -279
45 45
export constant MAX_LOOP_DEPTH: u32 = 16;
46 46
/// Maximum trait instances.
47 47
export constant MAX_INSTANCES: u32 = 128;
48 48
/// Maximum standalone methods (across all types).
49 49
export constant MAX_METHODS: u32 = 256;
50 +
/// Maximum number of linear bindings active in one function.
51 +
constant MAX_LINEAR_BINDINGS: u32 = 32;
52 +
/// Maximum nesting depth tracked for loops.
53 +
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
50 54
51 55
/// Trait definition stored in the resolver.
52 56
export record TraitType {
53 57
    /// Trait name.
54 58
    name: *[u8],
64 68
    name: *[u8],
65 69
    /// Function type for the method, excluding the receiver.
66 70
    fnType: *FnType,
67 71
    /// Whether the receiver is mutable.
68 72
    mutable: bool,
73 +
    /// Pointer-like class used by the receiver.
74 +
    receiverClass: types::PointerClass,
69 75
    /// V-table slot index.
70 76
    index: u32,
71 77
}
72 78
73 79
/// An entry in the trait instance registry.
94 100
    name: *[u8],
95 101
    /// Function type excluding the receiver.
96 102
    fnType: *FnType,
97 103
    /// Whether the receiver is mutable.
98 104
    mutable: bool,
105 +
    /// Pointer-like class used by the receiver.
106 +
    receiverClass: types::PointerClass,
99 107
    /// Symbol for the method.
100 108
    symbol: *mut Symbol,
101 109
}
102 110
103 111
/// Identifier for the synthetic `len` field.
160 168
export record RecordType {
161 169
    fields: *[RecordField],
162 170
    labeled: bool,
163 171
    /// Cached layout.
164 172
    layout: Layout,
173 +
    /// Whether the declaration explicitly carries the `Linear` marker.
174 +
    declaredLinear: bool,
165 175
}
166 176
167 177
/// Union nominal type.
168 178
export record UnionType {
169 179
    variants: *[UnionVariant],
171 181
    layout: Layout,
172 182
    /// Cached payload offset within the union aggregate.
173 183
    valOffset: u32,
174 184
    /// If all variants have void payloads.
175 185
    isAllVoid: bool,
186 +
    /// Whether the declaration explicitly carries the `Linear` marker.
187 +
    declaredLinear: bool,
176 188
}
177 189
178 190
/// Metadata for user-defined types.
179 191
export union NominalType {
180 192
    /// Placeholder for a type that hasn't been fully resolved yet.
259 271
/// Resolved function signature details.
260 272
export record FnType {
261 273
    paramTypes: *[*Type],
262 274
    returnType: *Type,
263 275
    throwList: *[*Type],
276 +
    /// Whether calling this function requires an unsafe context.
277 +
    isUnsafe: bool,
264 278
    localCount: u32,
265 279
}
266 280
267 281
/// Describes a type computed during semantic analysis.
268 282
export union Type {
277 291
    /// Range types, eg. `start..end`.
278 292
    Range {
279 293
        start: ?*Type,
280 294
        end: ?*Type,
281 295
    },
282 -
    /// Pointer-like address.
296 +
    /// Owning pointer-like address.
283 297
    Pointer {
284 298
        class: types::PointerClass,
285 299
        target: *Type,
286 300
        mutable: bool,
287 301
    },
288 -
    /// Pointer-like slice.
302 +
    /// Owning slice.
289 303
    Slice {
290 304
        class: types::PointerClass,
291 305
        item: *Type,
292 306
        mutable: bool,
293 307
    },
297 311
    Optional(*Type),
298 312
    /// Eg. `fn id(i32) -> i32`.
299 313
    Fn(*FnType),
300 314
    /// Named, ie. user-defined types, includes union variants.
301 315
    Nominal(*NominalType),
302 -
    /// An erased pointer-like type with a v-table.
316 +
    /// Owning trait object. An erased type with v-table.
303 317
    TraitObject {
304 318
        /// Ownership and safety class.
305 319
        class: types::PointerClass,
306 320
        /// Trait definition.
307 321
        traitInfo: *TraitType,
569 583
    MissingTraitMethod(*[u8]),
570 584
    /// Trait name used as a value expression.
571 585
    UnexpectedTraitName,
572 586
    /// Trait method receiver does not point to the declaring trait.
573 587
    TraitReceiverMismatch,
588 +
    /// Trait declaration and instance disagree about unsafe call requirements.
589 +
    TraitMethodSafetyMismatch,
574 590
    /// Function declaration has too many parameters.
575 591
    FnParamOverflow(CountMismatch),
576 592
    /// Function declaration has too many throws.
577 593
    FnThrowOverflow(CountMismatch),
578 594
    /// Trait declaration has too many methods.
579 595
    TraitMethodOverflow(CountMismatch),
580 596
    /// Instance declaration is missing a required supertrait instance.
581 597
    MissingSupertraitInstance(*[u8]),
598 +
    /// Linear binding was consumed more than once.
599 +
    LinearUseAfterConsume(*[u8]),
600 +
    /// Linear binding remains available at an exit.
601 +
    LinearNotConsumed(*[u8]),
602 +
    /// A case-pattern `let-else` fallback must terminate control flow.
603 +
    LinearLetElseMustTerminate,
604 +
    /// Branches disagree about a linear binding's state.
605 +
    LinearBranchMismatch(*[u8]),
606 +
    /// A linear field cannot be moved independently.
607 +
    LinearPartialMove,
608 +
    /// A linear value cannot be discarded.
609 +
    LinearDiscard,
610 +
    /// Assignment would overwrite a live linear value.
611 +
    LinearOverwrite,
612 +
    /// `undefined` cannot initialize a linear type.
613 +
    LinearUndefined,
614 +
    /// A reference appears in a storable or escaping position.
615 +
    InvalidRefPosition,
616 +
    /// A reference cannot be bound to a local.
617 +
    RefBinding,
618 +
    /// Call arguments contain overlapping incompatible loans.
619 +
    BorrowConflict(*[u8]),
620 +
    /// Unsafe pointer operation outside an `unsafe` declaration.
621 +
    UnsafeOperation,
622 +
    /// Safe code cannot call an `unsafe` function.
623 +
    UnsafeCall,
582 624
    /// Internal error.
583 625
    Internal,
584 626
}
585 627
586 628
/// Diagnostics returned by the analyzer.
728 770
    effectiveTy: Type,
729 771
    /// How bindings should be created.
730 772
    by: MatchBy,
731 773
}
732 774
775 +
/// How an expression uses a linear result.
776 +
union LinearUse {
777 +
    /// Consume the value and end its availability.
778 +
    Consume,
779 +
    /// Read the value without consuming it.
780 +
    Observe,
781 +
    /// Borrow the value through a reference.
782 +
    Borrow,
783 +
    /// Discard an unused expression result.
784 +
    Discard,
785 +
    /// Use the value as an assignment target.
786 +
    Place,
787 +
}
788 +
789 +
/// Per-control-flow-path ownership state.
790 +
record LinearEnv {
791 +
    /// Symbols tracked on this control-flow path.
792 +
    symbols: [?*mut Symbol; MAX_LINEAR_BINDINGS],
793 +
    /// Bit set for each binding that remains available.
794 +
    available: u64,
795 +
    /// Number of entries in `symbols`.
796 +
    len: u32,
797 +
    /// Whether this control-flow path has terminated.
798 +
    terminated: bool,
799 +
}
800 +
801 +
/// Function-local exact-use checker state.
802 +
record LinearChecker {
803 +
    /// Resolver that owns the symbols and diagnostics.
804 +
    resolver: *mut Resolver,
805 +
    /// Binding count at entry to each active loop.
806 +
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
807 +
    /// Available bindings at entry to each active loop.
808 +
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
809 +
    /// Available bindings shared by the exits from each active loop.
810 +
    loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
811 +
    /// Whether each active loop can exit without `break`.
812 +
    loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH],
813 +
    /// Whether each active loop contains a reachable `break`.
814 +
    loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH],
815 +
    /// Number of active loops.
816 +
    loopDepth: u32,
817 +
}
818 +
733 819
/// Unwrap a pointer type for pattern matching.
734 820
export fn unwrapMatchSubject(ty: Type) -> MatchSubject {
735 821
    if let case Type::Pointer { target, mutable, .. } = ty {
736 822
        let by = MatchBy::MutRef if mutable else MatchBy::Ref;
737 823
        return MatchSubject { effectiveTy: *target, by };
751 837
    loopDepth: u32,
752 838
    /// Signature of the function currently being analyzed.
753 839
    currentFn: ?*FnType,
754 840
    /// Current module being analyzed.
755 841
    currentMod: u16,
842 +
    /// Nesting depth of unsafe modules and function bodies.
843 +
    unsafeDepth: u32,
756 844
    /// Configuration for semantic analysis.
757 845
    config: Config,
758 846
    /// Unified arena for symbols, scopes, and nominal type.
759 847
    arena: alloc::Arena,
760 848
    /// Combined semantic metadata table indexed by node ID.
908 996
        pkgScope: storage.pkgScope,
909 997
        loopStack: undefined,
910 998
        loopDepth: 0,
911 999
        currentFn: nil,
912 1000
        currentMod: 0,
1001 +
        unsafeDepth: 0,
913 1002
        config,
914 1003
        arena,
915 1004
        nodeData: NodeDataTable { entries: storage.nodeData },
916 1005
        types: nil,
917 1006
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1350 1439
}
1351 1440
1352 1441
/// Get the layout of a type.
1353 1442
export fn getTypeLayout(ty: Type) -> Layout {
1354 1443
    match ty {
1444 +
        case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1445 +
        case Type::Slice { .. }, Type::TraitObject { .. } =>
1446 +
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1355 1447
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
1356 1448
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
1357 1449
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
1358 1450
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
1359 1451
        case Type::Int => return Layout { size: 8, alignment: 8 },
1360 1452
        case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 },
1361 -
        case Type::Pointer { .. },
1362 -
             Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1363 -
        case Type::Slice { .. },
1364 -
             Type::TraitObject { .. } => return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1453 +
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1365 1454
        case Type::Array(arr) => return getArrayLayout(arr),
1366 1455
        case Type::Optional(inner) => return getOptionalLayout(*inner),
1367 1456
        case Type::Nominal(info) => return getNominalLayout(*info),
1368 1457
        else => {
1369 1458
            panic "getTypeLayout: the given type cannot be layed out";
1528 1617
    return unionType.isAllVoid;
1529 1618
}
1530 1619
1531 1620
/// Check if a type should be treated as an address-like value.
1532 1621
fn isAddressType(ty: Type) -> bool {
1622 +
    if isNullableType(ty) {
1623 +
        return true;
1624 +
    }
1533 1625
    match ty {
1534 -
        case Type::Pointer { .. }, Type::Slice { .. }, Type::Fn(_) => return true,
1626 +
        case Type::Fn(_) => return true,
1535 1627
        else => return false,
1536 1628
    }
1537 1629
}
1538 1630
1539 1631
/// Return the representable range for an integer type.
1602 1694
1603 1695
/// Ensure all nested nominal types in a type are resolved.
1604 1696
fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
1605 1697
    match ty {
1606 1698
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1607 -
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1608 1699
        case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site),
1609 1700
        case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse.
1701 +
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1610 1702
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
1611 1703
        else => {},
1612 1704
    }
1613 1705
}
1614 1706
1659 1751
        }
1660 1752
    }
1661 1753
    return true;
1662 1754
}
1663 1755
1756 +
/// Return whether pointer classes are compatible.
1757 +
fn pointerClassesAssignable(
1758 +
    to: types::PointerClass,
1759 +
    from: types::PointerClass,
1760 +
) -> bool {
1761 +
    return to == from or (
1762 +
        to == types::PointerClass::Owned
1763 +
        and from == types::PointerClass::Ref
1764 +
    );
1765 +
}
1766 +
1664 1767
/// Check if the `from` type is assignable to the `to` type, and return a
1665 1768
/// coercion plan if so.
1666 1769
fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
1667 1770
    if to == Type::Unknown or from == Type::Unknown {
1668 1771
        return nil;
1678 1781
        return Coercion::Identity;
1679 1782
    }
1680 1783
    if to == from {
1681 1784
        return Coercion::Identity;
1682 1785
    }
1786 +
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
1787 +
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
1788 +
            else return nil;
1789 +
        if not pointerClassesAssignable(lhsClass, rhsClass) {
1790 +
            return nil;
1791 +
        }
1792 +
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
1793 +
        if *lhsTarget == Type::Opaque {
1794 +
            if lhsMutable and not rhsMutable {
1795 +
                return nil;
1796 +
            }
1797 +
            return Coercion::Identity;
1798 +
        }
1799 +
        if lhsMutable and not rhsMutable {
1800 +
            return nil;
1801 +
        }
1802 +
        return isAssignable(self, *lhsTarget, *rhsTarget, rval);
1803 +
    }
1804 +
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
1805 +
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
1806 +
            if not pointerClassesAssignable(lhsClass, rhsClass)
1807 +
                or (lhsMutable and not rhsMutable)
1808 +
            {
1809 +
                return nil;
1810 +
            }
1811 +
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
1812 +
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
1813 +
            }
1814 +
        }
1815 +
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
1816 +
            if not pointerClassesAssignable(lhsClass, rhsClass)
1817 +
                or lhsTraitInfo <> rhsTraitInfo
1818 +
            {
1819 +
                return nil;
1820 +
            }
1821 +
            if lhsMutable and not rhsMutable {
1822 +
                return nil;
1823 +
            }
1824 +
            return Coercion::Identity;
1825 +
        }
1826 +
        return nil;
1827 +
    }
1828 +
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
1829 +
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
1830 +
            else return nil;
1831 +
        if not pointerClassesAssignable(lhsClass, rhsClass)
1832 +
            or (lhsMutable and not rhsMutable)
1833 +
        {
1834 +
            return nil;
1835 +
        }
1836 +
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
1837 +
        if *lhsItem == Type::Opaque {
1838 +
            return Coercion::Identity;
1839 +
        }
1840 +
        return isAssignable(self, *lhsItem, *rhsItem, rval);
1841 +
    }
1683 1842
    match to {
1684 1843
        case Type::Array(lhs) => {
1685 1844
            let case Type::Array(rhs) = from
1686 1845
                else return nil;
1687 1846
1712 1871
                    }
1713 1872
                    return nil;
1714 1873
                }
1715 1874
            }
1716 1875
        }
1717 -
        case Type::Pointer { target: lhsTarget, mutable: lhsMutable, .. } => {
1718 -
            let case Type::Pointer { target: rhsTarget, mutable: rhsMutable, .. } = from
1719 -
                else return nil;
1720 1876
1721 -
            // Allow coercion from `*T` to `*opaque`, and `*mut T` to `*mut opaque`.
1722 -
            if *lhsTarget == Type::Opaque {
1723 -
                if lhsMutable and not rhsMutable {
1724 -
                    return nil;
1725 -
                }
1726 -
                return Coercion::Identity;
1727 -
            }
1728 -
            if lhsMutable and not rhsMutable {
1729 -
                return nil;
1730 -
            }
1731 -
            return isAssignable(self, *lhsTarget, *rhsTarget, rval);
1732 -
        }
1733 1877
        case Type::Optional(inner) => {
1734 1878
            if from == Type::Nil {
1735 1879
                return Coercion::OptionalLift(to);
1736 1880
            }
1737 1881
            if let _ = isAssignable(self, *inner, from, rval) {
1740 1884
            if let case Type::Optional(fromInner) = from {
1741 1885
                return isAssignable(self, *inner, *fromInner, rval);
1742 1886
            }
1743 1887
            return nil;
1744 1888
        }
1745 -
        case Type::TraitObject {
1746 -
            class: lhsClass, traitInfo, mutable: lhsMutable,
1747 -
        } => {
1748 -
            // Coerce `*T` or `*mut T` where `T` implements the trait.
1749 -
            if let case Type::Pointer { target, mutable: rhsMutable, .. } = from {
1750 -
                if lhsMutable and not rhsMutable {
1751 -
                    return nil;
1752 -
                }
1753 -
                // Look up instance registry.
1754 -
                if let inst = findInstance(self, traitInfo, *target) {
1755 -
                    return Coercion::TraitObject { traitInfo, inst };
1756 -
                }
1757 -
            }
1758 -
            // Identity: same trait object.
1759 -
            if let case Type::TraitObject {
1760 -
                class: rhsClass, traitInfo: rhsTrait, mutable: rhsMutable,
1761 -
            } = from {
1762 -
                if lhsClass <> rhsClass or traitInfo <> rhsTrait {
1763 -
                    return nil;
1764 -
                }
1765 -
                if lhsMutable and not rhsMutable {
1766 -
                    return nil;
1767 -
                }
1768 -
                return Coercion::Identity;
1769 -
            }
1770 -
            return nil;
1771 -
        }
1772 -
        case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } => {
1773 -
            match from {
1774 -
                case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } => {
1775 -
                    if lhsClass <> rhsClass {
1776 -
                        return nil;
1777 -
                    }
1778 -
                    if lhsMutable and not rhsMutable {
1779 -
                        return nil;
1780 -
                    }
1781 -
                    // Allow coercion from `*[T]` to `*[opaque]`, and `*mut [T]` to `*mut [opaque]`.
1782 -
                    if *lhsItem == Type::Opaque {
1783 -
                        return Coercion::Identity;
1784 -
                    }
1785 -
                    return isAssignable(self, *lhsItem, *rhsItem, rval);
1786 -
                }
1787 -
                else => return nil,
1788 -
            }
1789 -
        }
1889 +
1790 1890
        case Type::Fn(toInfo) => {
1791 1891
            // Allow function type structural matching.
1792 1892
            if let case Type::Fn(fromInfo) = from {
1793 1893
                if fnTypeEqual(toInfo, fromInfo) {
1794 1894
                    return Coercion::Identity;
1829 1929
    return nil;
1830 1930
}
1831 1931
1832 1932
/// Check if two function type descriptors are structurally equivalent.
1833 1933
fn fnTypeEqual(a: *FnType, b: *FnType) -> bool {
1934 +
    if a.isUnsafe <> b.isUnsafe {
1935 +
        return false;
1936 +
    }
1834 1937
    if a.paramTypes.len <> b.paramTypes.len {
1835 1938
        return false;
1836 1939
    }
1837 1940
    if a.throwList.len <> b.throwList.len {
1838 1941
        return false;
1856 1959
/// Check if two types are structurally equal.
1857 1960
export fn typesEqual(a: Type, b: Type) -> bool {
1858 1961
    if a == b {
1859 1962
        return true;
1860 1963
    }
1964 +
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
1965 +
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
1966 +
            else return false;
1967 +
        return aClass == bClass and aMutable == bMutable
1968 +
            and typesEqual(*aTarget, *bTarget);
1969 +
    }
1970 +
    if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a {
1971 +
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
1972 +
            else return false;
1973 +
        return aClass == bClass and aMutable == bMutable
1974 +
            and typesEqual(*aItem, *bItem);
1975 +
    }
1976 +
    if let case Type::TraitObject { class: aClass, traitInfo: aTraitInfo, mutable: aMutable } = a {
1977 +
        let case Type::TraitObject { class: bClass, traitInfo: bTraitInfo, mutable: bMutable } = b
1978 +
            else return false;
1979 +
        return aClass == bClass and aMutable == bMutable
1980 +
            and aTraitInfo == bTraitInfo;
1981 +
    }
1861 1982
    match a {
1862 -
        case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } => {
1863 -
            let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
1864 -
                else return false;
1865 -
            return aClass == bClass and aMutable == bMutable and typesEqual(*aTarget, *bTarget);
1866 -
        }
1867 -
        case Type::Slice { class: aClass, item: aItem, mutable: aMutable } => {
1868 -
            let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
1869 -
                else return false;
1870 -
            return aClass == bClass and aMutable == bMutable and typesEqual(*aItem, *bItem);
1871 -
        }
1872 1983
        case Type::Array(aa) => {
1873 1984
            let case Type::Array(ab) = b else return false;
1874 1985
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
1875 1986
        }
1876 1987
        case Type::Optional(oa) => {
1883 1994
        }
1884 1995
        else => return false,
1885 1996
    }
1886 1997
}
1887 1998
1999 +
/// Return whether `ty` is a direct reference.
2000 +
export fn isRefType(ty: Type) -> bool {
2001 +
    match ty {
2002 +
        case Type::Pointer { class: types::PointerClass::Ref, .. },
2003 +
             Type::Slice { class: types::PointerClass::Ref, .. },
2004 +
             Type::TraitObject { class: types::PointerClass::Ref, .. } => return true,
2005 +
        else => return false,
2006 +
    }
2007 +
}
2008 +
2009 +
/// Return whether a type contains a reference.
2010 +
fn containsRef(ty: Type) -> bool {
2011 +
    if isRefType(ty) {
2012 +
        return true;
2013 +
    }
2014 +
    if let case Type::Pointer { target, .. } = ty {
2015 +
        return containsRef(*target);
2016 +
    }
2017 +
    if let case Type::Slice { item, .. } = ty {
2018 +
        return containsRef(*item);
2019 +
    }
2020 +
    match ty {
2021 +
        case Type::Array(array) => return containsRef(*array.item),
2022 +
        case Type::Optional(inner) => return containsRef(*inner),
2023 +
        // Nominal declarations validate their own fields and variants.
2024 +
        // Treating them as leaves also terminates recursive pointer types.
2025 +
        case Type::Nominal(_) => return false,
2026 +
        else => return false,
2027 +
    }
2028 +
}
2029 +
2030 +
/// Return whether a type is exact-linear.
2031 +
export fn isLinear(ty: Type) -> bool {
2032 +
    match ty {
2033 +
        case Type::Pointer { class: types::PointerClass::Owned, .. },
2034 +
             Type::Slice { class: types::PointerClass::Owned, .. },
2035 +
             Type::TraitObject { class: types::PointerClass::Owned, .. } => return true,
2036 +
        case Type::Pointer { class: types::PointerClass::Ref, .. },
2037 +
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
2038 +
             Type::Slice { class: types::PointerClass::Ref, .. },
2039 +
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2040 +
             Type::TraitObject { class: types::PointerClass::Ref, .. },
2041 +
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return false,
2042 +
2043 +
        case Type::Array(array) => return isLinear(*array.item),
2044 +
        case Type::Optional(inner) => return isLinear(*inner),
2045 +
        case Type::Nominal(NominalType::Record(recInfo)) => {
2046 +
            if recInfo.declaredLinear {
2047 +
                return true;
2048 +
            }
2049 +
            for field in recInfo.fields {
2050 +
                if isLinear(field.fieldType) {
2051 +
                    return true;
2052 +
                }
2053 +
            }
2054 +
            return false;
2055 +
        }
2056 +
        case Type::Nominal(NominalType::Union(unionType)) => {
2057 +
            if unionType.declaredLinear {
2058 +
                return true;
2059 +
            }
2060 +
            for variant in unionType.variants {
2061 +
                if isLinear(variant.valueType) {
2062 +
                    return true;
2063 +
                }
2064 +
            }
2065 +
            return false;
2066 +
        }
2067 +
        else => return false,
2068 +
    }
2069 +
}
2070 +
2071 +
/// Return whether `ty` is a direct unsafe pointer-like value.
2072 +
fn isUnsafePointerType(ty: Type) -> bool {
2073 +
    match ty {
2074 +
        case Type::Pointer { class: types::PointerClass::Unsafe, .. },
2075 +
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2076 +
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
2077 +
        else => return false,
2078 +
    }
2079 +
}
2080 +
1888 2081
/// Get the record info from a record type.
1889 2082
export fn getRecord(ty: Type) -> ?RecordType {
1890 2083
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
1891 2084
    return recInfo;
1892 2085
}
1899 2092
    return ty;
1900 2093
}
1901 2094
1902 2095
/// Get field info for a record-like type (records, slices) by field index.
1903 2096
export fn getRecordField(ty: Type, index: u32) -> ?RecordField {
1904 -
    match ty {
1905 -
        case Type::Nominal(NominalType::Record(recInfo)) => {
1906 -
            if index >= recInfo.fields.len {
1907 -
                return nil;
1908 -
            }
1909 -
            return recInfo.fields[index];
1910 -
        }
1911 -
        case Type::Slice { item, mutable, .. } => {
1912 -
            match index {
1913 -
                case 0 => return RecordField {
1914 -
                    name: PTR_FIELD,
1915 -
                    fieldType: Type::Pointer {
1916 -
                        class: types::PointerClass::Owned, target: item, mutable,
1917 -
                    },
1918 -
                    offset: 0,
1919 -
                },
1920 -
                case 1 => return RecordField {
1921 -
                    name: LEN_FIELD,
1922 -
                    fieldType: Type::U32,
1923 -
                    offset: PTR_SIZE as i32,
1924 -
                },
1925 -
                case 2 => return RecordField {
1926 -
                    name: CAP_FIELD,
1927 -
                    fieldType: Type::U32,
1928 -
                    offset: PTR_SIZE as i32 + 4,
1929 -
                },
1930 -
                else => return nil,
1931 -
            }
1932 -
        }
1933 -
        else => return nil,
2097 +
    if let case Type::Slice { class, item, mutable } = ty {
2098 +
        match index {
2099 +
            case 0 => return RecordField {
2100 +
                name: PTR_FIELD,
2101 +
                fieldType: Type::Pointer { class, target: item, mutable },
2102 +
                offset: 0,
2103 +
            },
2104 +
            case 1 => return RecordField {
2105 +
                name: LEN_FIELD,
2106 +
                fieldType: Type::U32,
2107 +
                offset: PTR_SIZE as i32,
2108 +
            },
2109 +
            case 2 => return RecordField {
2110 +
                name: CAP_FIELD,
2111 +
                fieldType: Type::U32,
2112 +
                offset: PTR_SIZE as i32 + 4,
2113 +
            },
2114 +
            else => return nil,
2115 +
        }
2116 +
    }
2117 +
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
2118 +
        index < recInfo.fields.len
2119 +
    {
2120 +
        return recInfo.fields[index];
1934 2121
    }
2122 +
    return nil;
1935 2123
}
1936 2124
1937 2125
/// Check if the two types can be compared for equality.
1938 2126
fn isComparable(left: Type, right: Type) -> bool {
1939 2127
    if left == Type::Unknown or right == Type::Unknown {
2330 2518
    scope: *Scope
2331 2519
) -> *mut Symbol throws (ResolveError) {
2332 2520
    assert path.len <> 0, "resolvePath: empty path";
2333 2521
    // Start by finding the root of the path.
2334 2522
    let root = path[0];
2335 -
    let sym = findInScopeRecursive(scope, root, isAnySymbol) else
2336 -
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2523 +
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
2524 +
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2337 2525
    let suffix = &path[1..];
2338 2526
2339 2527
    // Check visibility for symbol.
2340 2528
    if not isSymbolVisible(sym, scope, self.scope) {
2341 2529
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2484 2672
            // Ignore non-declaration nodes.
2485 2673
        }
2486 2674
    }
2487 2675
}
2488 2676
2677 +
/// Require the current declaration to be unsafe.
2678 +
fn requireUnsafe(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2679 +
    if self.unsafeDepth == 0 {
2680 +
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2681 +
    }
2682 +
}
2683 +
2684 +
/// Reject calls from safe code through unsafe function types.
2685 +
fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType)
2686 +
    throws (ResolveError)
2687 +
{
2688 +
    if info.isUnsafe and self.unsafeDepth == 0 {
2689 +
        throw emitError(self, node, ErrorKind::UnsafeCall);
2690 +
    }
2691 +
}
2692 +
2489 2693
/// Visit a top-level definition, recursing into sub-modules.
2490 2694
fn visitDef(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2491 2695
    match node.value {
2492 2696
        case ast::NodeValue::FnDecl(decl) => {
2493 2697
            try resolveFnDeclBody(self, node, decl) catch {
2500 2704
            }
2501 2705
            let modName = try nodeName(self, decl.name);
2502 2706
            let submod = try enterSubModule(self, modName, node);
2503 2707
            let case ast::NodeValue::Block(block) = submod.root.value
2504 2708
                else panic "visitDef: expected block for module root";
2505 -
            try resolveModuleDefs(self, &block);
2709 +
            let mut isUnsafe = false;
2710 +
            if let attrs = decl.attrs {
2711 +
                set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe);
2712 +
            }
2713 +
            if isUnsafe {
2714 +
                set self.unsafeDepth += 1;
2715 +
            }
2716 +
            try resolveModuleDefs(self, &block) catch e {
2717 +
                if isUnsafe { set self.unsafeDepth -= 1; }
2718 +
                exitModuleScope(self, submod);
2719 +
                throw e;
2720 +
            };
2721 +
            if isUnsafe {
2722 +
                set self.unsafeDepth -= 1;
2723 +
            }
2506 2724
            exitModuleScope(self, submod);
2507 2725
        }
2508 2726
        case ast::NodeValue::RecordDecl(_),
2509 2727
             ast::NodeValue::UnionDecl(_),
2510 2728
             ast::NodeValue::Use(_),
2532 2750
/// Try to infer a node's type.
2533 2751
fn infer(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2534 2752
    return try visit(self, node, Type::Unknown);
2535 2753
}
2536 2754
2755 +
/// Reject nested references while allowing a direct parameter reference.
2756 +
fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type)
2757 +
    throws (ResolveError)
2758 +
{
2759 +
    if isRefType(ty) {
2760 +
        if let case Type::Pointer { target, .. } = ty {
2761 +
            if containsRef(*target) {
2762 +
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2763 +
            }
2764 +
        } else if let case Type::Slice { item, .. } = ty {
2765 +
            if containsRef(*item) {
2766 +
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2767 +
            }
2768 +
        }
2769 +
    } else if containsRef(ty) {
2770 +
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2771 +
    }
2772 +
}
2773 +
2774 +
/// Require a type that may be stored or escape a call.
2775 +
fn ensureStorableType(self: *mut Resolver, node: *ast::Node, ty: Type)
2776 +
    throws (ResolveError)
2777 +
{
2778 +
    if containsRef(ty) {
2779 +
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2780 +
    }
2781 +
}
2782 +
2537 2783
/// Resolve a type signature node.
2538 2784
fn resolveValueType(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2539 2785
    let ty = try visit(self, node, Type::Unknown);
2540 2786
    // Opaque value types are not allowed.
2541 2787
    if ty == Type::Opaque {
2542 2788
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
2543 2789
    }
2790 +
    try validateValueTypeReferences(self, node, ty);
2544 2791
    return ty;
2545 2792
}
2546 2793
2547 2794
/// Analyze a node's type and check that it can be assigned to the expected type.
2548 2795
fn checkAssignable(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2636 2883
        case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr),
2637 2884
        case ast::NodeValue::Panic { message } => {
2638 2885
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2639 2886
                class: types::PointerClass::Owned,
2640 2887
                item: allocType(self, Type::U8),
2641 -
                mutable: false
2888 +
                mutable: false,
2642 2889
            });
2643 2890
            return setNodeType(self, node, Type::Never);
2644 2891
        },
2645 2892
        case ast::NodeValue::Assert { condition, message } => {
2646 2893
            try visit(self, condition, Type::Bool);
2647 2894
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2648 2895
                class: types::PointerClass::Owned,
2649 2896
                item: allocType(self, Type::U8),
2650 -
                mutable: false
2897 +
                mutable: false,
2651 2898
            });
2652 2899
            return setNodeType(self, node, Type::Void);
2653 2900
        },
2654 2901
        case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop),
2655 2902
        case ast::NodeValue::ExprStmt(expr) => {
2792 3039
        if not isTypeInferrable(bindingTy) {
2793 3040
            throw emitError(self, decl.value, ErrorKind::CannotInferType);
2794 3041
        }
2795 3042
    }
2796 3043
    // Variables cannot have void type.
3044 +
    if containsRef(bindingTy) {
3045 +
        throw emitError(self, node, ErrorKind::RefBinding);
3046 +
    }
2797 3047
    if bindingTy == Type::Void {
2798 3048
        throw emitError(self, decl.value, ErrorKind::CannotAssignVoid);
2799 3049
    }
2800 3050
    // Variables cannot have opaque type directly.
2801 3051
    if bindingTy == Type::Opaque {
3029 3279
    attrList: ?ast::Attributes,
3030 3280
    isConst: bool
3031 3281
) -> Type throws (ResolveError) {
3032 3282
    let attrs = resolveAttributes(self, attrList);
3033 3283
    let bindingTy = try infer(self, typeNode);
3284 +
    try ensureStorableType(self, typeNode, bindingTy);
3034 3285
    let valueTy = try checkAssignable(self, valueNode, bindingTy);
3035 3286
3036 3287
    if isConst {
3037 3288
        let mut constVal = constValueEntry(self, valueNode);
3038 3289
        if constVal == nil and not isConstExpr(self, valueNode) {
3061 3312
{
3062 3313
    let attrMask = resolveAttributes(self, decl.attrs);
3063 3314
    let mut retTy = Type::Void;
3064 3315
    if let retNode = decl.sig.returnType {
3065 3316
        set retTy = try infer(self, retNode);
3317 +
        try ensureStorableType(self, retNode, retTy);
3066 3318
    }
3067 3319
    let a = alloc::arenaAllocator(&mut self.arena);
3068 3320
    let mut paramTypes: *mut [*Type] = &mut [];
3069 3321
    let mut throwList: *mut [*Type] = &mut [];
3070 3322
    let mut fnType = FnType {
3071 3323
        paramTypes: &[],
3072 3324
        returnType: allocType(self, retTy),
3073 3325
        throwList: &[],
3326 +
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3074 3327
        localCount: 0,
3075 3328
    };
3076 3329
    // Enter the function scope to process parameters.
3077 3330
    enterFn(self, node, &fnType);
3078 3331
3102 3355
        let throwTy = try infer(self, throwNode) catch e {
3103 3356
            exitFn(self);
3104 3357
            throw e;
3105 3358
        };
3106 3359
        throwList.append(allocType(self, throwTy), a);
3360 +
        try ensureStorableType(self, throwNode, throwTy);
3107 3361
    }
3108 3362
    exitFn(self);
3109 3363
    set fnType.paramTypes = &paramTypes[..];
3110 3364
    set fnType.throwList = &throwList[..];
3111 3365
3128 3382
        panic "resolveFnDeclBody: unexpected symbol data for function";
3129 3383
    };
3130 3384
    let retTy = *fnType.returnType;
3131 3385
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3132 3386
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3387 +
    let isUnsafe = fnType.isUnsafe;
3133 3388
3134 3389
    if let body = decl.body {
3135 3390
        if isIntrinsic {
3136 3391
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
3137 3392
        }
3138 3393
        if isExtern {
3139 3394
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
3140 3395
        }
3396 +
        if isUnsafe {
3397 +
            set self.unsafeDepth += 1;
3398 +
        }
3141 3399
        enterFn(self, node, fnType); // Enter function scope for body analysis.
3142 3400
3143 3401
        let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3144 3402
            exitFn(self);
3403 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3145 3404
            throw e;
3146 3405
        };
3147 3406
        if retTy <> Type::Void and bodyTy <> Type::Never {
3148 3407
            exitFn(self);
3408 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3149 3409
            throw emitError(self, body, ErrorKind::FnMissingReturn);
3150 3410
        }
3151 3411
        exitFn(self);
3412 +
        if isUnsafe {
3413 +
            set self.unsafeDepth -= 1;
3414 +
        }
3152 3415
    } else if not isExtern {
3153 3416
        throw emitError(self, node, ErrorKind::FnMissingBody);
3154 3417
    }
3155 3418
}
3156 3419
3162 3425
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3163 3426
3164 3427
    return ty;
3165 3428
}
3166 3429
3430 +
/// Resolve the compiler-known `Linear` marker from a derive list.
3431 +
fn resolveLinearDerive(self: *mut Resolver, derives: *mut [*ast::Node]) -> bool
3432 +
    throws (ResolveError)
3433 +
{
3434 +
    let mut linear = false;
3435 +
    for derive in derives {
3436 +
        let name = try nodeName(self, derive);
3437 +
        if mem::eq(name, "Linear") {
3438 +
            if linear {
3439 +
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3440 +
            }
3441 +
            set linear = true;
3442 +
        } else {
3443 +
            // Resolve an ordinary trait derive.
3444 +
            try infer(self, derive);
3445 +
        }
3446 +
    }
3447 +
    return linear;
3448 +
}
3449 +
3167 3450
/// Resolve record fields from a node list.
3168 3451
fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3169 3452
    throws (ResolveError)
3170 3453
{
3171 3454
    let a = alloc::arenaAllocator(&mut self.arena);
3182 3465
            field: fieldNode,
3183 3466
            type: typeNode,
3184 3467
            value: valueNode
3185 3468
        } = field.value else panic "resolveRecordFields: invalid record field";
3186 3469
        let fieldTy = try resolveValueType(self, typeNode);
3470 +
        try ensureStorableType(self, typeNode, fieldTy);
3187 3471
3188 3472
        if let v = valueNode {
3189 3473
            let _valTy = try checkAssignable(self, v, fieldTy);
3190 3474
        }
3191 3475
        // Get field name for labeled records.
3216 3500
    // Compute cached layout.
3217 3501
    let recordLayout = Layout {
3218 3502
        size: mem::alignUp(currentOffset, maxAlignment),
3219 3503
        alignment: maxAlignment
3220 3504
    };
3221 -
    return RecordType { fields: &result[..], labeled, layout: recordLayout };
3505 +
    return RecordType {
3506 +
        fields: &result[..],
3507 +
        labeled,
3508 +
        layout: recordLayout,
3509 +
        declaredLinear: false,
3510 +
    };
3222 3511
}
3223 3512
3224 3513
/// Resolve record field types for a named record declaration.
3225 3514
fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3226 3515
    throws (ResolveError)
3234 3523
3235 3524
    // Skip if already resolved.
3236 3525
    if let case NominalType::Record(_) = *nominalTy {
3237 3526
        return;
3238 3527
    }
3239 -
    try visitList(self, decl.derives);
3240 -
    let recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3528 +
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
3529 +
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3530 +
    set recordType.declaredLinear = declaredLinear;
3241 3531
3242 3532
    set *nominalTy = NominalType::Record(recordType);
3243 3533
}
3244 3534
3245 3535
/// Bind a type name.
3335 3625
            }
3336 3626
            traitType.methods.append(TraitMethod {
3337 3627
                name: inherited.name,
3338 3628
                fnType: inherited.fnType,
3339 3629
                mutable: inherited.mutable,
3630 +
                receiverClass: inherited.receiverClass,
3340 3631
                index: traitType.methods.len as u32,
3341 3632
            }, a);
3342 3633
        }
3343 3634
        traitType.supertraits.append(superTrait, a);
3344 3635
    }
3349 3640
            actual: traitType.methods.len as u32 + methods.len as u32,
3350 3641
        }));
3351 3642
    }
3352 3643
3353 3644
    for methodNode in methods {
3354 -
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, .. } = methodNode.value
3645 +
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value
3355 3646
            else continue;
3356 3647
        let methodName = try nodeName(self, name);
3648 +
        let attrMask = resolveAttributes(self, attrs);
3357 3649
3358 3650
        // Reject duplicate method names.
3359 3651
        if let _ = findTraitMethod(traitType, methodName) {
3360 3652
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3361 3653
        }
3362 -
        // Determine receiver mutability from the receiver type node
3363 -
        // and validate that the receiver points to the declaring trait.
3654 +
        // Determine the receiver class and mutability, and validate that it
3655 +
        // points to the declaring trait.
3364 3656
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3365 3657
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3366 -
        let case ast::TypeSig::Pointer { mutable, valueType, .. } = typeSig
3658 +
        let case ast::TypeSig::Pointer {
3659 +
            class: receiverClass, valueType: receiverValueType, mutable,
3660 +
        } = typeSig
3367 3661
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3368 -
        let case ast::NodeValue::TypeSig(innerSig) = valueType.value
3662 +
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
3369 3663
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3370 3664
        let case ast::TypeSig::Nominal(nameNode) = innerSig
3371 3665
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3372 3666
        let receiverTargetName = try nodeName(self, nameNode);
3373 3667
3406 3700
        }
3407 3701
        let fnType = FnType {
3408 3702
            paramTypes: &paramTypes[..],
3409 3703
            returnType: retType,
3410 3704
            throwList: &throwList[..],
3705 +
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3411 3706
            localCount: 0,
3412 3707
        };
3413 3708
        traitType.methods.append(TraitMethod {
3414 3709
            name: methodName,
3415 3710
            fnType: allocFnType(self, fnType),
3416 3711
            mutable,
3712 +
            receiverClass,
3417 3713
            index: traitType.methods.len as u32,
3418 3714
        }, a);
3419 3715
3420 3716
        setNodeType(self, methodNode, Type::Void);
3421 3717
    }
3490 3786
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
3491 3787
3492 3788
    // Match each instance method to a trait method.
3493 3789
    for methodNode in methods {
3494 3790
        let case ast::NodeValue::MethodDecl {
3495 -
            name, receiverName, receiverType, sig, body, ..
3791 +
            name, receiverName, receiverType, sig, body, attrs,
3496 3792
        } = methodNode.value else continue;
3497 3793
3498 3794
        let methodName = try nodeName(self, name);
3795 +
        let attrMask = resolveAttributes(self, attrs);
3499 3796
3500 3797
        // Find the matching trait method.
3501 3798
        let tm = findTraitMethod(traitInfo, methodName)
3502 3799
            else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
3800 +
        let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
3801 +
        if instanceUnsafe <> tm.fnType.isUnsafe {
3802 +
            throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
3803 +
        }
3503 3804
3504 3805
        // Determine receiver mutability and validate receiver type.
3505 3806
        // The receiver must be `*Type` or `*mut Type`.
3506 3807
        let case ast::NodeValue::TypeSig(typeSig) = receiverType.value
3507 3808
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3508 -
        let case ast::TypeSig::Pointer { mutable: receiverMut, valueType, .. } = typeSig
3809 +
        let case ast::TypeSig::Pointer {
3810 +
            class: receiverClass, valueType, mutable: receiverMut,
3811 +
        } = typeSig
3509 3812
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3813 +
        if receiverClass <> tm.receiverClass {
3814 +
            throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3815 +
        }
3510 3816
3511 3817
        // Validate that the receiver type annotation matches the
3512 3818
        // concrete type from the instance declaration.
3513 3819
        let annotatedTy = try infer(self, valueType);
3514 3820
        if not typesEqual(annotatedTy, concreteType) {
3527 3833
        }
3528 3834
3529 3835
        // Build the function type for the instance method.
3530 3836
        // The receiver becomes the first parameter.
3531 3837
        let receiverPtrType = Type::Pointer {
3532 -
            class: types::PointerClass::Owned,
3838 +
            class: receiverClass,
3533 3839
            target: allocType(self, concreteType),
3534 3840
            mutable: receiverMut,
3535 3841
        };
3536 3842
3537 3843
        // Validate that the instance method's signature matches the
3590 3896
        }
3591 3897
        let fnType = FnType {
3592 3898
            paramTypes: &paramTypes[..],
3593 3899
            returnType: tm.fnType.returnType,
3594 3900
            throwList: tm.fnType.throwList,
3901 +
            isUnsafe: tm.fnType.isUnsafe,
3595 3902
            localCount: 0,
3596 3903
        };
3597 3904
3598 3905
        // Create a symbol for the instance method without binding it into the
3599 3906
        // module scope. Instance methods are dispatched via v-table, so they
3600 3907
        // must not pollute the enclosing scope.
3601 3908
        let fnTy = Type::Fn(allocFnType(self, fnType));
3602 3909
        let mName = try nodeName(self, name);
3603 3910
        let sym = allocSymbol(self, SymbolData::Value {
3604 3911
            mutable: false, alignment: 0, type: fnTy, addressTaken: false,
3605 -
        }, mName, methodNode, 0);
3912 +
        }, mName, methodNode, attrMask);
3606 3913
3607 3914
        setNodeSymbol(self, methodNode, sym);
3608 3915
        setNodeType(self, methodNode, fnTy);
3609 3916
        setNodeType(self, name, fnTy);
3610 3917
3668 3975
) throws (ResolveError) {
3669 3976
    let sym = symbolFor(self, node)
3670 3977
        else throw emitError(self, node, ErrorKind::Internal);
3671 3978
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3672 3979
        else panic "resolveMethodBody: expected value symbol";
3980 +
    let isUnsafe = fnType.isUnsafe;
3981 +
    if isUnsafe {
3982 +
        set self.unsafeDepth += 1;
3983 +
    }
3673 3984
3674 3985
    // Enter function scope.
3675 3986
    enterFn(self, node, fnType);
3676 3987
3677 3988
    // Bind the receiver parameter.
3678 3989
    let receiverTy = *fnType.paramTypes[0];
3679 3990
    try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch e {
3680 3991
        exitFn(self);
3992 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3681 3993
        throw e;
3682 3994
    };
3683 3995
    // Bind the remaining parameters from the signature.
3684 3996
    for paramNode in sig.params {
3685 3997
        let paramTy = try infer(self, paramNode) catch e {
3686 3998
            exitFn(self);
3999 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3687 4000
            throw e;
3688 4001
        };
3689 4002
    }
3690 4003
3691 4004
    // Resolve the body.
3692 4005
    let retTy = *fnType.returnType;
3693 4006
    let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3694 4007
        exitFn(self);
4008 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3695 4009
        throw e;
3696 4010
    };
3697 4011
    if retTy <> Type::Void and bodyTy <> Type::Never {
3698 4012
        exitFn(self);
4013 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3699 4014
        throw emitError(self, body, ErrorKind::FnMissingReturn);
3700 4015
    }
3701 4016
    exitFn(self);
4017 +
    if isUnsafe {
4018 +
        set self.unsafeDepth -= 1;
4019 +
    }
3702 4020
}
3703 4021
3704 4022
/// Resolve a standalone method declaration (signature only).
3705 4023
/// Validates the receiver type and registers the method in the method table.
3706 -
/// Extract the type name from a resolved receiver type node (`*T` or `*mut T`).
3707 -
fn receiverTypeName(self: *mut Resolver, receiverType: *ast::Node) -> *[u8] throws (ResolveError) {
3708 -
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = receiverType.value
4024 +
4025 +
/// Extract the type name from a resolved receiver type node.
4026 +
fn receiverTypeName(
4027 +
    self: *mut Resolver,
4028 +
    receiverType: *ast::Node,
4029 +
) -> *[u8] throws (ResolveError) {
4030 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) =
4031 +
        receiverType.value
3709 4032
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3710 4033
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
3711 4034
        else throw emitError(self, receiverType, ErrorKind::Internal);
3712 4035
    let sym = symbolFor(self, nameNode)
3713 4036
        else throw emitError(self, receiverType, ErrorKind::Internal);
3714 4037
3715 4038
    return sym.name;
3716 4039
}
3717 4040
4041 +
/// Resolve and register a standalone method declaration.
3718 4042
fn resolveMethodDecl(
3719 4043
    self: *mut Resolver,
3720 4044
    node: *ast::Node,
3721 4045
    name: *ast::Node,
3722 4046
    receiverName: *ast::Node,
3725 4049
    attrs: ?ast::Attributes,
3726 4050
) throws (ResolveError) {
3727 4051
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
3728 4052
    // nominal type.
3729 4053
    let fullReceiverTy = try infer(self, receiverType);
3730 -
    let case Type::Pointer { target, mutable: receiverMut, .. } = fullReceiverTy
4054 +
    let case Type::Pointer {
4055 +
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
4056 +
    } = fullReceiverTy
3731 4057
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3732 -
    let concreteType = *target;
4058 +
    let concreteType = *receiverTarget;
3733 4059
    let case Type::Nominal(nominalTy) = concreteType
3734 4060
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
3735 4061
    try ensureNominalResolved(self, nominalTy, receiverType);
3736 4062
3737 4063
    // Get the type name from the inner type node's symbol.
3738 4064
    let typeName = try receiverTypeName(self, receiverType);
3739 4065
    let methodName = try nodeName(self, name);
4066 +
    let attrMask = resolveAttributes(self, attrs);
3740 4067
3741 4068
    // Reject duplicate method for the same (type, name).
3742 4069
    if let _ = findMethod(self, concreteType, methodName) {
3743 4070
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3744 4071
    }
3747 4074
    let a = alloc::arenaAllocator(&mut self.arena);
3748 4075
    let mut paramTypes: *mut [*Type] = &mut [];
3749 4076
3750 4077
    // Receiver is the first parameter.
3751 4078
    let receiverPtrType = Type::Pointer {
3752 -
        class: types::PointerClass::Owned,
4079 +
        class: receiverClass,
3753 4080
        target: allocType(self, concreteType),
3754 4081
        mutable: receiverMut,
3755 4082
    };
3756 4083
    paramTypes.append(allocType(self, receiverPtrType), a);
3757 4084
3776 4103
    }
3777 4104
3778 4105
    let retTypePtr = allocType(self, returnType);
3779 4106
    let throwList = &throwTypes[..];
3780 4107
4108 +
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
3781 4109
    // Full function type (receiver + params) for lowering.
3782 4110
    let fullFnType = FnType {
3783 -
        paramTypes: &paramTypes[..], returnType: retTypePtr, throwList, localCount: 0,
4111 +
        paramTypes: &paramTypes[..],
4112 +
        returnType: retTypePtr,
4113 +
        throwList,
4114 +
        isUnsafe,
4115 +
        localCount: 0,
3784 4116
    };
3785 4117
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
3786 4118
3787 4119
    // Function type excluding receiver, for call arg checking.
3788 4120
    let checkFnType = FnType {
3789 -
        paramTypes: &paramTypes[1..], returnType: retTypePtr, throwList, localCount: 0,
4121 +
        paramTypes: &paramTypes[1..],
4122 +
        returnType: retTypePtr,
4123 +
        throwList,
4124 +
        isUnsafe,
4125 +
        localCount: 0,
3790 4126
    };
3791 4127
3792 -
    // Compute attribute mask.
3793 -
    let mut attrMask: u32 = 0;
3794 -
    if let a = attrs {
3795 -
        for attrNode in a.list {
3796 -
            if let case ast::NodeValue::Attribute(attr) = attrNode.value {
3797 -
                set attrMask = attrMask | (attr as u32);
3798 -
            }
3799 -
        }
3800 -
    }
3801 -
3802 4128
    // Create a symbol for the method without binding it into the module scope.
3803 4129
    let sym = allocSymbol(self, SymbolData::Value {
3804 4130
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
3805 4131
    }, methodName, node, attrMask);
3806 4132
3816 4142
        concreteType,
3817 4143
        concreteTypeName: typeName,
3818 4144
        name: methodName,
3819 4145
        fnType: allocFnType(self, checkFnType),
3820 4146
        mutable: receiverMut,
4147 +
        receiverClass,
3821 4148
        symbol: sym,
3822 4149
    };
3823 4150
    set self.methodsLen += 1;
3824 4151
}
3825 4152
3876 4203
    let mut variants: *mut [UnionVariant] = &mut [];
3877 4204
3878 4205
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
3879 4206
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
3880 4207
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4208 +
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
3881 4209
    set *nominalTy = NominalType::Union(UnionType {
3882 4210
        variants: &[],
3883 4211
        layout: Layout { size: 0, alignment: 0 },
3884 4212
        valOffset: 0,
3885 -
        isAllVoid: true
4213 +
        isAllVoid: true,
4214 +
        declaredLinear,
3886 4215
    });
3887 4216
3888 -
    try visitList(self, decl.derives);
3889 -
3890 4217
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
3891 4218
    let mut iota: u32 = 0;
3892 4219
    for variantNode, i in decl.variants {
3893 4220
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
3894 4221
            else panic "resolveUnionBody: invalid union variant";
3895 4222
        let variantName = try nodeName(self, variantDecl.name);
3896 4223
        // Resolve the variant's payload type if present.
3897 4224
        let mut variantType = Type::Void;
3898 -
        if let ty = try visitOptional(self, variantDecl.type, Type::Unknown) {
3899 -
            set variantType = ty;
4225 +
        if let typeNode = variantDecl.type {
4226 +
            set variantType = try infer(self, typeNode);
4227 +
            try ensureStorableType(self, typeNode, variantType);
3900 4228
        }
3901 4229
        // Process the variant's explicit discriminant value if present.
3902 4230
        try visitOptional(self, variantDecl.value, variantType);
3903 4231
        let tag = variantTag(variantDecl, &mut iota);
3904 4232
        // Create a symbol for this variant.
3917 4245
    set *nominalTy = NominalType::Union(UnionType {
3918 4246
        variants: &variants[..],
3919 4247
        layout: info.layout,
3920 4248
        valOffset: info.valOffset,
3921 4249
        isAllVoid: info.isAllVoid,
4250 +
        declaredLinear,
3922 4251
    });
3923 4252
}
3924 4253
3925 4254
/// Check if a module should be analyzed based on its attributes and build configuration.
3926 4255
fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool {
4101 4430
    pattern: *ast::Node,
4102 4431
    scrutineeTy: Type,
4103 4432
    mode: IdentMode,
4104 4433
    matchBy: MatchBy
4105 4434
) throws (ResolveError) {
4435 +
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
4436 +
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
4437 +
        return;
4438 +
    }
4106 4439
    // TODO: Collapse these nested matches.
4107 4440
    match scrutineeTy {
4108 -
        case Type::Pointer { target, .. } => {
4109 -
            // Auto-deref: when the scrutinee is a pointer and the pattern
4110 -
            // is a destructuring pattern, resolve against the pointed-to type.
4111 -
            if isDestructuringPattern(pattern) {
4112 -
                try resolveCasePattern(self, pattern, *target, mode, matchBy);
4113 -
                return;
4114 -
            }
4115 -
        }
4116 4441
        case Type::Nominal(info) => {
4117 4442
            try ensureNominalResolved(self, info, pattern);
4118 4443
4119 4444
            match *info {
4120 4445
                case NominalType::Union(unionType) => {
4209 4534
        }
4210 4535
    }
4211 4536
    // Extract item type and store pre-computed loop metadata for the lowerer.
4212 4537
    let mut itemTy: Type = undefined;
4213 4538
    match iterableTy {
4539 +
        case Type::Slice { item, .. } => {
4540 +
            set itemTy = *item;
4541 +
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4542 +
                elemType: item, length: nil, bindingName, indexName
4543 +
            });
4544 +
        }
4214 4545
        case Type::Range { start, .. } => {
4215 4546
            // Iterable ranges must have a start, and since we enforce type
4216 4547
            // equality for start and end, that is always the item type.
4217 4548
            let valType = start else {
4218 4549
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4233 4564
                length: arrayInfo.length,
4234 4565
                bindingName,
4235 4566
                indexName,
4236 4567
            });
4237 4568
        }
4238 -
        case Type::Slice { item, .. } => {
4239 -
            set itemTy = *item;
4240 -
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4241 -
                elemType: item, length: nil, bindingName, indexName
4242 -
            });
4243 -
        }
4244 4569
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
4245 4570
    }
4246 4571
    enterScope(self, node);
4247 4572
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
4248 4573
4776 5101
{
4777 5102
    let mut bindTy = ty;
4778 5103
    match matchBy {
4779 5104
        case MatchBy::Value => {}
4780 5105
        case MatchBy::Ref => set bindTy = Type::Pointer {
4781 -
            class: types::PointerClass::Owned, target: allocType(self, ty), mutable: false,
5106 +
            class: types::PointerClass::Ref,
5107 +
            target: allocType(self, ty),
5108 +
            mutable: false,
4782 5109
        },
4783 5110
        case MatchBy::MutRef => set bindTy = Type::Pointer {
4784 -
            class: types::PointerClass::Owned, target: allocType(self, ty), mutable: true,
5111 +
            class: types::PointerClass::Ref,
5112 +
            target: allocType(self, ty),
5113 +
            mutable: true,
4785 5114
        },
4786 5115
    }
4787 5116
    match binding.value {
4788 5117
        case ast::NodeValue::Placeholder => {
4789 5118
            // Nothing to do.
4900 5229
            });
4901 5230
        }
4902 5231
    }
4903 5232
}
4904 5233
5234 +
/// Return whether a case pattern introduces value bindings.
5235 +
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
5236 +
    match pattern.value {
5237 +
        case ast::NodeValue::Ident(_) => return nested,
5238 +
        case ast::NodeValue::Call(call) => {
5239 +
            for arg in call.args {
5240 +
                if casePatternIntroducesBindings(arg, true) {
5241 +
                    return true;
5242 +
                }
5243 +
            }
5244 +
        }
5245 +
        case ast::NodeValue::RecordLit(lit) => {
5246 +
            for fieldNode in lit.fields {
5247 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5248 +
                    else continue;
5249 +
                if casePatternIntroducesBindings(field.value, true) {
5250 +
                    return true;
5251 +
                }
5252 +
            }
5253 +
        }
5254 +
        case ast::NodeValue::ArrayLit(items) => {
5255 +
            for item in items {
5256 +
                if casePatternIntroducesBindings(item, true) {
5257 +
                    return true;
5258 +
                }
5259 +
            }
5260 +
        }
5261 +
        else => {}
5262 +
    }
5263 +
    return false;
5264 +
}
5265 +
4905 5266
/// Analyze a `let-else` guard.
4906 5267
fn resolveLetElse(self: *mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
4907 5268
    throws (ResolveError)
4908 5269
{
4909 5270
    let pat = &letElse.pattern;
4915 5276
            let case Type::Optional(inner) = exprTy else {
4916 5277
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
4917 5278
            };
4918 5279
            let payloadTy = *inner;
4919 5280
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
4920 -
            // The `else` branch must be assignable to the payload type.
5281 +
            // The `else` branch supplies the binding when the optional is nil.
4921 5282
            try checkAssignable(self, letElse.elseBranch, payloadTy);
4922 5283
4923 5284
            return setNodeType(self, node, Type::Void);
4924 5285
        }
4925 5286
        case ast::PatternKind::Case => {
4926 -
            // Analyze pattern against expression type.
4927 -
            try resolveCasePattern(self, pat.pattern, exprTy, IdentMode::Compare, MatchBy::Value);
5287 +
            // Resolve the failure path before introducing success-only bindings.
5288 +
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
5289 +
            try resolveCasePattern(
5290 +
                self,
5291 +
                pat.pattern,
5292 +
                exprTy,
5293 +
                IdentMode::Compare,
5294 +
                MatchBy::Value,
5295 +
            );
5296 +
            if let guardExpr = pat.guard {
5297 +
                try checkBoolean(self, guardExpr);
5298 +
            }
5299 +
            if elseTy <> Type::Never and
5300 +
               casePatternIntroducesBindings(pat.pattern, false)
5301 +
            {
5302 +
                throw emitError(
5303 +
                    self,
5304 +
                    letElse.elseBranch,
5305 +
                    ErrorKind::LinearLetElseMustTerminate,
5306 +
                );
5307 +
            }
4928 5308
        }
4929 5309
    }
4930 -
    if let guardExpr = pat.guard {
4931 -
        try checkBoolean(self, guardExpr);
4932 -
    }
4933 -
    // The `else` branch must be assignable to the expression type.
4934 -
    try checkAssignable(self, letElse.elseBranch, exprTy);
4935 -
4936 5310
    return setNodeType(self, node, Type::Void);
4937 5311
}
4938 5312
4939 5313
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
4940 5314
fn resolveBuiltinCall(
4950 5324
                expected: 2,
4951 5325
                actual: args.len as u32,
4952 5326
            }));
4953 5327
        }
4954 5328
        let ptrType = try visit(self, args[0], Type::Unknown);
4955 -
        let case Type::Pointer { target, mutable, .. } = ptrType else {
5329 +
        let case Type::Pointer { class, target, mutable } = ptrType else {
4956 5330
            throw emitError(self, node, ErrorKind::ExpectedPointer);
4957 5331
        };
4958 5332
        let _ = try checkAssignable(self, args[1], Type::U32);
4959 5333
        if args.len == 3 {
4960 5334
            let _ = try checkAssignable(self, args[2], Type::U32);
4961 5335
        }
4962 -
        return setNodeType(self, node, Type::Slice {
4963 -
            class: types::PointerClass::Owned, item: target, mutable,
4964 -
        });
5336 +
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
4965 5337
    }
4966 5338
    if args.len <> 1 {
4967 5339
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
4968 5340
            expected: 1,
4969 5341
            actual: args.len as u32,
5028 5400
    throws (ResolveError)
5029 5401
{
5030 5402
    // Intercept method calls on slices before inferring the callee.
5031 5403
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5032 5404
        let parentTy = try infer(self, access.parent);
5405 +
        if isUnsafePointerType(parentTy) {
5406 +
            try requireUnsafe(self, access.parent);
5407 +
        }
5033 5408
        let subjectTy = autoDeref(parentTy);
5034 5409
5035 5410
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5036 5411
            let methodName = try nodeName(self, access.child);
5037 5412
            if methodName == "append" {
5038 -
                return try resolveSliceAppend(self, node, access.parent, parentTy, call.args, item, mutable);
5413 +
                return try resolveSliceAppend(
5414 +
                    self, node, access.parent, parentTy, call.args, item, mutable
5415 +
                );
5039 5416
            }
5040 5417
            if methodName == "delete" {
5041 -
                return try resolveSliceDelete(self, node, access.parent, call.args, item, mutable);
5418 +
                return try resolveSliceDelete(
5419 +
                    self, node, access.parent, call.args, item, mutable
5420 +
                );
5042 5421
            }
5043 5422
        }
5044 5423
    }
5045 5424
    let calleeTy = try infer(self, call.callee);
5425 +
    if let case Type::Fn(info) = calleeTy {
5426 +
        try checkUnsafeCall(self, call.callee, info);
5427 +
    }
5046 5428
5047 5429
    // Check if callee is a union variant and dispatch to constructor handler.
5048 5430
    // TODO: Move this out. We should decide on this earlier, based on the callee.
5049 5431
    if let calleeSym = symbolFor(self, call.callee) {
5050 5432
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
5198 5580
            try checkSliceRangeIndices(self, range);
5199 5581
5200 5582
            let mut item: *Type = undefined;
5201 5583
            let mut capacity: ?u32 = nil;
5202 5584
5203 -
            match subjectTy {
5204 -
                case Type::Array(a) => {
5205 -
                    try validateArraySliceBounds(self, range, a.length, node);
5206 -
                    set item = a.item;
5207 -
                    set capacity = a.length;
5585 +
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
5586 +
                if not sliceMutable {
5587 +
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
5208 5588
                }
5209 -
                case Type::Slice { item: i, mutable, .. } => {
5210 -
                    if not mutable { throw emitError(self, container, ErrorKind::ImmutableBinding); }
5211 -
                    set item = i;
5589 +
                set item = sliceItem;
5590 +
            } else {
5591 +
                match subjectTy {
5592 +
                    case Type::Array(a) => {
5593 +
                        try validateArraySliceBounds(self, range, a.length, node);
5594 +
                        set item = a.item;
5595 +
                        set capacity = a.length;
5596 +
                    }
5597 +
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5212 5598
                }
5213 -
                else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5214 5599
            }
5215 5600
            // RHS is either a fill value or a source slice.
5216 5601
            let rhsTy = try infer(self, assign.right);
5217 -
            if let case Type::Slice { item: srcItem, .. } = rhsTy {
5218 -
                if *srcItem <> *item {
5219 -
                    throw emitTypeMismatch(self, assign.right, TypeMismatch { expected: *item, actual: *srcItem });
5602 +
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
5603 +
                if *sourceItem <> *item {
5604 +
                    throw emitTypeMismatch(
5605 +
                        self,
5606 +
                        assign.right,
5607 +
                        TypeMismatch { expected: *item, actual: *sourceItem },
5608 +
                    );
5220 5609
                }
5221 5610
            } else {
5222 5611
                try checkAssignable(self, assign.right, *item);
5223 5612
            }
5224 5613
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
5310 5699
        let _ = try infer(self, container);
5311 5700
        try checkSliceRangeIndices(self, range);
5312 5701
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
5313 5702
    }
5314 5703
    let containerTy = try infer(self, container);
5704 +
    if isUnsafePointerType(containerTy) {
5705 +
        try requireUnsafe(self, container);
5706 +
    }
5315 5707
    try checkIndex(self, indexNode);
5316 5708
    let subjectTy = autoDeref(containerTy);
5709 +
    if let case Type::Slice { item, .. } = subjectTy {
5710 +
        return setNodeType(self, node, *item);
5711 +
    }
5317 5712
5318 5713
    match subjectTy {
5319 5714
        case Type::Array(arrayInfo) => {
5320 5715
            return setNodeType(self, node, *arrayInfo.item);
5321 5716
        }
5322 -
        case Type::Slice { item, .. } => {
5323 -
            return setNodeType(self, node, *item);
5324 -
        }
5325 5717
        else => {
5326 5718
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
5327 5719
        }
5328 5720
    }
5329 5721
}
5680 6072
/// Analyze a field access expression.
5681 6073
fn resolveFieldAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
5682 6074
    throws (ResolveError)
5683 6075
{
5684 6076
    let parentTy = try infer(self, access.parent);
6077 +
    if isUnsafePointerType(parentTy) {
6078 +
        try requireUnsafe(self, access.parent);
6079 +
    }
5685 6080
    let subjectTy = autoDeref(parentTy);
5686 6081
5687 -
    match subjectTy {
5688 -
        case Type::Nominal(NominalType::Record(recordType)) => {
5689 -
            let fieldNode = access.child;
5690 -
            let fieldName = try nodeName(self, fieldNode);
5691 -
            if let fieldIndex = findRecordField(&recordType, fieldName) {
6082 +
    if let case Type::Slice { class, item, mutable } = subjectTy {
6083 +
        let fieldNode = access.child;
6084 +
        let fieldName = try nodeName(self, fieldNode);
6085 +
        if mem::eq(fieldName, PTR_FIELD) {
6086 +
            setRecordFieldIndex(self, fieldNode, 0);
6087 +
            return setNodeType(
6088 +
                self,
6089 +
                node,
6090 +
                Type::Pointer { class, target: item, mutable },
6091 +
            );
6092 +
        }
6093 +
        if mem::eq(fieldName, LEN_FIELD) {
6094 +
            setRecordFieldIndex(self, fieldNode, 1);
6095 +
            return setNodeType(self, node, Type::U32);
6096 +
        }
6097 +
        if mem::eq(fieldName, CAP_FIELD) {
6098 +
            setRecordFieldIndex(self, fieldNode, 2);
6099 +
            return setNodeType(self, node, Type::U32);
6100 +
        }
6101 +
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
6102 +
    }
6103 +
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
6104 +
        let fieldName = try nodeName(self, access.child);
6105 +
        let method = findTraitMethod(traitInfo, fieldName)
6106 +
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6107 +
        return setNodeType(self, node, Type::Fn(method.fnType));
6108 +
    }
6109 +
6110 +
    match subjectTy {
6111 +
        case Type::Nominal(NominalType::Record(recordType)) => {
6112 +
            let fieldNode = access.child;
6113 +
            let fieldName = try nodeName(self, fieldNode);
6114 +
            if let fieldIndex = findRecordField(&recordType, fieldName) {
5692 6115
                let fieldTy = recordType.fields[fieldIndex].fieldType;
5693 6116
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5694 6117
                return setNodeType(self, node, fieldTy);
5695 6118
            }
5696 6119
            // Not a field: check for a standalone method.
5709 6132
5710 6133
                return setNodeType(self, node, Type::U32);
5711 6134
            }
5712 6135
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
5713 6136
        }
5714 -
        case Type::Slice { item, mutable, .. } => {
5715 -
            let fieldNode = access.child;
5716 -
            let fieldName = try nodeName(self, fieldNode);
5717 -
5718 -
            if mem::eq(fieldName, PTR_FIELD) {
5719 -
                setRecordFieldIndex(self, fieldNode, 0);
5720 -
                let ptrTy = Type::Pointer {
5721 -
                    class: types::PointerClass::Owned,
5722 -
                    target: item,
5723 -
                    mutable,
5724 -
                };
5725 -
                return setNodeType(self, node, ptrTy);
5726 -
            }
5727 -
            if mem::eq(fieldName, LEN_FIELD) {
5728 -
                setRecordFieldIndex(self, fieldNode, 1);
5729 -
                return setNodeType(self, node, Type::U32);
5730 -
            }
5731 -
            if mem::eq(fieldName, CAP_FIELD) {
5732 -
                setRecordFieldIndex(self, fieldNode, 2);
5733 -
                return setNodeType(self, node, Type::U32);
5734 -
            }
5735 -
            throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
5736 -
        }
5737 -
        case Type::TraitObject { traitInfo, .. } => {
5738 -
            let fieldName = try nodeName(self, access.child);
5739 -
            let method = findTraitMethod(traitInfo, fieldName)
5740 -
                else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5741 6137
5742 -
            return setNodeType(self, node, Type::Fn(method.fnType));
5743 -
        }
5744 6138
        else => {
5745 6139
            // Check for standalone methods on any nominal type (e.g. unions).
5746 6140
            if let case Type::Nominal(_) = subjectTy {
5747 6141
                let fieldName = try nodeName(self, access.child);
5748 6142
                if let method = findMethod(self, subjectTy, fieldName) {
5849 6243
5850 6244
/// Analyze an address-of expression.
5851 6245
fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
5852 6246
    throws (ResolveError)
5853 6247
{
6248 +
    // Checked and unsafe pointer contexts require a reference source.
6249 +
    let class = types::PointerClass::Ref
6250 +
        if isRefType(hint) or isUnsafePointerType(hint)
6251 +
        else types::PointerClass::Owned;
5854 6252
    if addr.mutable {
5855 6253
        if not try canBorrowMutFrom(self, addr.target) {
5856 6254
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
5857 6255
        }
5858 6256
    }
5864 6262
            try checkSliceRangeIndices(self, range);
5865 6263
5866 6264
            let mut item: *Type = undefined;
5867 6265
            let mut capacity: ?u32 = nil;
5868 6266
5869 -
            match subjectTy {
5870 -
                case Type::Array(arrayInfo) => {
5871 -
                    try validateArraySliceBounds(self, range, arrayInfo.length, node);
5872 -
                    set item = arrayInfo.item;
5873 -
                    set capacity = arrayInfo.length;
6267 +
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
6268 +
                if addr.mutable and not sliceMutable {
6269 +
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
5874 6270
                }
5875 -
                case Type::Slice { item: sliceItem, mutable, .. } => {
5876 -
                    if addr.mutable and not mutable {
5877 -
                        throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6271 +
                set item = sliceItem;
6272 +
            } else {
6273 +
                match subjectTy {
6274 +
                    case Type::Array(arrayInfo) => {
6275 +
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
6276 +
                        set item = arrayInfo.item;
6277 +
                        set capacity = arrayInfo.length;
6278 +
                    }
6279 +
                    else => {
6280 +
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
5878 6281
                    }
5879 -
                    set item = sliceItem;
5880 -
                }
5881 -
                else => {
5882 -
                    throw emitError(self, container, ErrorKind::ExpectedIndexable);
5883 6282
                }
5884 6283
            }
5885 -
            let sliceTy = Type::Slice {
5886 -
                class: types::PointerClass::Owned, item, mutable: addr.mutable,
5887 -
            };
6284 +
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
5888 6285
            let alloc = allocType(self, sliceTy);
5889 6286
            setSliceRangeInfo(self, node, SliceRangeInfo {
5890 6287
                itemType: item,
5891 6288
                mutable: addr.mutable,
5892 6289
                capacity,
5918 6315
    if let case Type::Array(arrayInfo) = targetTy {
5919 6316
        match addr.target.value {
5920 6317
            case ast::NodeValue::ArrayLit(_),
5921 6318
                 ast::NodeValue::ArrayRepeatLit(_) =>
5922 6319
            {
5923 -
                let sliceTy = Type::Slice {
5924 -
                    class: types::PointerClass::Owned,
5925 -
                    item: arrayInfo.item,
5926 -
                    mutable: addr.mutable,
5927 -
                };
6320 +
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.mutable };
5928 6321
                return setNodeType(self, node, *allocType(self, sliceTy));
5929 6322
            }
5930 6323
            else => {}
5931 6324
        }
5932 6325
    }
5933 6326
    let pointerTy = Type::Pointer {
5934 -
        class: types::PointerClass::Owned,
5935 -
        target: allocType(self, targetTy),
5936 -
        mutable: addr.mutable,
6327 +
        class, target: allocType(self, targetTy), mutable: addr.mutable,
5937 6328
    };
5938 6329
    return setNodeType(self, node, pointerTy);
5939 6330
}
5940 6331
5941 6332
/// Analyze a dereference expression.
5942 6333
fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
5943 6334
    throws (ResolveError)
5944 6335
{
5945 6336
    let operandTy = try visit(self, targetNode, hint);
5946 -
    if let case Type::Pointer { target, .. } = operandTy {
6337 +
    if let case Type::Pointer { class, target, .. } = operandTy {
6338 +
        if class == types::PointerClass::Unsafe {
6339 +
            try requireUnsafe(self, targetNode);
6340 +
        }
5947 6341
        // Disallow dereferencing opaque pointers.
5948 6342
        if *target == Type::Opaque {
5949 6343
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
5950 6344
        }
5951 6345
        return setNodeType(self, node, *target);
5997 6391
        // Disallow slice to numeric; slices are fat pointers.
5998 6392
    } else if isAddressType(source) and isNumericType(target) {
5999 6393
        return true;
6000 6394
    }
6001 6395
    // Allow pointer casts if one side is `*opaque` or target types are castable.
6002 -
    if let case Type::Pointer { target: sourceTarget, .. } = source {
6003 -
        if let case Type::Pointer { target: targetTarget, .. } = target {
6396 +
    if let case Type::Pointer {
6397 +
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6398 +
    } = source {
6399 +
        if let case Type::Pointer {
6400 +
            class: targetClass, target: targetTarget, mutable: targetMutable,
6401 +
        } = target {
6402 +
            if sourceClass <> targetClass {
6403 +
                return false;
6404 +
            }
6405 +
            if targetMutable and not sourceMutable {
6406 +
                return false;
6407 +
            }
6004 6408
            if isOpaquePointer(source) or isOpaquePointer(target) {
6005 6409
                return true;
6006 6410
            }
6007 6411
            return isValidCast(*sourceTarget, *targetTarget);
6008 6412
        }
6009 6413
    }
6010 6414
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
6011 6415
    // or element types are castable.
6012 -
    if let case Type::Slice { item: sourceItem, .. } = source {
6013 -
        if let case Type::Slice { item: targetItem, .. } = target {
6416 +
    if let case Type::Slice {
6417 +
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6418 +
    } = source {
6419 +
        if let case Type::Slice {
6420 +
            class: targetClass, item: targetItem, mutable: targetMutable,
6421 +
        } = target {
6422 +
            if sourceClass <> targetClass {
6423 +
                return false;
6424 +
            }
6425 +
            if targetMutable and not sourceMutable {
6426 +
                return false;
6427 +
            }
6014 6428
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
6015 6429
                return true;
6016 6430
            }
6017 6431
            if *targetItem == Type::U8 {
6018 6432
                return true;
6027 6441
fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6028 6442
    throws (ResolveError)
6029 6443
{
6030 6444
    let targetTy = try infer(self, expr.type);
6031 6445
    let sourceTy = try visit(self, expr.value, targetTy);
6446 +
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6447 +
        try requireUnsafe(self, node);
6448 +
    }
6032 6449
6033 6450
    assert sourceTy <> Type::Unknown;
6034 6451
    assert targetTy <> Type::Unknown;
6035 6452
6036 -
    if isValidCast(sourceTy, targetTy) {
6453 +
    let mut valid = isValidCast(sourceTy, targetTy);
6454 +
    if let case Type::Pointer {
6455 +
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6456 +
    } = sourceTy {
6457 +
        if let case Type::Pointer {
6458 +
            class: targetClass, target: targetTarget, mutable: targetMutable,
6459 +
        } = targetTy {
6460 +
            if sourceClass == types::PointerClass::Ref and
6461 +
               targetClass == types::PointerClass::Unsafe and
6462 +
               (not targetMutable or sourceMutable) and
6463 +
               isValidCast(*sourceTarget, *targetTarget)
6464 +
            {
6465 +
                set valid = true;
6466 +
            }
6467 +
        }
6468 +
    }
6469 +
    if let case Type::Slice {
6470 +
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6471 +
    } = sourceTy {
6472 +
        if let case Type::Slice {
6473 +
            class: targetClass, item: targetItem, mutable: targetMutable,
6474 +
        } = targetTy {
6475 +
            if sourceClass == types::PointerClass::Ref and
6476 +
               targetClass == types::PointerClass::Unsafe and
6477 +
               (not targetMutable or sourceMutable) and
6478 +
               isValidCast(*sourceItem, *targetItem)
6479 +
            {
6480 +
                set valid = true;
6481 +
            }
6482 +
        }
6483 +
    }
6484 +
    if valid {
6037 6485
        // Propagate the constant value after applying the cast's target-width
6038 6486
        // truncation and signed interpretation.
6039 6487
        if let value = constValueEntry(self, expr.value) {
6040 6488
            if let case ConstValue::Int(i) = value {
6041 6489
                setNodeConstValue(self, node, castConstInt(i, targetTy));
6478 6926
        case ast::BinaryOp::Eq,
6479 6927
             ast::BinaryOp::Ne =>
6480 6928
        {
6481 6929
            let leftTy = try infer(self, binop.left);
6482 6930
            let rightTy = try visit(self, binop.right, leftTy);
6931 +
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
6932 +
                try requireUnsafe(self, node);
6933 +
            }
6483 6934
6484 6935
            if not isComparable(leftTy, rightTy) {
6485 6936
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
6486 6937
                    expected: leftTy,
6487 6938
                    actual: rightTy,
6507 6958
            // Check for pointer arithmetic before numeric check.
6508 6959
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
6509 6960
                let leftTy = try infer(self, binop.left);
6510 6961
                let rightTy = try visit(self, binop.right, leftTy);
6511 6962
6512 -
                // Disallow pointer arithmetic on opaque pointers.
6513 -
                if let case Type::Pointer { target: leftTarget, .. } = leftTy; *leftTarget == Type::Opaque {
6514 -
                    throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6515 -
                }
6516 -
                if let case Type::Pointer { target: rightTarget, .. } = rightTy; *rightTarget == Type::Opaque {
6517 -
                    throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6518 -
                }
6519 -
                // Allow pointer plus integer or integer plus pointer.
6520 -
                if let case Type::Pointer { .. } = leftTy; isNumericType(rightTy) {
6521 -
                    return setNodeType(self, node, leftTy);
6963 +
                // Allow arithmetic on owning pointers and unsafe pointers, but
6964 +
                // never on references.
6965 +
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
6966 +
                    if *leftTarget == Type::Opaque {
6967 +
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6968 +
                    }
6969 +
                    if leftClass <> types::PointerClass::Ref
6970 +
                        and isNumericType(rightTy)
6971 +
                    {
6972 +
                        if leftClass == types::PointerClass::Unsafe {
6973 +
                            try requireUnsafe(self, node);
6974 +
                        }
6975 +
                        return setNodeType(self, node, leftTy);
6976 +
                    }
6522 6977
                }
6523 -
                if binop.op == ast::BinaryOp::Add {
6524 -
                    if let case Type::Pointer { .. } = rightTy; isNumericType(leftTy) {
6978 +
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
6979 +
                    if *rightTarget == Type::Opaque {
6980 +
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6981 +
                    }
6982 +
                    if binop.op == ast::BinaryOp::Add
6983 +
                        and rightClass <> types::PointerClass::Ref
6984 +
                        and isNumericType(leftTy)
6985 +
                    {
6986 +
                        if rightClass == types::PointerClass::Unsafe {
6987 +
                            try requireUnsafe(self, node);
6988 +
                        }
6525 6989
                        return setNodeType(self, node, rightTy);
6526 6990
                    }
6527 6991
                }
6528 6992
            }
6529 6993
            let leftTy = try checkNumeric(self, binop.left);
6690 7154
                    actual: t.throwList.len,
6691 7155
                }));
6692 7156
            }
6693 7157
6694 7158
            for paramNode in t.params {
6695 -
                let paramTy = try infer(self, paramNode);
7159 +
                let paramTy = try resolveValueType(self, paramNode);
6696 7160
                paramTypes.append(allocType(self, paramTy), a);
6697 7161
            }
6698 7162
            for tyNode in t.throwList {
6699 -
                let throwTy = try infer(self, tyNode);
7163 +
                let throwTy = try resolveValueType(self, tyNode);
7164 +
                try ensureStorableType(self, tyNode, throwTy);
6700 7165
                throwList.append(allocType(self, throwTy), a);
6701 7166
            }
6702 7167
            let mut retType = allocType(self, Type::Void);
6703 7168
            if let ret = t.returnType {
6704 -
                set retType = allocType(self, try infer(self, ret));
7169 +
                let resolvedRet = try resolveValueType(self, ret);
7170 +
                try ensureStorableType(self, ret, resolvedRet);
7171 +
                set retType = allocType(self, resolvedRet);
6705 7172
            }
6706 7173
            let fnType = FnType {
6707 7174
                paramTypes: &paramTypes[..],
6708 7175
                returnType: retType,
6709 7176
                throwList: &throwList[..],
7177 +
                isUnsafe: false,
6710 7178
                localCount: 0,
6711 7179
            };
6712 7180
            return Type::Fn(allocFnType(self, fnType));
6713 7181
        }
7182 +
        // Resolve an opaque trait object signature.
6714 7183
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
6715 7184
            let sym = try resolveNamePath(self, traitName);
6716 7185
            let case SymbolData::Trait(traitInfo) = sym.data
6717 7186
                else throw emitError(self, traitName, ErrorKind::Internal);
6718 7187
            setNodeSymbol(self, traitName, sym);
6719 7188
6720 -
            return Type::TraitObject {
6721 -
                class,
6722 -
                traitInfo,
6723 -
                mutable,
6724 -
            };
7189 +
            return Type::TraitObject { class, traitInfo, mutable };
6725 7190
        }
6726 7191
    }
6727 7192
}
6728 7193
6729 7194
/// Check if a type can be used for inferrence.
6730 7195
fn isTypeInferrable(type: Type) -> bool {
7196 +
    if let case Type::Pointer { target, .. } = type {
7197 +
        return isTypeInferrable(*target);
7198 +
    }
6731 7199
    match type {
6732 7200
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
6733 7201
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
6734 7202
        case Type::Optional(opt) => return isTypeInferrable(*opt),
6735 -
        case Type::Pointer { target, .. } => return isTypeInferrable(*target),
6736 7203
        else => return true,
6737 7204
    }
6738 7205
}
6739 7206
6740 7207
/// Analyze a standalone expression by wrapping it in a synthetic function.
6898 7365
    for stmt in block.statements {
6899 7366
        try visitDecl(res, stmt);
6900 7367
    }
6901 7368
}
6902 7369
7370 +
/// Find a tracked binding by symbol identity.
7371 +
fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 {
7372 +
    for i in 0..env.len {
7373 +
        if let bound = env.symbols[i]; bound == sym {
7374 +
            return i;
7375 +
        }
7376 +
    }
7377 +
    return nil;
7378 +
}
7379 +
7380 +
/// Return whether a tracked binding is still available.
7381 +
fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool {
7382 +
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7383 +
}
7384 +
7385 +
/// Add a local binding when its resolved type is linear.
7386 +
fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node)
7387 +
    throws (ResolveError)
7388 +
{
7389 +
    let sym = symbolFor(checker.resolver, node) else return;
7390 +
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7391 +
    if not isLinear(ty) {
7392 +
        return;
7393 +
    }
7394 +
    if env.len >= MAX_LINEAR_BINDINGS {
7395 +
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7396 +
    }
7397 +
    set env.symbols[env.len] = sym;
7398 +
    set env.available |= (1 as u64) << (env.len as u64);
7399 +
    set env.len += 1;
7400 +
}
7401 +
7402 +
/// Require all bindings introduced after `start` to have been consumed.
7403 +
fn finishLinearScope(
7404 +
    checker: *mut LinearChecker,
7405 +
    env: *mut LinearEnv,
7406 +
    start: u32,
7407 +
) throws (ResolveError) {
7408 +
    if not env.terminated {
7409 +
        for i in start..env.len {
7410 +
            if linearBindingAvailable(env, i) {
7411 +
                let sym = env.symbols[i] else panic "finishLinearScope: missing symbol";
7412 +
                throw emitError(
7413 +
                    checker.resolver,
7414 +
                    sym.node,
7415 +
                    ErrorKind::LinearNotConsumed(sym.name),
7416 +
                );
7417 +
            }
7418 +
        }
7419 +
    }
7420 +
    set env.len = start;
7421 +
}
7422 +
7423 +
/// Consume a tracked identifier exactly once.
7424 +
fn consumeLinearIdent(
7425 +
    checker: *mut LinearChecker,
7426 +
    env: *mut LinearEnv,
7427 +
    node: *ast::Node,
7428 +
) throws (ResolveError) {
7429 +
    let sym = symbolFor(checker.resolver, node) else return;
7430 +
    let index = findLinearBinding(env, sym) else return;
7431 +
    if not linearBindingAvailable(env, index) {
7432 +
        throw emitError(
7433 +
            checker.resolver,
7434 +
            node,
7435 +
            ErrorKind::LinearUseAfterConsume(sym.name),
7436 +
        );
7437 +
    }
7438 +
    set env.available &= ~((1 as u64) << (index as u64));
7439 +
}
7440 +
7441 +
/// Verify that two live branches agree on every outer binding.
7442 +
fn joinLinearBranches(
7443 +
    checker: *mut LinearChecker,
7444 +
    env: *mut LinearEnv,
7445 +
    left: LinearEnv,
7446 +
    right: LinearEnv,
7447 +
    node: *ast::Node,
7448 +
) throws (ResolveError) {
7449 +
    if left.terminated and right.terminated {
7450 +
        set *env = left;
7451 +
        set env.terminated = true;
7452 +
        return;
7453 +
    }
7454 +
    if left.terminated {
7455 +
        set *env = right;
7456 +
        return;
7457 +
    }
7458 +
    if right.terminated {
7459 +
        set *env = left;
7460 +
        return;
7461 +
    }
7462 +
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7463 +
    for i in 0..left.len {
7464 +
        if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) {
7465 +
            let sym = left.symbols[i] else panic "joinLinearBranches: missing symbol";
7466 +
            throw emitError(
7467 +
                checker.resolver,
7468 +
                node,
7469 +
                ErrorKind::LinearBranchMismatch(sym.name),
7470 +
            );
7471 +
        }
7472 +
    }
7473 +
    set *env = left;
7474 +
}
7475 +
7476 +
/// Require all current bindings to be consumed at a function exit.
7477 +
fn finishLinearExit(
7478 +
    checker: *mut LinearChecker,
7479 +
    env: *mut LinearEnv,
7480 +
) throws (ResolveError) {
7481 +
    for i in 0..env.len {
7482 +
        if linearBindingAvailable(env, i) {
7483 +
            let sym = env.symbols[i] else panic "finishLinearExit: missing symbol";
7484 +
            throw emitError(
7485 +
                checker.resolver,
7486 +
                sym.node,
7487 +
                ErrorKind::LinearNotConsumed(sym.name),
7488 +
            );
7489 +
        }
7490 +
    }
7491 +
    set env.terminated = true;
7492 +
}
7493 +
7494 +
/// Find the local root borrowed or consumed by an argument expression.
7495 +
fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7496 +
    match node.value {
7497 +
        case ast::NodeValue::Ident(_) => return symbolFor(self, node),
7498 +
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7499 +
        case ast::NodeValue::FieldAccess(access) =>
7500 +
            return linearRootSymbol(self, access.parent),
7501 +
        case ast::NodeValue::Subscript { container, .. } =>
7502 +
            return linearRootSymbol(self, container),
7503 +
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
7504 +
        else => return nil,
7505 +
    }
7506 +
}
7507 +
7508 +
/// Add the value identifiers introduced by a pattern.
7509 +
fn addLinearPatternBindings(
7510 +
    checker: *mut LinearChecker,
7511 +
    env: *mut LinearEnv,
7512 +
    pattern: *ast::Node,
7513 +
) throws (ResolveError) {
7514 +
    match pattern.value {
7515 +
        case ast::NodeValue::Ident(_) => try addLinearBinding(checker, env, pattern),
7516 +
        case ast::NodeValue::Call(call) => {
7517 +
            for arg in call.args {
7518 +
                try addLinearPatternBindings(checker, env, arg);
7519 +
            }
7520 +
        }
7521 +
        case ast::NodeValue::RecordLit(lit) => {
7522 +
            for fieldNode in lit.fields {
7523 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7524 +
                    else panic "addLinearPatternBindings: expected field";
7525 +
                try addLinearPatternBindings(checker, env, field.value);
7526 +
            }
7527 +
        }
7528 +
        case ast::NodeValue::ArrayLit(items) => {
7529 +
            for item in items {
7530 +
                try addLinearPatternBindings(checker, env, item);
7531 +
            }
7532 +
        }
7533 +
        else => {}
7534 +
    }
7535 +
}
7536 +
7537 +
/// Check a lexical block and exact-use of locals introduced in it.
7538 +
fn checkLinearBlock(
7539 +
    checker: *mut LinearChecker,
7540 +
    env: *mut LinearEnv,
7541 +
    node: *ast::Node,
7542 +
) throws (ResolveError) {
7543 +
    let start = env.len;
7544 +
    let case ast::NodeValue::Block(block) = node.value
7545 +
        else panic "checkLinearBlock: expected block";
7546 +
    for stmt in block.statements {
7547 +
        if env.terminated {
7548 +
            break;
7549 +
        }
7550 +
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
7551 +
    }
7552 +
    try finishLinearScope(checker, env, start);
7553 +
}
7554 +
7555 +
/// Push a repeated-control-flow boundary.
7556 +
fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) {
7557 +
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
7558 +
    let depth = checker.loopDepth;
7559 +
    set checker.loopMarks[depth] = env.len;
7560 +
    set checker.loopAvailable[depth] = env.available;
7561 +
    set checker.loopExitAvailable[depth] = env.available;
7562 +
    set checker.loopHasNaturalExit[depth] = false;
7563 +
    set checker.loopBreakSeen[depth] = false;
7564 +
    set checker.loopDepth += 1;
7565 +
}
7566 +
7567 +
/// Require a repeated body's outer bindings to match its entry state.
7568 +
fn checkLinearLoopBackEdge(
7569 +
    checker: *mut LinearChecker,
7570 +
    env: *LinearEnv,
7571 +
    node: *ast::Node,
7572 +
) throws (ResolveError) {
7573 +
    if env.terminated {
7574 +
        return;
7575 +
    }
7576 +
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
7577 +
    let depth = checker.loopDepth - 1;
7578 +
    let mark = checker.loopMarks[depth];
7579 +
    let entryAvailable = checker.loopAvailable[depth];
7580 +
    for i in 0..mark {
7581 +
        let bit = (1 as u64) << (i as u64);
7582 +
        if (env.available & bit) <> (entryAvailable & bit) {
7583 +
            let sym = env.symbols[i] else panic "checkLinearLoopBackEdge: missing symbol";
7584 +
            throw emitError(
7585 +
                checker.resolver,
7586 +
                node,
7587 +
                ErrorKind::LinearBranchMismatch(sym.name),
7588 +
            );
7589 +
        }
7590 +
    }
7591 +
}
7592 +
7593 +
/// Record the ownership state of a loop's condition-false exit.
7594 +
fn setLinearLoopNaturalExit(checker: *mut LinearChecker, env: *LinearEnv) {
7595 +
    assert checker.loopDepth > 0, "linear loop exit outside loop";
7596 +
    let depth = checker.loopDepth - 1;
7597 +
    set checker.loopExitAvailable[depth] = env.available;
7598 +
    set checker.loopHasNaturalExit[depth] = true;
7599 +
}
7600 +
7601 +
/// Require a break exit to agree with every other exit from this loop.
7602 +
fn checkLinearLoopBreak(
7603 +
    checker: *mut LinearChecker,
7604 +
    env: *LinearEnv,
7605 +
    node: *ast::Node,
7606 +
) throws (ResolveError) {
7607 +
    assert checker.loopDepth > 0, "linear loop break outside loop";
7608 +
    let depth = checker.loopDepth - 1;
7609 +
    let mark = checker.loopMarks[depth];
7610 +
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
7611 +
        let expected = checker.loopExitAvailable[depth];
7612 +
        for i in 0..mark {
7613 +
            let bit = (1 as u64) << (i as u64);
7614 +
            if (env.available & bit) <> (expected & bit) {
7615 +
                let sym = env.symbols[i] else panic "checkLinearLoopBreak: missing symbol";
7616 +
                throw emitError(
7617 +
                    checker.resolver,
7618 +
                    node,
7619 +
                    ErrorKind::LinearBranchMismatch(sym.name),
7620 +
                );
7621 +
            }
7622 +
        }
7623 +
    } else {
7624 +
        set checker.loopExitAvailable[depth] = env.available;
7625 +
    }
7626 +
    set checker.loopBreakSeen[depth] = true;
7627 +
}
7628 +
7629 +
/// Pop a repeated-control-flow boundary.
7630 +
fn exitLinearLoop(checker: *mut LinearChecker) {
7631 +
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
7632 +
    set checker.loopDepth -= 1;
7633 +
}
7634 +
7635 +
/// Check a conditional and merge its ownership states.
7636 +
fn checkLinearIf(
7637 +
    checker: *mut LinearChecker,
7638 +
    env: *mut LinearEnv,
7639 +
    node: *ast::Node,
7640 +
    conditional: ast::If,
7641 +
) throws (ResolveError) {
7642 +
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7643 +
    let base = *env;
7644 +
    let mut thenEnv = base;
7645 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7646 +
    let mut elseEnv = base;
7647 +
    if let branch = conditional.elseBranch {
7648 +
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7649 +
    }
7650 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7651 +
}
7652 +
7653 +
/// Check an expression conditional and merge its ownership states.
7654 +
fn checkLinearCondExpr(
7655 +
    checker: *mut LinearChecker,
7656 +
    env: *mut LinearEnv,
7657 +
    node: *ast::Node,
7658 +
    conditional: ast::CondExpr,
7659 +
    usage: LinearUse,
7660 +
) throws (ResolveError) {
7661 +
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7662 +
    let base = *env;
7663 +
    let mut thenEnv = base;
7664 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
7665 +
    let mut elseEnv = base;
7666 +
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7667 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7668 +
}
7669 +
7670 +
/// Check a match expression, including ownership transferred into patterns.
7671 +
fn checkLinearMatch(
7672 +
    checker: *mut LinearChecker,
7673 +
    env: *mut LinearEnv,
7674 +
    node: *ast::Node,
7675 +
    matchExpr: ast::Match,
7676 +
) throws (ResolveError) {
7677 +
    try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume);
7678 +
    let base = *env;
7679 +
    let mut haveResult = false;
7680 +
    let mut result = base;
7681 +
    for prongNode in matchExpr.prongs {
7682 +
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
7683 +
            else panic "checkLinearMatch: expected prong";
7684 +
        let mut branch = base;
7685 +
        let bindingsStart = branch.len;
7686 +
        match prong.arm {
7687 +
            case ast::ProngArm::Case(patterns) => {
7688 +
                for pattern in patterns {
7689 +
                    try addLinearPatternBindings(checker, &mut branch, pattern);
7690 +
                }
7691 +
            }
7692 +
            case ast::ProngArm::Binding(binding) => {
7693 +
                try addLinearPatternBindings(checker, &mut branch, binding);
7694 +
            }
7695 +
            case ast::ProngArm::Else => {}
7696 +
        }
7697 +
        if prong.guard <> nil and branch.len > bindingsStart {
7698 +
            throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7699 +
        }
7700 +
        if let guard = prong.guard {
7701 +
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
7702 +
        }
7703 +
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
7704 +
        try finishLinearScope(checker, &mut branch, bindingsStart);
7705 +
        if haveResult {
7706 +
            try joinLinearBranches(checker, &mut result, result, branch, node);
7707 +
        } else {
7708 +
            set result = branch;
7709 +
            set haveResult = true;
7710 +
        }
7711 +
    }
7712 +
    if haveResult {
7713 +
        set *env = result;
7714 +
    }
7715 +
}
7716 +
7717 +
/// Check call-scoped loans and argument ownership transfers.
7718 +
fn checkLinearCall(
7719 +
    checker: *mut LinearChecker,
7720 +
    env: *mut LinearEnv,
7721 +
    node: *ast::Node,
7722 +
    call: ast::Call,
7723 +
) throws (ResolveError) {
7724 +
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
7725 +
    let calleeTy = typeFor(checker.resolver, call.callee) else {
7726 +
        throw emitError(checker.resolver, call.callee, ErrorKind::Internal);
7727 +
    };
7728 +
    let case Type::Fn(info) = calleeTy else {
7729 +
        for arg in call.args {
7730 +
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
7731 +
        }
7732 +
        return;
7733 +
    };
7734 +
    let mut roots: [?*mut Symbol; MAX_FN_PARAMS + 1] = undefined;
7735 +
    let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined;
7736 +
    let mut rootsLen: u32 = 0;
7737 +
7738 +
    // Method function types exclude their implicit receiver. Account for it
7739 +
    // explicitly so owning receivers are consumed and reference receivers
7740 +
    // participate in call-scoped loan conflict checks.
7741 +
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
7742 +
        let mut receiverClass = types::PointerClass::Unsafe;
7743 +
        let mut receiverMutable = false;
7744 +
        let mut haveReceiver = false;
7745 +
        match checker.resolver.nodeData.entries[node.id].extra {
7746 +
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
7747 +
                let method = &traitInfo.methods[methodIndex];
7748 +
                set receiverClass = method.receiverClass;
7749 +
                set receiverMutable = method.mutable;
7750 +
                set haveReceiver = true;
7751 +
            }
7752 +
            case NodeExtra::MethodCall { method } => {
7753 +
                set receiverClass = method.receiverClass;
7754 +
                set receiverMutable = method.mutable;
7755 +
                set haveReceiver = true;
7756 +
            }
7757 +
            else => {}
7758 +
        }
7759 +
        if haveReceiver {
7760 +
            if receiverClass <> types::PointerClass::Unsafe {
7761 +
                let root = linearRootSymbol(checker.resolver, access.parent);
7762 +
                if let rootSym = root {
7763 +
                    set roots[rootsLen] = rootSym;
7764 +
                    set exclusive[rootsLen] =
7765 +
                        receiverClass == types::PointerClass::Owned or receiverMutable;
7766 +
                    set rootsLen += 1;
7767 +
                }
7768 +
            }
7769 +
            if receiverClass == types::PointerClass::Ref {
7770 +
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
7771 +
            } else if receiverClass == types::PointerClass::Owned {
7772 +
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
7773 +
            }
7774 +
        }
7775 +
    }
7776 +
7777 +
    for arg, i in call.args {
7778 +
        let expected = *info.paramTypes[i];
7779 +
        let root = linearRootSymbol(checker.resolver, arg);
7780 +
        let mut argExclusive = isLinear(expected);
7781 +
        if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected {
7782 +
            set argExclusive = mutable;
7783 +
        } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected {
7784 +
            set argExclusive = mutable;
7785 +
        } else if let case Type::TraitObject {
7786 +
            class: types::PointerClass::Ref, mutable, ..
7787 +
        } = expected {
7788 +
            set argExclusive = mutable;
7789 +
        }
7790 +
        if not isUnsafePointerType(expected) {
7791 +
            if let rootSym = root {
7792 +
                for j in 0..rootsLen {
7793 +
                    if let previous = roots[j] {
7794 +
                        if previous == rootSym and (exclusive[j] or argExclusive) {
7795 +
                            throw emitError(
7796 +
                                checker.resolver,
7797 +
                                arg,
7798 +
                                ErrorKind::BorrowConflict(rootSym.name),
7799 +
                            );
7800 +
                        }
7801 +
                    }
7802 +
                }
7803 +
                set roots[rootsLen] = rootSym;
7804 +
                set exclusive[rootsLen] = argExclusive;
7805 +
                set rootsLen += 1;
7806 +
            }
7807 +
        }
7808 +
        if isRefType(expected) {
7809 +
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
7810 +
        } else {
7811 +
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
7812 +
        }
7813 +
    }
7814 +
}
7815 +
7816 +
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
7817 +
fn checkLinearIfLet(
7818 +
    checker: *mut LinearChecker,
7819 +
    env: *mut LinearEnv,
7820 +
    node: *ast::Node,
7821 +
    conditional: ast::IfLet,
7822 +
) throws (ResolveError) {
7823 +
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
7824 +
        isLinear(subjectTy)
7825 +
    {
7826 +
        throw emitError(
7827 +
            checker.resolver,
7828 +
            conditional.pattern.scrutinee,
7829 +
            ErrorKind::LinearPartialMove,
7830 +
        );
7831 +
    }
7832 +
    try checkLinearNode(
7833 +
        checker,
7834 +
        env,
7835 +
        conditional.pattern.scrutinee,
7836 +
        LinearUse::Consume,
7837 +
    );
7838 +
    let base = *env;
7839 +
    let mut thenEnv = base;
7840 +
    let bindingsStart = thenEnv.len;
7841 +
    try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern);
7842 +
    if let guard = conditional.pattern.guard {
7843 +
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
7844 +
    }
7845 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7846 +
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
7847 +
    let mut elseEnv = base;
7848 +
    if let branch = conditional.elseBranch {
7849 +
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7850 +
    }
7851 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7852 +
}
7853 +
7854 +
/// Check one expression or statement under an ownership-use context.
7855 +
fn checkLinearNode(
7856 +
    checker: *mut LinearChecker,
7857 +
    env: *mut LinearEnv,
7858 +
    node: *ast::Node,
7859 +
    usage: LinearUse,
7860 +
) throws (ResolveError) {
7861 +
    if env.terminated {
7862 +
        return;
7863 +
    }
7864 +
    match node.value {
7865 +
        case ast::NodeValue::Ident(_) => {
7866 +
            if usage == LinearUse::Consume {
7867 +
                try consumeLinearIdent(checker, env, node);
7868 +
            }
7869 +
        }
7870 +
        case ast::NodeValue::ExprStmt(expr) => {
7871 +
            if let exprTy = typeFor(checker.resolver, expr) {
7872 +
                if isLinear(exprTy) {
7873 +
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
7874 +
                }
7875 +
            }
7876 +
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
7877 +
        }
7878 +
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
7879 +
        case ast::NodeValue::Let(binding) => {
7880 +
            if let case ast::NodeValue::Undef = binding.value.value {
7881 +
                if let bindingTy = typeFor(checker.resolver, binding.ident);
7882 +
                    isLinear(bindingTy)
7883 +
                {
7884 +
                    throw emitError(
7885 +
                        checker.resolver,
7886 +
                        binding.value,
7887 +
                        ErrorKind::LinearUndefined,
7888 +
                    );
7889 +
                }
7890 +
            }
7891 +
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
7892 +
            try addLinearBinding(checker, env, node);
7893 +
        }
7894 +
        case ast::NodeValue::Assign(assign) => {
7895 +
            let mut target: ?u32 = nil;
7896 +
            if let leftTy = typeFor(checker.resolver, assign.left) {
7897 +
                if isLinear(leftTy) {
7898 +
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
7899 +
                        if let sym = symbolFor(checker.resolver, assign.left) {
7900 +
                            set target = findLinearBinding(env, sym);
7901 +
                        }
7902 +
                    }
7903 +
                    if target == nil {
7904 +
                        throw emitError(
7905 +
                            checker.resolver,
7906 +
                            assign.left,
7907 +
                            ErrorKind::LinearOverwrite,
7908 +
                        );
7909 +
                    }
7910 +
                }
7911 +
            }
7912 +
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
7913 +
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
7914 +
            if let index = target {
7915 +
                if linearBindingAvailable(env, index) {
7916 +
                    throw emitError(
7917 +
                        checker.resolver,
7918 +
                        assign.left,
7919 +
                        ErrorKind::LinearOverwrite,
7920 +
                    );
7921 +
                }
7922 +
                set env.available |= (1 as u64) << (index as u64);
7923 +
            }
7924 +
        }
7925 +
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
7926 +
        case ast::NodeValue::AddressOf(addr) => {
7927 +
            try checkLinearNode(checker, env, addr.target, LinearUse::Borrow);
7928 +
        }
7929 +
        case ast::NodeValue::Deref(target) => {
7930 +
            if let resultTy = typeFor(checker.resolver, node) {
7931 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7932 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7933 +
                }
7934 +
            }
7935 +
            try checkLinearNode(checker, env, target, LinearUse::Observe);
7936 +
        }
7937 +
        case ast::NodeValue::FieldAccess(access) => {
7938 +
            if let resultTy = typeFor(checker.resolver, node) {
7939 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7940 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7941 +
                }
7942 +
            }
7943 +
            try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
7944 +
        }
7945 +
        case ast::NodeValue::ScopeAccess(_) => {}
7946 +
        case ast::NodeValue::Subscript { container, index } => {
7947 +
            if let resultTy = typeFor(checker.resolver, node) {
7948 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7949 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7950 +
                }
7951 +
            }
7952 +
            try checkLinearNode(checker, env, container, LinearUse::Observe);
7953 +
            try checkLinearNode(checker, env, index, LinearUse::Consume);
7954 +
        }
7955 +
        case ast::NodeValue::RecordLit(lit) => {
7956 +
            for fieldNode in lit.fields {
7957 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7958 +
                    else panic "checkLinearNode: expected field";
7959 +
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
7960 +
            }
7961 +
        }
7962 +
        case ast::NodeValue::ArrayLit(items) => {
7963 +
            for item in items {
7964 +
                try checkLinearNode(checker, env, item, LinearUse::Consume);
7965 +
            }
7966 +
        }
7967 +
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
7968 +
            if let itemTy = typeFor(checker.resolver, repeat.item) {
7969 +
                if isLinear(itemTy) {
7970 +
                    throw emitError(
7971 +
                        checker.resolver,
7972 +
                        repeat.item,
7973 +
                        ErrorKind::LinearDiscard,
7974 +
                    );
7975 +
                }
7976 +
            }
7977 +
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
7978 +
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
7979 +
        }
7980 +
        case ast::NodeValue::BinOp(op) => {
7981 +
            try checkLinearNode(checker, env, op.left, LinearUse::Consume);
7982 +
            try checkLinearNode(checker, env, op.right, LinearUse::Consume);
7983 +
        }
7984 +
        case ast::NodeValue::UnOp(op) => {
7985 +
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
7986 +
        }
7987 +
        case ast::NodeValue::As(expr) => {
7988 +
            try checkLinearNode(checker, env, expr.value, LinearUse::Consume);
7989 +
        }
7990 +
        case ast::NodeValue::Range(range) => {
7991 +
            if let start = range.start {
7992 +
                try checkLinearNode(checker, env, start, LinearUse::Consume);
7993 +
            }
7994 +
            if let end = range.end {
7995 +
                try checkLinearNode(checker, env, end, LinearUse::Consume);
7996 +
            }
7997 +
        }
7998 +
        case ast::NodeValue::BuiltinCall { args, .. } => {
7999 +
            for arg in args {
8000 +
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
8001 +
            }
8002 +
        }
8003 +
        case ast::NodeValue::If(conditional) => {
8004 +
            try checkLinearIf(checker, env, node, conditional);
8005 +
        }
8006 +
        case ast::NodeValue::CondExpr(conditional) => {
8007 +
            try checkLinearCondExpr(checker, env, node, conditional, usage);
8008 +
        }
8009 +
        case ast::NodeValue::IfLet(conditional) => {
8010 +
            try checkLinearIfLet(checker, env, node, conditional);
8011 +
        }
8012 +
        case ast::NodeValue::LetElse(binding) => {
8013 +
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
8014 +
                isLinear(subjectTy)
8015 +
            {
8016 +
                throw emitError(
8017 +
                    checker.resolver,
8018 +
                    binding.pattern.scrutinee,
8019 +
                    ErrorKind::LinearPartialMove,
8020 +
                );
8021 +
            }
8022 +
            try checkLinearNode(
8023 +
                checker,
8024 +
                env,
8025 +
                binding.pattern.scrutinee,
8026 +
                LinearUse::Consume,
8027 +
            );
8028 +
            let base = *env;
8029 +
            let mut guardedEnv = base;
8030 +
            if let guard = binding.pattern.guard {
8031 +
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
8032 +
            }
8033 +
            let mut successEnv = guardedEnv;
8034 +
            try addLinearPatternBindings(
8035 +
                checker,
8036 +
                &mut successEnv,
8037 +
                binding.pattern.pattern,
8038 +
            );
8039 +
            let mut fallbackEnv = base;
8040 +
            try checkLinearNode(
8041 +
                checker,
8042 +
                &mut fallbackEnv,
8043 +
                binding.elseBranch,
8044 +
                LinearUse::Consume,
8045 +
            );
8046 +
            if binding.pattern.guard <> nil {
8047 +
                let mut guardFallbackEnv = guardedEnv;
8048 +
                try checkLinearNode(
8049 +
                    checker,
8050 +
                    &mut guardFallbackEnv,
8051 +
                    binding.elseBranch,
8052 +
                    LinearUse::Consume,
8053 +
                );
8054 +
                try joinLinearBranches(
8055 +
                    checker,
8056 +
                    &mut fallbackEnv,
8057 +
                    fallbackEnv,
8058 +
                    guardFallbackEnv,
8059 +
                    binding.elseBranch,
8060 +
                );
8061 +
            }
8062 +
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8063 +
                try addLinearPatternBindings(
8064 +
                    checker,
8065 +
                    &mut fallbackEnv,
8066 +
                    binding.pattern.pattern,
8067 +
                );
8068 +
            }
8069 +
            try joinLinearBranches(checker, env, successEnv, fallbackEnv, node);
8070 +
        }
8071 +
        case ast::NodeValue::Match(matchExpr) => {
8072 +
            try checkLinearMatch(checker, env, node, matchExpr);
8073 +
        }
8074 +
        case ast::NodeValue::Try(tryExpr) => {
8075 +
            try checkLinearNode(checker, env, tryExpr.expr, usage);
8076 +
            let success = *env;
8077 +
            for catchNode in tryExpr.catches {
8078 +
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
8079 +
                    else panic "checkLinearNode: expected catch";
8080 +
                let mut branch = success;
8081 +
                let start = branch.len;
8082 +
                if let binding = catchClause.binding {
8083 +
                    try addLinearBinding(checker, &mut branch, binding);
8084 +
                }
8085 +
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8086 +
                try finishLinearScope(checker, &mut branch, start);
8087 +
                try joinLinearBranches(checker, env, *env, branch, node);
8088 +
            }
8089 +
        }
8090 +
        case ast::NodeValue::While(whileStmt) => {
8091 +
            enterLinearLoop(checker, env);
8092 +
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8093 +
            let conditionExit = *env;
8094 +
            setLinearLoopNaturalExit(checker, &conditionExit);
8095 +
            let mut bodyEnv = conditionExit;
8096 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8097 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8098 +
            exitLinearLoop(checker);
8099 +
            set *env = conditionExit;
8100 +
            if let elseBranch = whileStmt.elseBranch {
8101 +
                let mut elseEnv = conditionExit;
8102 +
                try checkLinearNode(
8103 +
                    checker,
8104 +
                    &mut elseEnv,
8105 +
                    elseBranch,
8106 +
                    LinearUse::Discard,
8107 +
                );
8108 +
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8109 +
            }
8110 +
        }
8111 +
        case ast::NodeValue::WhileLet(whileStmt) => {
8112 +
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8113 +
                isLinear(subjectTy)
8114 +
            {
8115 +
                throw emitError(
8116 +
                    checker.resolver,
8117 +
                    whileStmt.pattern.scrutinee,
8118 +
                    ErrorKind::LinearPartialMove,
8119 +
                );
8120 +
            }
8121 +
            let base = *env;
8122 +
            enterLinearLoop(checker, env);
8123 +
            let mut bodyEnv = base;
8124 +
            try checkLinearNode(
8125 +
                checker,
8126 +
                &mut bodyEnv,
8127 +
                whileStmt.pattern.scrutinee,
8128 +
                LinearUse::Consume,
8129 +
            );
8130 +
            let mut conditionExit = bodyEnv;
8131 +
            let start = bodyEnv.len;
8132 +
            try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern);
8133 +
            if let guard = whileStmt.pattern.guard {
8134 +
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8135 +
                let mut guardExit = bodyEnv;
8136 +
                try finishLinearScope(checker, &mut guardExit, start);
8137 +
                try joinLinearBranches(
8138 +
                    checker,
8139 +
                    &mut conditionExit,
8140 +
                    conditionExit,
8141 +
                    guardExit,
8142 +
                    guard,
8143 +
                );
8144 +
            }
8145 +
            setLinearLoopNaturalExit(checker, &conditionExit);
8146 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8147 +
            try finishLinearScope(checker, &mut bodyEnv, start);
8148 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8149 +
            exitLinearLoop(checker);
8150 +
            set *env = conditionExit;
8151 +
            if let elseBranch = whileStmt.elseBranch {
8152 +
                let mut elseEnv = conditionExit;
8153 +
                try checkLinearNode(
8154 +
                    checker,
8155 +
                    &mut elseEnv,
8156 +
                    elseBranch,
8157 +
                    LinearUse::Discard,
8158 +
                );
8159 +
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8160 +
            }
8161 +
        }
8162 +
        case ast::NodeValue::For(forStmt) => {
8163 +
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8164 +
                if isLinear(iterableTy) {
8165 +
                    throw emitError(
8166 +
                        checker.resolver,
8167 +
                        forStmt.iterable,
8168 +
                        ErrorKind::LinearPartialMove,
8169 +
                    );
8170 +
                }
8171 +
            }
8172 +
            try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
8173 +
            let base = *env;
8174 +
            enterLinearLoop(checker, env);
8175 +
            setLinearLoopNaturalExit(checker, &base);
8176 +
            let mut bodyEnv = base;
8177 +
            let start = bodyEnv.len;
8178 +
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
8179 +
            if let index = forStmt.index {
8180 +
                try addLinearBinding(checker, &mut bodyEnv, index);
8181 +
            }
8182 +
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
8183 +
            try finishLinearScope(checker, &mut bodyEnv, start);
8184 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
8185 +
            exitLinearLoop(checker);
8186 +
            set *env = base;
8187 +
            if let elseBranch = forStmt.elseBranch {
8188 +
                let mut elseEnv = base;
8189 +
                try checkLinearNode(
8190 +
                    checker,
8191 +
                    &mut elseEnv,
8192 +
                    elseBranch,
8193 +
                    LinearUse::Discard,
8194 +
                );
8195 +
                try joinLinearBranches(checker, env, base, elseEnv, node);
8196 +
            }
8197 +
        }
8198 +
        case ast::NodeValue::Loop { body } => {
8199 +
            let base = *env;
8200 +
            enterLinearLoop(checker, env);
8201 +
            let mut bodyEnv = base;
8202 +
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
8203 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
8204 +
            let depth = checker.loopDepth - 1;
8205 +
            let breakSeen = checker.loopBreakSeen[depth];
8206 +
            let exitAvailable = checker.loopExitAvailable[depth];
8207 +
            exitLinearLoop(checker);
8208 +
            set *env = base;
8209 +
            if breakSeen {
8210 +
                set env.available = exitAvailable;
8211 +
            } else {
8212 +
                set env.terminated = true;
8213 +
            }
8214 +
        }
8215 +
        case ast::NodeValue::Break => {
8216 +
            assert checker.loopDepth > 0, "linear loop control outside loop";
8217 +
            let start = checker.loopMarks[checker.loopDepth - 1];
8218 +
            try finishLinearScope(checker, env, start);
8219 +
            try checkLinearLoopBreak(checker, env, node);
8220 +
            set env.terminated = true;
8221 +
        }
8222 +
        case ast::NodeValue::Continue => {
8223 +
            assert checker.loopDepth > 0, "linear loop control outside loop";
8224 +
            let start = checker.loopMarks[checker.loopDepth - 1];
8225 +
            try finishLinearScope(checker, env, start);
8226 +
            try checkLinearLoopBackEdge(checker, env, node);
8227 +
            set env.terminated = true;
8228 +
        }
8229 +
        case ast::NodeValue::Return { value } => {
8230 +
            if let expr = value {
8231 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8232 +
            }
8233 +
            try finishLinearExit(checker, env);
8234 +
        }
8235 +
        case ast::NodeValue::Throw { expr } => {
8236 +
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8237 +
            try finishLinearExit(checker, env);
8238 +
        }
8239 +
        case ast::NodeValue::Panic { message } => {
8240 +
            if let expr = message {
8241 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8242 +
            }
8243 +
            set env.terminated = true;
8244 +
        }
8245 +
        case ast::NodeValue::Assert { condition, message } => {
8246 +
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
8247 +
            if let expr = message {
8248 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8249 +
            }
8250 +
        }
8251 +
        else => {}
8252 +
    }
8253 +
}
8254 +
8255 +
/// Check exact-use ownership for one resolved function.
8256 +
fn checkLinearFn(
8257 +
    self: *mut Resolver,
8258 +
    receiver: ?*ast::Node,
8259 +
    params: *mut [*ast::Node],
8260 +
    body: *ast::Node,
8261 +
) throws (ResolveError) {
8262 +
    let mut checker = LinearChecker {
8263 +
        resolver: self,
8264 +
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
8265 +
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8266 +
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8267 +
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
8268 +
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
8269 +
        loopDepth: 0,
8270 +
    };
8271 +
    let mut env = LinearEnv {
8272 +
        symbols: [nil; MAX_LINEAR_BINDINGS],
8273 +
        available: 0,
8274 +
        len: 0,
8275 +
        terminated: false,
8276 +
    };
8277 +
    if let receiverNode = receiver {
8278 +
        try addLinearBinding(&mut checker, &mut env, receiverNode);
8279 +
    }
8280 +
    for paramNode in params {
8281 +
        let case ast::NodeValue::FnParam(_) = paramNode.value
8282 +
            else panic "checkLinearFn: expected parameter";
8283 +
        try addLinearBinding(&mut checker, &mut env, paramNode);
8284 +
    }
8285 +
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
8286 +
    try finishLinearScope(&mut checker, &mut env, 0);
8287 +
}
8288 +
6903 8289
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
6904 8290
fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
6905 8291
    for stmt in block.statements {
6906 8292
        try visitDef(self, stmt);
6907 8293
    }
lib/std/lang/resolver/printer.rad +59 -9
1 1
//! Resolver scope printer.
2 2
use std::io;
3 3
use std::lang::ast;
4 +
use std::lang::types;
4 5
use std::lang::scanner;
5 6
use std::lang::module;
6 7
7 8
/// Print a span in `@offset:length` form.
8 9
fn printSpan(span: ast::Span) {
28 29
    io::print(prefix);
29 30
    io::print(name);
30 31
    io::print("'");
31 32
}
32 33
33 -
/// Print `*` or `*mut ` depending on mutability.
34 -
fn printPtrPrefix(mutable: bool) {
35 -
    io::print("*");
34 +
/// Print a pointer-like type prefix.
35 +
fn printPtrPrefix(class: types::PointerClass, mutable: bool) {
36 +
    match class {
37 +
        case types::PointerClass::Owned => io::print("*"),
38 +
        case types::PointerClass::Ref => io::print("&"),
39 +
        case types::PointerClass::Unsafe => io::print("*unsafe "),
40 +
    }
36 41
    if mutable {
37 42
        io::print("mut ");
38 43
    }
39 44
}
40 45
97 102
            io::print("i32");
98 103
        }
99 104
        case super::Type::I64 => {
100 105
            io::print("i64");
101 106
        }
102 -
        case super::Type::Pointer { target, mutable, .. } => {
103 -
            printPtrPrefix(mutable);
107 +
        case super::Type::Pointer { class, target, mutable } => {
108 +
            printPtrPrefix(class, mutable);
104 109
            printTypeBody(*target, brief);
105 110
        }
106 -
        case super::Type::Slice { item, mutable, .. } => {
107 -
            printPtrPrefix(mutable);
111 +
        case super::Type::Slice { class, item, mutable } => {
112 +
            printPtrPrefix(class, mutable);
108 113
            io::print("[");
109 114
            printTypeBody(*item, brief);
110 115
            io::print("]");
111 116
        }
112 117
        case super::Type::Array(array) => {
119 124
        case super::Type::Optional(inner) => {
120 125
            io::print("?");
121 126
            printTypeBody(*inner, brief);
122 127
        }
123 128
        case super::Type::Fn(fnType) => {
129 +
            if fnType.isUnsafe {
130 +
                io::print("unsafe ");
131 +
            }
124 132
            io::print("fn(");
125 133
            for paramType, i in fnType.paramTypes {
126 134
                if i > 0 {
127 135
                    io::print(", ");
128 136
                }
146 154
                printNominalTypeName(info);
147 155
            } else {
148 156
                printNominalType(info);
149 157
            }
150 158
        }
151 -
        case super::Type::TraitObject { traitInfo, mutable, .. } => {
152 -
            printPtrPrefix(mutable);
159 +
        case super::Type::TraitObject { class, traitInfo, mutable } => {
160 +
            printPtrPrefix(class, mutable);
153 161
            io::print("opaque ");
154 162
            io::print(traitInfo.name);
155 163
        }
156 164
        case super::Type::Range { start, end } => {
157 165
            if let s = start {
474 482
            io::print("trait name cannot be used as a value");
475 483
        }
476 484
        case super::ErrorKind::TraitReceiverMismatch => {
477 485
            io::print("trait method receiver must be a pointer to the declaring trait");
478 486
        }
487 +
        case super::ErrorKind::TraitMethodSafetyMismatch => {
488 +
            io::print("trait method implementation has mismatched unsafe requirement");
489 +
        }
479 490
        case super::ErrorKind::FnParamOverflow(m) =>
480 491
            printMismatch("too many function parameters", "maximum", m),
481 492
        case super::ErrorKind::FnThrowOverflow(m) =>
482 493
            printMismatch("too many function throws", "maximum", m),
483 494
        case super::ErrorKind::TraitMethodOverflow(m) =>
484 495
            printMismatch("too many trait methods", "maximum", m),
485 496
        case super::ErrorKind::MissingSupertraitInstance(name) => {
486 497
            printQuoted("missing instance for supertrait '", name);
487 498
        }
499 +
        case super::ErrorKind::LinearUseAfterConsume(name) => {
500 +
            printQuoted("linear value used after consumption: '", name);
501 +
        }
502 +
        case super::ErrorKind::LinearNotConsumed(name) => {
503 +
            printQuoted("linear value is not consumed: '", name);
504 +
        }
505 +
        case super::ErrorKind::LinearLetElseMustTerminate => {
506 +
            io::print("`let-else` fallback must terminate control flow");
507 +
        }
508 +
        case super::ErrorKind::LinearBranchMismatch(name) => {
509 +
            printQuoted("linear value has inconsistent branch state: '", name);
510 +
        }
511 +
        case super::ErrorKind::LinearPartialMove => {
512 +
            io::print("cannot move a field out of a linear value");
513 +
        }
514 +
        case super::ErrorKind::LinearDiscard => {
515 +
            io::print("linear value cannot be discarded");
516 +
        }
517 +
        case super::ErrorKind::LinearOverwrite => {
518 +
            io::print("assignment would overwrite a linear value");
519 +
        }
520 +
        case super::ErrorKind::LinearUndefined => {
521 +
            io::print("linear values cannot be undefined");
522 +
        }
523 +
        case super::ErrorKind::InvalidRefPosition => {
524 +
            io::print("reference type is only allowed as a function parameter");
525 +
        }
526 +
        case super::ErrorKind::RefBinding => {
527 +
            io::print("references cannot be bound to locals");
528 +
        }
529 +
        case super::ErrorKind::BorrowConflict(name) => {
530 +
            printQuoted("conflicting call-scoped loans of '", name);
531 +
        }
532 +
        case super::ErrorKind::UnsafeOperation => {
533 +
            io::print("unsafe pointer operation requires an unsafe declaration");
534 +
        }
535 +
        case super::ErrorKind::UnsafeCall => {
536 +
            io::print("calling an unsafe function requires an unsafe declaration");
537 +
        }
488 538
        case super::ErrorKind::Internal => {
489 539
            io::print("internal compiler error");
490 540
        }
491 541
        case super::ErrorKind::RecordFieldOutOfOrder { .. } => {
492 542
            io::print("record field out of order");
lib/std/lang/resolver/tests.rad +324 -4
2 2
3 3
use std::mem;
4 4
use std::testing;
5 5
use std::lang::alloc;
6 6
use std::lang::ast;
7 +
use std::lang::types;
7 8
use std::lang::parser;
8 9
use std::lang::scanner;
9 10
use std::lang::module;
10 11
use std::lang::strings;
11 12
12 13
/// Synthetic file path used for resolver tests.
13 14
constant MODULE_PATH: *[u8] = "/dev/test.rad";
14 15
16 +
/// AST arena storage used by resolver tests.
15 17
static AST_ARENA: [u8; 2097152] = undefined;
18 +
19 +
/// Resolver arena storage used by resolver tests.
16 20
static ARENA_STORAGE: [u8; 2097152] = undefined;
21 +
22 +
/// Node metadata storage used by resolver tests.
17 23
static NODE_DATA_STORAGE: [super::NodeData; 256] = undefined;
24 +
25 +
/// Diagnostic storage used by resolver tests.
18 26
static ERROR_STORAGE: [super::Error; 16] = undefined;
27 +
28 +
/// Package scope used by resolver tests.
19 29
static PKG_SCOPE: super::Scope = undefined;
30 +
31 +
/// Module entries used by resolver tests.
20 32
static MODULE_ENTRIES: [module::ModuleEntry; 8] = undefined;
33 +
34 +
/// Module graph used by resolver tests.
21 35
static MODULE_GRAPH: module::ModuleGraph = undefined;
36 +
37 +
/// Module AST arena storage used by resolver tests.
22 38
static MODULE_ARENA_STORAGE: [u8; 4096] = undefined;
39 +
40 +
/// Module AST arena used by resolver tests.
23 41
static MODULE_ARENA: ast::NodeArena = undefined;
42 +
43 +
/// Interned string pool used by resolver tests.
24 44
static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
25 45
26 46
/// String literals used in tests.
27 47
constant LITERALS: [*[u8]; 15] = [
28 48
    "Ok", "Error", "R", "S",
259 279
        if let case super::ErrorKind::MissingSupertraitInstance(actualName) = *actual {
260 280
            return mem::eq(actualName, expectedName);
261 281
        }
262 282
        return false;
263 283
    }
284 +
    if let case super::ErrorKind::LinearUseAfterConsume(expectedName) = expected {
285 +
        if let case super::ErrorKind::LinearUseAfterConsume(actualName) = *actual {
286 +
            return mem::eq(actualName, expectedName);
287 +
        }
288 +
        return false;
289 +
    }
290 +
    if let case super::ErrorKind::LinearNotConsumed(expectedName) = expected {
291 +
        if let case super::ErrorKind::LinearNotConsumed(actualName) = *actual {
292 +
            return mem::eq(actualName, expectedName);
293 +
        }
294 +
        return false;
295 +
    }
296 +
    if let case super::ErrorKind::LinearBranchMismatch(expectedName) = expected {
297 +
        if let case super::ErrorKind::LinearBranchMismatch(actualName) = *actual {
298 +
            return mem::eq(actualName, expectedName);
299 +
        }
300 +
        return false;
301 +
    }
302 +
    if let case super::ErrorKind::BorrowConflict(expectedName) = expected {
303 +
        if let case super::ErrorKind::BorrowConflict(actualName) = *actual {
304 +
            return mem::eq(actualName, expectedName);
305 +
        }
306 +
        return false;
307 +
    }
264 308
    return *actual == expected;
265 309
}
266 310
267 311
/// Extract the first error and ensure it has the expected kind.
268 312
fn expectErrorKind(result: *TestResult, kind: super::ErrorKind) -> *super::Error
293 337
        else throw testing::TestError::Failed;
294 338
    try testing::expect(mismatch.expected == expected);
295 339
    try testing::expect(mismatch.actual == actual);
296 340
}
297 341
342 +
/// Resolve a program and require successful analysis.
298 343
fn expectAnalyzeOk(program: *[u8]) throws (testing::TestError) {
299 344
    let mut a = testResolver();
300 345
    let result = try resolveProgramStr(&mut a, program);
301 346
    try expectNoErrors(&result);
302 347
}
303 348
349 +
/// Require an inferred integer type mismatch.
304 350
fn expectIntMismatch(program: *[u8], expected: super::Type)
305 351
    throws (testing::TestError)
306 352
{
307 353
    let mut a = testResolver();
308 354
    let result = try resolveProgramStr(&mut a, program);
377 423
    let case super::SymbolData::Type(ty) = sym.data
378 424
        else throw testing::TestError::Failed;
379 425
    return ty;
380 426
}
381 427
428 +
/// Return the resolved type of a syntax node.
382 429
fn typeOf(a: *super::Resolver, node: *ast::Node) -> super::Type
383 430
    throws (testing::TestError)
384 431
{
385 432
    let ty = super::typeFor(a, node)
386 433
        else throw testing::TestError::Failed;
387 434
    return ty;
388 435
}
389 436
437 +
/// Require an array type and return its element type.
390 438
fn expectArrayType(ty: super::Type, length: u32) -> super::Type
391 439
    throws (testing::TestError)
392 440
{
393 441
    let case super::Type::Array(info) = ty
394 442
        else throw testing::TestError::Failed;
395 443
    try testing::expect(info.length == length);
396 444
397 445
    return *info.item;
398 446
}
399 447
448 +
/// Require a slice type and return its element type.
400 449
fn expectSliceType(ty: super::Type, mutable: bool) -> super::Type
401 450
    throws (testing::TestError)
402 451
{
403 452
    let case super::Type::Slice { item, mutable: sliceMut, .. } = ty
404 453
        else throw testing::TestError::Failed;
405 454
    try testing::expect(sliceMut == mutable);
406 455
407 456
    return *item;
408 457
}
409 458
459 +
/// Require a pointer type and return its target type.
410 460
fn expectPointerType(ty: super::Type, mutable: bool) -> super::Type
411 461
    throws (testing::TestError)
412 462
{
413 463
    let case super::Type::Pointer { target, mutable: ptrMut, .. } = ty
414 464
        else throw testing::TestError::Failed;
4331 4381
        else throw testing::TestError::Failed;
4332 4382
    let payloadSym = super::findSymbolInScope(scope, "x")
4333 4383
        else throw testing::TestError::Failed;
4334 4384
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4335 4385
        else throw testing::TestError::Failed;
4336 -
    let target = try expectPointerType(payloadValType, false);
4337 -
    try testing::expect(target == super::Type::I32);
4386 +
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4387 +
        else throw testing::TestError::Failed;
4388 +
    try testing::expect(not mutable);
4389 +
    try testing::expect(*target == super::Type::I32);
4338 4390
}
4339 4391
4340 4392
/// Test `match &mut opt` produces mutable pointer bindings.
4341 4393
@test fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) {
4342 4394
    let mut a = testResolver();
4354 4406
        else throw testing::TestError::Failed;
4355 4407
    let payloadSym = super::findSymbolInScope(scope, "x")
4356 4408
        else throw testing::TestError::Failed;
4357 4409
    let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data
4358 4410
        else throw testing::TestError::Failed;
4359 -
    let target = try expectPointerType(payloadValType, true);
4360 -
    try testing::expect(target == super::Type::I32);
4411 +
    let case super::Type::Pointer { class: types::PointerClass::Ref, target, mutable } = payloadValType
4412 +
        else throw testing::TestError::Failed;
4413 +
    try testing::expect(mutable);
4414 +
    try testing::expect(*target == super::Type::I32);
4361 4415
}
4362 4416
4363 4417
/// Non-constant integer widening must use an explicit cast.
4364 4418
@test fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) {
4365 4419
    {
5107 5161
    try expectConstFold("constant B: u32 = 10; constant C: u32 = B * 2;", 1, 20);
5108 5162
    try expectConstFold("constant D: u32 = 3 + 7;", 0, 10);
5109 5163
    try expectConstFold("constant E: u32 = 2 * 3 + 4;", 0, 10);
5110 5164
    try expectConstFold("constant F: i32 = -(3 + 4);", 0, 7);
5111 5165
}
5166 +
5167 +
/// References cannot escape through return types.
5168 +
@test fn testRefReturnRejected() throws (testing::TestError) {
5169 +
    let mut a = testResolver();
5170 +
    let program = "record Marker: Linear {} fn bad(value: &u32) -> &u32 { return value; }";
5171 +
    let result = try resolveProgramStr(&mut a, program);
5172 +
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5173 +
}
5174 +
5175 +
/// Case-pattern fallbacks must terminate instead of synthesizing bindings.
5176 +
@test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5177 +
    let mut a = testResolver();
5178 +
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
5179 +
    let result = try resolveProgramStr(&mut a, program);
5180 +
    try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
5181 +
}
5182 +
5183 +
/// Case bindings are unavailable on the pattern-failure path.
5184 +
@test fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5185 +
    let mut a = testResolver();
5186 +
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
5187 +
    let result = try resolveProgramStr(&mut a, program);
5188 +
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
5189 +
}
5190 +
5191 +
/// Unsafe pointer dereference requires an unsafe declaration.
5192 +
@test fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5193 +
    let mut a = testResolver();
5194 +
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5195 +
    let result = try resolveProgramStr(&mut a, program);
5196 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5197 +
}
5198 +
5199 +
/// Unsafe pointers remain freely copyable inside an unsafe declaration.
5200 +
@test fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5201 +
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5202 +
    try expectAnalyzeOk(program);
5203 +
}
5204 +
5205 +
/// Safe code cannot call a function that accepts unsafe operations.
5206 +
@test fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5207 +
    let mut a = testResolver();
5208 +
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
5209 +
    let result = try resolveProgramStr(&mut a, program);
5210 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5211 +
}
5212 +
5213 +
/// Unsafe function values retain their call-site safety requirement.
5214 +
@test fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5215 +
    let mut a = testResolver();
5216 +
    let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
5217 +
    let result = try resolveProgramStr(&mut a, program);
5218 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5219 +
}
5220 +
5221 +
/// References cannot be embedded in aggregate fields.
5222 +
@test fn testRefFieldRejected() throws (testing::TestError) {
5223 +
    let mut a = testResolver();
5224 +
    let program = "record Marker: Linear {} record Bad { value: &u32 }";
5225 +
    let result = try resolveProgramStr(&mut a, program);
5226 +
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5227 +
}
5228 +
5229 +
/// Trait methods may use reference receivers.
5230 +
@test fn testTraitRefReceiver() throws (testing::TestError) {
5231 +
    let program = "record Marker: Linear {} record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: &Value) get() -> i32 { return value.number; } } fn inspect(object: &opaque Read) -> i32 { return object.get(); } fn call(value: &Value) -> i32 { return inspect(value); }";
5232 +
    try expectAnalyzeOk(program);
5233 +
}
5234 +
5235 +
/// Trait implementations must preserve the receiver pointer class.
5236 +
@test fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5237 +
    let mut a = testResolver();
5238 +
    let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
5239 +
    let result = try resolveProgramStr(&mut a, program);
5240 +
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
5241 +
}
5242 +
5243 +
/// The compiler-known marker cannot be derived more than once.
5244 +
@test fn testDuplicateLinearMarkerRejected() throws (testing::TestError) {
5245 +
    let mut a = testResolver();
5246 +
    let program = "record Token: Linear + Linear { value: u32 }";
5247 +
    let result = try resolveProgramStr(&mut a, program);
5248 +
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Linear"));
5249 +
}
5250 +
5251 +
/// A `Linear` marker does not change legacy pointer inference.
5252 +
@test fn testLinearMarkerKeepsLegacyPointerInference() throws (testing::TestError) {
5253 +
    let program = "record Marker: Linear {} fn run() { let value: u32 = 0; let pointer: *u32 = &value; pointer; }";
5254 +
    try expectAnalyzeOk(program);
5255 +
}
5256 +
5257 +
/// References are rejected from every nested or storable type position.
5258 +
@test fn testNestedRefPositionsRejected() throws (testing::TestError) {
5259 +
    {
5260 +
        let mut a = testResolver();
5261 +
        let program = "record Marker: Linear {} union Bad { Value(&u32) }";
5262 +
        let result = try resolveProgramStr(&mut a, program);
5263 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5264 +
    } {
5265 +
        let mut a = testResolver();
5266 +
        let program = "record Marker: Linear {} fn bad(value: ?&u32) {}";
5267 +
        let result = try resolveProgramStr(&mut a, program);
5268 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5269 +
    } {
5270 +
        let mut a = testResolver();
5271 +
        let program = "record Marker: Linear {} fn bad(value: [&u32; 1]) {}";
5272 +
        let result = try resolveProgramStr(&mut a, program);
5273 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5274 +
    } {
5275 +
        let mut a = testResolver();
5276 +
        let program = "record Marker: Linear {} fn bad(value: *&u32) {}";
5277 +
        let result = try resolveProgramStr(&mut a, program);
5278 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5279 +
    } {
5280 +
        let mut a = testResolver();
5281 +
        let program = "record Marker: Linear {} static BAD: &u32 = undefined;";
5282 +
        let result = try resolveProgramStr(&mut a, program);
5283 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5284 +
    } {
5285 +
        let mut a = testResolver();
5286 +
        let program = "record Marker: Linear {} fn bad(callback: fn() -> &u32) {}";
5287 +
        let result = try resolveProgramStr(&mut a, program);
5288 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5289 +
    }
5290 +
}
5291 +
5292 +
/// Function pointer parameter references remain call-scoped and valid.
5293 +
@test fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5294 +
    let program = "record Marker: Linear {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }";
5295 +
    try expectAnalyzeOk(program);
5296 +
}
5297 +
5298 +
/// Pointer and slice casts cannot change reference ownership.
5299 +
@test fn testRefCastClassPreserved() throws (testing::TestError) {
5300 +
    {
5301 +
        let mut a = testResolver();
5302 +
        let program = "record Marker: Linear {} fn cast(value: &u32) { value as *u32; }";
5303 +
        let result = try resolveProgramStr(&mut a, program);
5304 +
        let err = try expectError(&result);
5305 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5306 +
            else throw testing::TestError::Failed;
5307 +
    } {
5308 +
        let mut a = testResolver();
5309 +
        let program = "record Marker: Linear {} fn cast(values: &[u32]) { values as *[u32]; }";
5310 +
        let result = try resolveProgramStr(&mut a, program);
5311 +
        let err = try expectError(&result);
5312 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5313 +
            else throw testing::TestError::Failed;
5314 +
    }
5315 +
}
5316 +
5317 +
/// Every operation that interprets an unsafe address requires an unsafe declaration.
5318 +
@test fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5319 +
    {
5320 +
        let mut a = testResolver();
5321 +
        let program = "record Marker: Linear {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
5322 +
        let result = try resolveProgramStr(&mut a, program);
5323 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5324 +
    } {
5325 +
        let mut a = testResolver();
5326 +
        let program = "record Marker: Linear {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }";
5327 +
        let result = try resolveProgramStr(&mut a, program);
5328 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5329 +
    } {
5330 +
        let mut a = testResolver();
5331 +
        let program = "record Marker: Linear {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }";
5332 +
        let result = try resolveProgramStr(&mut a, program);
5333 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5334 +
    } {
5335 +
        let mut a = testResolver();
5336 +
        let program = "record Marker: Linear {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }";
5337 +
        let result = try resolveProgramStr(&mut a, program);
5338 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5339 +
    } {
5340 +
        let mut a = testResolver();
5341 +
        let program = "record Marker: Linear {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }";
5342 +
        let result = try resolveProgramStr(&mut a, program);
5343 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5344 +
    } {
5345 +
        let mut a = testResolver();
5346 +
        let program = "record Marker: Linear {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }";
5347 +
        let result = try resolveProgramStr(&mut a, program);
5348 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5349 +
    } {
5350 +
        let mut a = testResolver();
5351 +
        let program = "record Marker: Linear {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }";
5352 +
        let result = try resolveProgramStr(&mut a, program);
5353 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5354 +
    }
5355 +
}
5356 +
5357 +
/// Unsafe declarations may compose unsafe operations and calls.
5358 +
@test fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5359 +
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }";
5360 +
    try expectAnalyzeOk(program);
5361 +
}
5362 +
5363 +
/// Unsafe code may drop a checked reference to an unsafe pointer.
5364 +
@test fn testUnsafePointerFromReference() throws (testing::TestError) {
5365 +
    let program = "record Marker: Linear {} unsafe fn store(pointer: *unsafe mut u32) { set *pointer = 42; } unsafe fn run() { let mut value: u32 = 0; store(&mut value as *unsafe mut u32); }";
5366 +
    try expectAnalyzeOk(program);
5367 +
}
5368 +
5369 +
/// Dropping a reference to an unsafe pointer cannot add mutability.
5370 +
@test fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5371 +
    let mut a = testResolver();
5372 +
    let program = "record Marker: Linear {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
5373 +
    let result = try resolveProgramStr(&mut a, program);
5374 +
    let err = try expectError(&result);
5375 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5376 +
        else throw testing::TestError::Failed;
5377 +
}
5378 +
5379 +
/// Recursive cast validation cannot hide a checked-to-unsafe transition.
5380 +
@test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5381 +
    let mut a = testResolver();
5382 +
    let program = "record Marker: Linear {} fn run(value: **u32) { value as **unsafe u32; }";
5383 +
    let result = try resolveProgramStr(&mut a, program);
5384 +
    let err = try expectError(&result);
5385 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5386 +
        else throw testing::TestError::Failed;
5387 +
}
5388 +
5389 +
/// Unsafe code may drop a checked slice reference to an unsafe slice.
5390 +
@test fn testUnsafeSliceFromReference() throws (testing::TestError) {
5391 +
    let program = "record Marker: Linear {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }";
5392 +
    try expectAnalyzeOk(program);
5393 +
}
5394 +
5395 +
/// Slice casts cannot add mutability.
5396 +
@test fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5397 +
    let mut a = testResolver();
5398 +
    let program = "record Marker: Linear {} fn run(values: &[u32]) { values as &mut [u32]; }";
5399 +
    let result = try resolveProgramStr(&mut a, program);
5400 +
    let err = try expectError(&result);
5401 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5402 +
        else throw testing::TestError::Failed;
5403 +
}
5404 +
5405 +
/// Mutable unsafe receivers do not create checked exclusive loans.
5406 +
@test fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5407 +
    let program = "record Marker: Linear {} record Value { number: u32 } unsafe fn (value: *unsafe mut Value) update(other: *unsafe mut Value) {} unsafe fn run(value: *unsafe mut Value) { value.update(value); }";
5408 +
    try expectAnalyzeOk(program);
5409 +
}
5410 +
5411 +
/// Unsafe instance-method attributes enable unsafe operations in the body.
5412 +
@test fn testUnsafeInstanceMethodBody() throws (testing::TestError) {
5413 +
    let program = "record Marker: Linear {} record Value { number: u32 } trait Read { unsafe fn (*unsafe Read) get() -> u32; } instance Read for Value { unsafe fn (value: *unsafe Value) get() -> u32 { return value.number; } }";
5414 +
    try expectAnalyzeOk(program);
5415 +
}
5416 +
5417 +
/// Unsafe instance methods cannot implement safe trait contracts.
5418 +
@test fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5419 +
    let mut a = testResolver();
5420 +
    let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
5421 +
    let result = try resolveProgramStr(&mut a, program);
5422 +
    try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
5423 +
}
5424 +
5425 +
/// Unsafe trait methods retain their call-site requirement through dispatch.
5426 +
@test fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5427 +
    let mut a = testResolver();
5428 +
    let program = "record Marker: Linear {} record Value { number: u32 } trait Read { unsafe fn (&Read) get() -> u32; } instance Read for Value { unsafe fn (value: &Value) get() -> u32 { return value.number; } } fn inspect(object: &opaque Read) -> u32 { return object.get(); }";
5429 +
    let result = try resolveProgramStr(&mut a, program);
5430 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5431 +
}