lang: Enforce linear ownership

7e3db9f1f3eb8ae75aa36c065a2a4b7187a496daef061dc7039bb89531d12827
Alexis Sellier committed ago 1 parent 3c8a74bf
lib/std/lang/resolver.rad +1653 -289
64 64
    name: *[u8],
65 65
    /// Function type for the method, excluding the receiver.
66 66
    fnType: *FnType,
67 67
    /// Whether the receiver is mutable.
68 68
    mutable: bool,
69 +
    /// Pointer-like class used by the receiver.
70 +
    receiverClass: types::PointerClass,
69 71
    /// V-table slot index.
70 72
    index: u32,
71 73
}
72 74
73 75
/// An entry in the trait instance registry.
94 96
    name: *[u8],
95 97
    /// Function type excluding the receiver.
96 98
    fnType: *FnType,
97 99
    /// Whether the receiver is mutable.
98 100
    mutable: bool,
101 +
    /// Pointer-like class used by the receiver.
102 +
    receiverClass: types::PointerClass,
99 103
    /// Symbol for the method.
100 104
    symbol: *mut Symbol,
101 105
}
102 106
103 107
/// Identifier for the synthetic `len` field.
160 164
export record RecordType {
161 165
    fields: *[RecordField],
162 166
    labeled: bool,
163 167
    /// Cached layout.
164 168
    layout: Layout,
169 +
    /// Whether the declaration explicitly carries the `Linear` marker.
170 +
    declaredLinear: bool,
165 171
}
166 172
167 173
/// Union nominal type.
168 174
export record UnionType {
169 175
    variants: *[UnionVariant],
171 177
    layout: Layout,
172 178
    /// Cached payload offset within the union aggregate.
173 179
    valOffset: u32,
174 180
    /// If all variants have void payloads.
175 181
    isAllVoid: bool,
182 +
    /// Whether the declaration explicitly carries the `Linear` marker.
183 +
    declaredLinear: bool,
176 184
}
177 185
178 186
/// Metadata for user-defined types.
179 187
export union NominalType {
180 188
    /// Placeholder for a type that hasn't been fully resolved yet.
259 267
/// Resolved function signature details.
260 268
export record FnType {
261 269
    paramTypes: *[*Type],
262 270
    returnType: *Type,
263 271
    throwList: *[*Type],
272 +
    /// Whether calling this function requires an unsafe context.
273 +
    isUnsafe: bool,
264 274
    localCount: u32,
265 275
}
266 276
267 277
/// Describes a type computed during semantic analysis.
268 278
export union Type {
277 287
    /// Range types, eg. `start..end`.
278 288
    Range {
279 289
        start: ?*Type,
280 290
        end: ?*Type,
281 291
    },
282 -
    /// Pointer-like address.
292 +
    /// Owning pointer-like address.
283 293
    Pointer {
284 294
        class: types::PointerClass,
285 295
        target: *Type,
286 296
        mutable: bool,
287 297
    },
288 -
    /// Pointer-like slice.
298 +
    /// Owning slice.
289 299
    Slice {
290 300
        class: types::PointerClass,
291 301
        item: *Type,
292 302
        mutable: bool,
293 303
    },
297 307
    Optional(*Type),
298 308
    /// Eg. `fn id(i32) -> i32`.
299 309
    Fn(*FnType),
300 310
    /// Named, ie. user-defined types, includes union variants.
301 311
    Nominal(*NominalType),
302 -
    /// An erased pointer-like type with a v-table.
312 +
    /// Owning trait object. An erased type with v-table.
303 313
    TraitObject {
304 314
        /// Ownership and safety class.
305 315
        class: types::PointerClass,
306 316
        /// Trait definition.
307 317
        traitInfo: *TraitType,
569 579
    MissingTraitMethod(*[u8]),
570 580
    /// Trait name used as a value expression.
571 581
    UnexpectedTraitName,
572 582
    /// Trait method receiver does not point to the declaring trait.
573 583
    TraitReceiverMismatch,
584 +
    /// Trait declaration and instance disagree about unsafe call requirements.
585 +
    TraitMethodSafetyMismatch,
574 586
    /// Function declaration has too many parameters.
575 587
    FnParamOverflow(CountMismatch),
576 588
    /// Function declaration has too many throws.
577 589
    FnThrowOverflow(CountMismatch),
578 590
    /// Trait declaration has too many methods.
579 591
    TraitMethodOverflow(CountMismatch),
580 592
    /// Instance declaration is missing a required supertrait instance.
581 593
    MissingSupertraitInstance(*[u8]),
594 +
    /// Linear binding was consumed more than once.
595 +
    LinearUseAfterConsume(*[u8]),
596 +
    /// Linear binding remains available at an exit.
597 +
    LinearNotConsumed(*[u8]),
598 +
    /// A case-pattern `let-else` fallback must terminate control flow.
599 +
    LinearLetElseMustTerminate,
600 +
    /// Branches disagree about a linear binding's state.
601 +
    LinearBranchMismatch(*[u8]),
602 +
    /// A linear field cannot be moved independently.
603 +
    LinearPartialMove,
604 +
    /// A linear value cannot be discarded.
605 +
    LinearDiscard,
606 +
    /// Assignment would overwrite a live linear value.
607 +
    LinearOverwrite,
608 +
    /// `undefined` cannot initialize a linear type.
609 +
    LinearUndefined,
610 +
    /// A reference appears in a storable or escaping position.
611 +
    InvalidRefPosition,
612 +
    /// A reference cannot be bound to a local.
613 +
    RefBinding,
614 +
    /// Call arguments contain overlapping incompatible loans.
615 +
    BorrowConflict(*[u8]),
616 +
    /// Unsafe pointer operation outside an `unsafe` declaration.
617 +
    UnsafeOperation,
618 +
    /// Safe code cannot call an `unsafe` function.
619 +
    UnsafeCall,
582 620
    /// Internal error.
583 621
    Internal,
584 622
}
585 623
586 624
/// Diagnostics returned by the analyzer.
751 789
    loopDepth: u32,
752 790
    /// Signature of the function currently being analyzed.
753 791
    currentFn: ?*FnType,
754 792
    /// Current module being analyzed.
755 793
    currentMod: u16,
794 +
    /// Nesting depth of unsafe modules and function bodies.
795 +
    unsafeDepth: u32,
796 +
    /// Whether this compilation contains explicitly linear declarations.
797 +
    linearEnabled: bool,
756 798
    /// Configuration for semantic analysis.
757 799
    config: Config,
758 800
    /// Unified arena for symbols, scopes, and nominal type.
759 801
    arena: alloc::Arena,
760 802
    /// Combined semantic metadata table indexed by node ID.
908 950
        pkgScope: storage.pkgScope,
909 951
        loopStack: undefined,
910 952
        loopDepth: 0,
911 953
        currentFn: nil,
912 954
        currentMod: 0,
955 +
        unsafeDepth: 0,
956 +
        linearEnabled: false,
913 957
        config,
914 958
        arena,
915 959
        nodeData: NodeDataTable { entries: storage.nodeData },
916 960
        types: nil,
917 961
        errors: @sliceOf(storage.errors.ptr, 0, storage.errors.len),
1350 1394
}
1351 1395
1352 1396
/// Get the layout of a type.
1353 1397
export fn getTypeLayout(ty: Type) -> Layout {
1354 1398
    match ty {
1399 +
        case Type::Pointer { .. } => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1400 +
        case Type::Slice { .. }, Type::TraitObject { .. } =>
1401 +
            return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE },
1355 1402
        case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 },
1356 1403
        case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 },
1357 1404
        case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 },
1358 1405
        case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 },
1359 1406
        case Type::Int => return Layout { size: 8, alignment: 8 },
1360 1407
        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 },
1408 +
        case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE },
1365 1409
        case Type::Array(arr) => return getArrayLayout(arr),
1366 1410
        case Type::Optional(inner) => return getOptionalLayout(*inner),
1367 1411
        case Type::Nominal(info) => return getNominalLayout(*info),
1368 1412
        else => {
1369 1413
            panic "getTypeLayout: the given type cannot be layed out";
1528 1572
    return unionType.isAllVoid;
1529 1573
}
1530 1574
1531 1575
/// Check if a type should be treated as an address-like value.
1532 1576
fn isAddressType(ty: Type) -> bool {
1577 +
    if isNullableType(ty) {
1578 +
        return true;
1579 +
    }
1533 1580
    match ty {
1534 -
        case Type::Pointer { .. }, Type::Slice { .. }, Type::Fn(_) => return true,
1581 +
        case Type::Fn(_) => return true,
1535 1582
        else => return false,
1536 1583
    }
1537 1584
}
1538 1585
1539 1586
/// Return the representable range for an integer type.
1602 1649
1603 1650
/// Ensure all nested nominal types in a type are resolved.
1604 1651
fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) {
1605 1652
    match ty {
1606 1653
        case Type::Nominal(info) => try ensureNominalResolved(self, info, site),
1607 -
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1608 1654
        case Type::Slice { item, .. } => try ensureTypeResolved(self, *item, site),
1609 1655
        case Type::Pointer { .. } => {}, // Pointers have fixed layout, don't recurse.
1656 +
        case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site),
1610 1657
        case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site),
1611 1658
        else => {},
1612 1659
    }
1613 1660
}
1614 1661
1659 1706
        }
1660 1707
    }
1661 1708
    return true;
1662 1709
}
1663 1710
1711 +
/// Preserve legacy address-of coercions until a package opts into linearity.
1712 +
fn pointerClassesAssignable(
1713 +
    self: *Resolver,
1714 +
    to: types::PointerClass,
1715 +
    from: types::PointerClass,
1716 +
) -> bool {
1717 +
    return to == from or (
1718 +
        not self.linearEnabled
1719 +
        and to == types::PointerClass::Owned
1720 +
        and from == types::PointerClass::Ref
1721 +
    );
1722 +
}
1723 +
1664 1724
/// Check if the `from` type is assignable to the `to` type, and return a
1665 1725
/// coercion plan if so.
1666 1726
fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion {
1667 1727
    if to == Type::Unknown or from == Type::Unknown {
1668 1728
        return nil;
1678 1738
        return Coercion::Identity;
1679 1739
    }
1680 1740
    if to == from {
1681 1741
        return Coercion::Identity;
1682 1742
    }
1743 +
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
1744 +
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
1745 +
            else return nil;
1746 +
        if not pointerClassesAssignable(self, lhsClass, rhsClass) {
1747 +
            return nil;
1748 +
        }
1749 +
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
1750 +
        if *lhsTarget == Type::Opaque {
1751 +
            if lhsMutable and not rhsMutable {
1752 +
                return nil;
1753 +
            }
1754 +
            return Coercion::Identity;
1755 +
        }
1756 +
        if lhsMutable and not rhsMutable {
1757 +
            return nil;
1758 +
        }
1759 +
        return isAssignable(self, *lhsTarget, *rhsTarget, rval);
1760 +
    }
1761 +
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
1762 +
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
1763 +
            if not pointerClassesAssignable(self, lhsClass, rhsClass)
1764 +
                or (lhsMutable and not rhsMutable)
1765 +
            {
1766 +
                return nil;
1767 +
            }
1768 +
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
1769 +
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
1770 +
            }
1771 +
        }
1772 +
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
1773 +
            if not pointerClassesAssignable(self, lhsClass, rhsClass)
1774 +
                or lhsTraitInfo <> rhsTraitInfo
1775 +
            {
1776 +
                return nil;
1777 +
            }
1778 +
            if lhsMutable and not rhsMutable {
1779 +
                return nil;
1780 +
            }
1781 +
            return Coercion::Identity;
1782 +
        }
1783 +
        return nil;
1784 +
    }
1785 +
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
1786 +
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
1787 +
            else return nil;
1788 +
        if not pointerClassesAssignable(self, lhsClass, rhsClass)
1789 +
            or (lhsMutable and not rhsMutable)
1790 +
        {
1791 +
            return nil;
1792 +
        }
1793 +
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
1794 +
        if *lhsItem == Type::Opaque {
1795 +
            return Coercion::Identity;
1796 +
        }
1797 +
        return isAssignable(self, *lhsItem, *rhsItem, rval);
1798 +
    }
1683 1799
    match to {
1684 1800
        case Type::Array(lhs) => {
1685 1801
            let case Type::Array(rhs) = from
1686 1802
                else return nil;
1687 1803
1712 1828
                    }
1713 1829
                    return nil;
1714 1830
                }
1715 1831
            }
1716 1832
        }
1717 -
        case Type::Pointer { target: lhsTarget, mutable: lhsMutable, .. } => {
1718 -
            let case Type::Pointer { target: rhsTarget, mutable: rhsMutable, .. } = from
1719 -
                else return nil;
1720 1833
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 1834
        case Type::Optional(inner) => {
1734 1835
            if from == Type::Nil {
1735 1836
                return Coercion::OptionalLift(to);
1736 1837
            }
1737 1838
            if let _ = isAssignable(self, *inner, from, rval) {
1740 1841
            if let case Type::Optional(fromInner) = from {
1741 1842
                return isAssignable(self, *inner, *fromInner, rval);
1742 1843
            }
1743 1844
            return nil;
1744 1845
        }
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 -
        }
1846 +
1790 1847
        case Type::Fn(toInfo) => {
1791 1848
            // Allow function type structural matching.
1792 1849
            if let case Type::Fn(fromInfo) = from {
1793 1850
                if fnTypeEqual(toInfo, fromInfo) {
1794 1851
                    return Coercion::Identity;
1829 1886
    return nil;
1830 1887
}
1831 1888
1832 1889
/// Check if two function type descriptors are structurally equivalent.
1833 1890
fn fnTypeEqual(a: *FnType, b: *FnType) -> bool {
1891 +
    if a.isUnsafe <> b.isUnsafe {
1892 +
        return false;
1893 +
    }
1834 1894
    if a.paramTypes.len <> b.paramTypes.len {
1835 1895
        return false;
1836 1896
    }
1837 1897
    if a.throwList.len <> b.throwList.len {
1838 1898
        return false;
1856 1916
/// Check if two types are structurally equal.
1857 1917
export fn typesEqual(a: Type, b: Type) -> bool {
1858 1918
    if a == b {
1859 1919
        return true;
1860 1920
    }
1921 +
    if let case Type::Pointer { class: aClass, target: aTarget, mutable: aMutable } = a {
1922 +
        let case Type::Pointer { class: bClass, target: bTarget, mutable: bMutable } = b
1923 +
            else return false;
1924 +
        return aClass == bClass and aMutable == bMutable
1925 +
            and typesEqual(*aTarget, *bTarget);
1926 +
    }
1927 +
    if let case Type::Slice { class: aClass, item: aItem, mutable: aMutable } = a {
1928 +
        let case Type::Slice { class: bClass, item: bItem, mutable: bMutable } = b
1929 +
            else return false;
1930 +
        return aClass == bClass and aMutable == bMutable
1931 +
            and typesEqual(*aItem, *bItem);
1932 +
    }
1933 +
    if let case Type::TraitObject { class: aClass, traitInfo: aTraitInfo, mutable: aMutable } = a {
1934 +
        let case Type::TraitObject { class: bClass, traitInfo: bTraitInfo, mutable: bMutable } = b
1935 +
            else return false;
1936 +
        return aClass == bClass and aMutable == bMutable
1937 +
            and aTraitInfo == bTraitInfo;
1938 +
    }
1861 1939
    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 1940
        case Type::Array(aa) => {
1873 1941
            let case Type::Array(ab) = b else return false;
1874 1942
            return aa.length == ab.length and typesEqual(*aa.item, *ab.item);
1875 1943
        }
1876 1944
        case Type::Optional(oa) => {
1883 1951
        }
1884 1952
        else => return false,
1885 1953
    }
1886 1954
}
1887 1955
1956 +
/// Return whether `ty` is a direct reference.
1957 +
export fn isRefType(ty: Type) -> bool {
1958 +
    match ty {
1959 +
        case Type::Pointer { class: types::PointerClass::Ref, .. },
1960 +
             Type::Slice { class: types::PointerClass::Ref, .. },
1961 +
             Type::TraitObject { class: types::PointerClass::Ref, .. } => return true,
1962 +
        else => return false,
1963 +
    }
1964 +
}
1965 +
1966 +
/// Return whether a type contains a reference.
1967 +
fn containsRef(ty: Type) -> bool {
1968 +
    if isRefType(ty) {
1969 +
        return true;
1970 +
    }
1971 +
    if let case Type::Pointer { target, .. } = ty {
1972 +
        return containsRef(*target);
1973 +
    }
1974 +
    if let case Type::Slice { item, .. } = ty {
1975 +
        return containsRef(*item);
1976 +
    }
1977 +
    match ty {
1978 +
        case Type::Array(array) => return containsRef(*array.item),
1979 +
        case Type::Optional(inner) => return containsRef(*inner),
1980 +
        // Nominal declarations validate their own fields and variants.
1981 +
        // Treating them as leaves also terminates recursive pointer types.
1982 +
        case Type::Nominal(_) => return false,
1983 +
        else => return false,
1984 +
    }
1985 +
}
1986 +
1987 +
/// Return whether a type is exact-linear.
1988 +
export fn isLinear(ty: Type) -> bool {
1989 +
    match ty {
1990 +
        case Type::Pointer { class: types::PointerClass::Owned, .. },
1991 +
             Type::Slice { class: types::PointerClass::Owned, .. },
1992 +
             Type::TraitObject { class: types::PointerClass::Owned, .. } => return true,
1993 +
        case Type::Pointer { class: types::PointerClass::Ref, .. },
1994 +
             Type::Pointer { class: types::PointerClass::Unsafe, .. },
1995 +
             Type::Slice { class: types::PointerClass::Ref, .. },
1996 +
             Type::Slice { class: types::PointerClass::Unsafe, .. },
1997 +
             Type::TraitObject { class: types::PointerClass::Ref, .. },
1998 +
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return false,
1999 +
2000 +
        case Type::Array(array) => return isLinear(*array.item),
2001 +
        case Type::Optional(inner) => return isLinear(*inner),
2002 +
        case Type::Nominal(NominalType::Record(recInfo)) => {
2003 +
            if recInfo.declaredLinear {
2004 +
                return true;
2005 +
            }
2006 +
            for field in recInfo.fields {
2007 +
                if isLinear(field.fieldType) {
2008 +
                    return true;
2009 +
                }
2010 +
            }
2011 +
            return false;
2012 +
        }
2013 +
        case Type::Nominal(NominalType::Union(unionType)) => {
2014 +
            if unionType.declaredLinear {
2015 +
                return true;
2016 +
            }
2017 +
            for variant in unionType.variants {
2018 +
                if isLinear(variant.valueType) {
2019 +
                    return true;
2020 +
                }
2021 +
            }
2022 +
            return false;
2023 +
        }
2024 +
        else => return false,
2025 +
    }
2026 +
}
2027 +
2028 +
/// Return whether `ty` is a direct unsafe pointer-like value.
2029 +
fn isUnsafePointerType(ty: Type) -> bool {
2030 +
    match ty {
2031 +
        case Type::Pointer { class: types::PointerClass::Unsafe, .. },
2032 +
             Type::Slice { class: types::PointerClass::Unsafe, .. },
2033 +
             Type::TraitObject { class: types::PointerClass::Unsafe, .. } => return true,
2034 +
        else => return false,
2035 +
    }
2036 +
}
2037 +
1888 2038
/// Get the record info from a record type.
1889 2039
export fn getRecord(ty: Type) -> ?RecordType {
1890 2040
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
1891 2041
    return recInfo;
1892 2042
}
1899 2049
    return ty;
1900 2050
}
1901 2051
1902 2052
/// Get field info for a record-like type (records, slices) by field index.
1903 2053
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,
2054 +
    if let case Type::Slice { class, item, mutable } = ty {
2055 +
        match index {
2056 +
            case 0 => return RecordField {
2057 +
                name: PTR_FIELD,
2058 +
                fieldType: Type::Pointer { class, target: item, mutable },
2059 +
                offset: 0,
2060 +
            },
2061 +
            case 1 => return RecordField {
2062 +
                name: LEN_FIELD,
2063 +
                fieldType: Type::U32,
2064 +
                offset: PTR_SIZE as i32,
2065 +
            },
2066 +
            case 2 => return RecordField {
2067 +
                name: CAP_FIELD,
2068 +
                fieldType: Type::U32,
2069 +
                offset: PTR_SIZE as i32 + 4,
2070 +
            },
2071 +
            else => return nil,
2072 +
        }
2073 +
    }
2074 +
    if let case Type::Nominal(NominalType::Record(recInfo)) = ty;
2075 +
        index < recInfo.fields.len
2076 +
    {
2077 +
        return recInfo.fields[index];
1934 2078
    }
2079 +
    return nil;
1935 2080
}
1936 2081
1937 2082
/// Check if the two types can be compared for equality.
1938 2083
fn isComparable(left: Type, right: Type) -> bool {
1939 2084
    if left == Type::Unknown or right == Type::Unknown {
2330 2475
    scope: *Scope
2331 2476
) -> *mut Symbol throws (ResolveError) {
2332 2477
    assert path.len <> 0, "resolvePath: empty path";
2333 2478
    // Start by finding the root of the path.
2334 2479
    let root = path[0];
2335 -
    let sym = findInScopeRecursive(scope, root, isAnySymbol) else
2336 -
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2480 +
    let sym = findInScopeRecursive(scope, root, isAnySymbol)
2481 +
        else throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2337 2482
    let suffix = &path[1..];
2338 2483
2339 2484
    // Check visibility for symbol.
2340 2485
    if not isSymbolVisible(sym, scope, self.scope) {
2341 2486
        throw emitError(self, node, ErrorKind::UnresolvedSymbol(root));
2484 2629
            // Ignore non-declaration nodes.
2485 2630
        }
2486 2631
    }
2487 2632
}
2488 2633
2634 +
/// Require the current declaration to be unsafe.
2635 +
fn requireUnsafe(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2636 +
    if self.unsafeDepth == 0 {
2637 +
        throw emitError(self, node, ErrorKind::UnsafeOperation);
2638 +
    }
2639 +
}
2640 +
2641 +
/// Reject calls from safe code through unsafe function types.
2642 +
fn checkUnsafeCall(self: *mut Resolver, node: *ast::Node, info: *FnType)
2643 +
    throws (ResolveError)
2644 +
{
2645 +
    if info.isUnsafe and self.unsafeDepth == 0 {
2646 +
        throw emitError(self, node, ErrorKind::UnsafeCall);
2647 +
    }
2648 +
}
2649 +
2489 2650
/// Visit a top-level definition, recursing into sub-modules.
2490 2651
fn visitDef(self: *mut Resolver, node: *ast::Node) throws (ResolveError) {
2491 2652
    match node.value {
2492 2653
        case ast::NodeValue::FnDecl(decl) => {
2493 2654
            try resolveFnDeclBody(self, node, decl) catch {
2500 2661
            }
2501 2662
            let modName = try nodeName(self, decl.name);
2502 2663
            let submod = try enterSubModule(self, modName, node);
2503 2664
            let case ast::NodeValue::Block(block) = submod.root.value
2504 2665
                else panic "visitDef: expected block for module root";
2505 -
            try resolveModuleDefs(self, &block);
2666 +
            let mut isUnsafe = false;
2667 +
            if let attrs = decl.attrs {
2668 +
                set isUnsafe = ast::attributesContains(&attrs, ast::Attribute::Unsafe);
2669 +
            }
2670 +
            if isUnsafe {
2671 +
                set self.unsafeDepth += 1;
2672 +
            }
2673 +
            try resolveModuleDefs(self, &block) catch e {
2674 +
                if isUnsafe { set self.unsafeDepth -= 1; }
2675 +
                exitModuleScope(self, submod);
2676 +
                throw e;
2677 +
            };
2678 +
            if isUnsafe {
2679 +
                set self.unsafeDepth -= 1;
2680 +
            }
2506 2681
            exitModuleScope(self, submod);
2507 2682
        }
2508 2683
        case ast::NodeValue::RecordDecl(_),
2509 2684
             ast::NodeValue::UnionDecl(_),
2510 2685
             ast::NodeValue::Use(_),
2532 2707
/// Try to infer a node's type.
2533 2708
fn infer(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2534 2709
    return try visit(self, node, Type::Unknown);
2535 2710
}
2536 2711
2712 +
/// Reject nested references while allowing a direct parameter reference.
2713 +
fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type)
2714 +
    throws (ResolveError)
2715 +
{
2716 +
    if isRefType(ty) {
2717 +
        if let case Type::Pointer { target, .. } = ty {
2718 +
            if containsRef(*target) {
2719 +
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2720 +
            }
2721 +
        } else if let case Type::Slice { item, .. } = ty {
2722 +
            if containsRef(*item) {
2723 +
                throw emitError(self, node, ErrorKind::InvalidRefPosition);
2724 +
            }
2725 +
        }
2726 +
    } else if containsRef(ty) {
2727 +
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2728 +
    }
2729 +
}
2730 +
2731 +
/// Require a type that may be stored or escape a call.
2732 +
fn ensureStorableType(self: *mut Resolver, node: *ast::Node, ty: Type)
2733 +
    throws (ResolveError)
2734 +
{
2735 +
    if containsRef(ty) {
2736 +
        throw emitError(self, node, ErrorKind::InvalidRefPosition);
2737 +
    }
2738 +
}
2739 +
2537 2740
/// Resolve a type signature node.
2538 2741
fn resolveValueType(self: *mut Resolver, node: *ast::Node) -> Type throws (ResolveError) {
2539 2742
    let ty = try visit(self, node, Type::Unknown);
2540 2743
    // Opaque value types are not allowed.
2541 2744
    if ty == Type::Opaque {
2542 2745
        throw emitError(self, node, ErrorKind::OpaqueTypeNotAllowed);
2543 2746
    }
2747 +
    try validateValueTypeReferences(self, node, ty);
2544 2748
    return ty;
2545 2749
}
2546 2750
2547 2751
/// Analyze a node's type and check that it can be assigned to the expected type.
2548 2752
fn checkAssignable(self: *mut Resolver, node: *ast::Node, expected: Type) -> Type throws (ResolveError) {
2636 2840
        case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr),
2637 2841
        case ast::NodeValue::Panic { message } => {
2638 2842
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2639 2843
                class: types::PointerClass::Owned,
2640 2844
                item: allocType(self, Type::U8),
2641 -
                mutable: false
2845 +
                mutable: false,
2642 2846
            });
2643 2847
            return setNodeType(self, node, Type::Never);
2644 2848
        },
2645 2849
        case ast::NodeValue::Assert { condition, message } => {
2646 2850
            try visit(self, condition, Type::Bool);
2647 2851
            try visitOptional(self, message, Type::Slice { // TODO: Have easy access to string type.
2648 2852
                class: types::PointerClass::Owned,
2649 2853
                item: allocType(self, Type::U8),
2650 -
                mutable: false
2854 +
                mutable: false,
2651 2855
            });
2652 2856
            return setNodeType(self, node, Type::Void);
2653 2857
        },
2654 2858
        case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop),
2655 2859
        case ast::NodeValue::ExprStmt(expr) => {
2792 2996
        if not isTypeInferrable(bindingTy) {
2793 2997
            throw emitError(self, decl.value, ErrorKind::CannotInferType);
2794 2998
        }
2795 2999
    }
2796 3000
    // Variables cannot have void type.
3001 +
    if containsRef(bindingTy) {
3002 +
        throw emitError(self, node, ErrorKind::RefBinding);
3003 +
    }
2797 3004
    if bindingTy == Type::Void {
2798 3005
        throw emitError(self, decl.value, ErrorKind::CannotAssignVoid);
2799 3006
    }
2800 3007
    // Variables cannot have opaque type directly.
2801 3008
    if bindingTy == Type::Opaque {
3029 3236
    attrList: ?ast::Attributes,
3030 3237
    isConst: bool
3031 3238
) -> Type throws (ResolveError) {
3032 3239
    let attrs = resolveAttributes(self, attrList);
3033 3240
    let bindingTy = try infer(self, typeNode);
3241 +
    try ensureStorableType(self, typeNode, bindingTy);
3034 3242
    let valueTy = try checkAssignable(self, valueNode, bindingTy);
3035 3243
3036 3244
    if isConst {
3037 3245
        let mut constVal = constValueEntry(self, valueNode);
3038 3246
        if constVal == nil and not isConstExpr(self, valueNode) {
3061 3269
{
3062 3270
    let attrMask = resolveAttributes(self, decl.attrs);
3063 3271
    let mut retTy = Type::Void;
3064 3272
    if let retNode = decl.sig.returnType {
3065 3273
        set retTy = try infer(self, retNode);
3274 +
        try ensureStorableType(self, retNode, retTy);
3066 3275
    }
3067 3276
    let a = alloc::arenaAllocator(&mut self.arena);
3068 3277
    let mut paramTypes: *mut [*Type] = &mut [];
3069 3278
    let mut throwList: *mut [*Type] = &mut [];
3070 3279
    let mut fnType = FnType {
3071 3280
        paramTypes: &[],
3072 3281
        returnType: allocType(self, retTy),
3073 3282
        throwList: &[],
3283 +
        isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3074 3284
        localCount: 0,
3075 3285
    };
3076 3286
    // Enter the function scope to process parameters.
3077 3287
    enterFn(self, node, &fnType);
3078 3288
3102 3312
        let throwTy = try infer(self, throwNode) catch e {
3103 3313
            exitFn(self);
3104 3314
            throw e;
3105 3315
        };
3106 3316
        throwList.append(allocType(self, throwTy), a);
3317 +
        try ensureStorableType(self, throwNode, throwTy);
3107 3318
    }
3108 3319
    exitFn(self);
3109 3320
    set fnType.paramTypes = &paramTypes[..];
3110 3321
    set fnType.throwList = &throwList[..];
3111 3322
3128 3339
        panic "resolveFnDeclBody: unexpected symbol data for function";
3129 3340
    };
3130 3341
    let retTy = *fnType.returnType;
3131 3342
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3132 3343
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3344 +
    let isUnsafe = fnType.isUnsafe;
3133 3345
3134 3346
    if let body = decl.body {
3135 3347
        if isIntrinsic {
3136 3348
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
3137 3349
        }
3138 3350
        if isExtern {
3139 3351
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
3140 3352
        }
3353 +
        if isUnsafe {
3354 +
            set self.unsafeDepth += 1;
3355 +
        }
3141 3356
        enterFn(self, node, fnType); // Enter function scope for body analysis.
3142 3357
3143 3358
        let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3144 3359
            exitFn(self);
3360 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3145 3361
            throw e;
3146 3362
        };
3147 3363
        if retTy <> Type::Void and bodyTy <> Type::Never {
3148 3364
            exitFn(self);
3365 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3149 3366
            throw emitError(self, body, ErrorKind::FnMissingReturn);
3150 3367
        }
3151 3368
        exitFn(self);
3369 +
        if isUnsafe {
3370 +
            set self.unsafeDepth -= 1;
3371 +
        }
3372 +
        if self.linearEnabled {
3373 +
            try checkLinearFn(self, nil, decl.sig.params, body);
3374 +
        }
3152 3375
    } else if not isExtern {
3153 3376
        throw emitError(self, node, ErrorKind::FnMissingBody);
3154 3377
    }
3155 3378
}
3156 3379
3162 3385
    let _ = try bindValueIdent(self, param.name, node, ty, false, 0, 0);
3163 3386
3164 3387
    return ty;
3165 3388
}
3166 3389
3390 +
/// Resolve the compiler-known `Linear` marker from a derive list.
3391 +
fn resolveLinearDerive(self: *mut Resolver, derives: *mut [*ast::Node]) -> bool
3392 +
    throws (ResolveError)
3393 +
{
3394 +
    let mut linear = false;
3395 +
    for derive in derives {
3396 +
        let name = try nodeName(self, derive);
3397 +
        if mem::eq(name, "Linear") {
3398 +
            if linear {
3399 +
                throw emitError(self, derive, ErrorKind::DuplicateBinding(name));
3400 +
            }
3401 +
            set linear = true;
3402 +
            set self.linearEnabled = true;
3403 +
        } else {
3404 +
            // Other derives retain their existing trait-name validation.
3405 +
            try infer(self, derive);
3406 +
        }
3407 +
    }
3408 +
    return linear;
3409 +
}
3410 +
3167 3411
/// Resolve record fields from a node list.
3168 3412
fn resolveRecordFields(self: *mut Resolver, node: *ast::Node, fields: *mut [*ast::Node], labeled: bool) -> RecordType
3169 3413
    throws (ResolveError)
3170 3414
{
3171 3415
    let a = alloc::arenaAllocator(&mut self.arena);
3182 3426
            field: fieldNode,
3183 3427
            type: typeNode,
3184 3428
            value: valueNode
3185 3429
        } = field.value else panic "resolveRecordFields: invalid record field";
3186 3430
        let fieldTy = try resolveValueType(self, typeNode);
3431 +
        try ensureStorableType(self, typeNode, fieldTy);
3187 3432
3188 3433
        if let v = valueNode {
3189 3434
            let _valTy = try checkAssignable(self, v, fieldTy);
3190 3435
        }
3191 3436
        // Get field name for labeled records.
3216 3461
    // Compute cached layout.
3217 3462
    let recordLayout = Layout {
3218 3463
        size: mem::alignUp(currentOffset, maxAlignment),
3219 3464
        alignment: maxAlignment
3220 3465
    };
3221 -
    return RecordType { fields: &result[..], labeled, layout: recordLayout };
3466 +
    return RecordType {
3467 +
        fields: &result[..],
3468 +
        labeled,
3469 +
        layout: recordLayout,
3470 +
        declaredLinear: false,
3471 +
    };
3222 3472
}
3223 3473
3224 3474
/// Resolve record field types for a named record declaration.
3225 3475
fn resolveRecordBody(self: *mut Resolver, node: *ast::Node, decl: ast::RecordDecl)
3226 3476
    throws (ResolveError)
3234 3484
3235 3485
    // Skip if already resolved.
3236 3486
    if let case NominalType::Record(_) = *nominalTy {
3237 3487
        return;
3238 3488
    }
3239 -
    try visitList(self, decl.derives);
3240 -
    let recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3489 +
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
3490 +
    let mut recordType = try resolveRecordFields(self, node, decl.fields, decl.labeled);
3491 +
    set recordType.declaredLinear = declaredLinear;
3241 3492
3242 3493
    set *nominalTy = NominalType::Record(recordType);
3243 3494
}
3244 3495
3245 3496
/// Bind a type name.
3335 3586
            }
3336 3587
            traitType.methods.append(TraitMethod {
3337 3588
                name: inherited.name,
3338 3589
                fnType: inherited.fnType,
3339 3590
                mutable: inherited.mutable,
3591 +
                receiverClass: inherited.receiverClass,
3340 3592
                index: traitType.methods.len as u32,
3341 3593
            }, a);
3342 3594
        }
3343 3595
        traitType.supertraits.append(superTrait, a);
3344 3596
    }
3349 3601
            actual: traitType.methods.len as u32 + methods.len as u32,
3350 3602
        }));
3351 3603
    }
3352 3604
3353 3605
    for methodNode in methods {
3354 -
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, .. } = methodNode.value
3606 +
        let case ast::NodeValue::TraitMethodSig { name, receiver, sig, attrs } = methodNode.value
3355 3607
            else continue;
3356 3608
        let methodName = try nodeName(self, name);
3609 +
        let attrMask = resolveAttributes(self, attrs);
3357 3610
3358 3611
        // Reject duplicate method names.
3359 3612
        if let _ = findTraitMethod(traitType, methodName) {
3360 3613
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3361 3614
        }
3362 -
        // Determine receiver mutability from the receiver type node
3363 -
        // and validate that the receiver points to the declaring trait.
3615 +
        // Determine the receiver class and mutability, and validate that it
3616 +
        // points to the declaring trait.
3364 3617
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3365 3618
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3366 -
        let case ast::TypeSig::Pointer { mutable, valueType, .. } = typeSig
3619 +
        let case ast::TypeSig::Pointer {
3620 +
            class: receiverClass, valueType: receiverValueType, mutable,
3621 +
        } = typeSig
3367 3622
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3368 -
        let case ast::NodeValue::TypeSig(innerSig) = valueType.value
3623 +
        let case ast::NodeValue::TypeSig(innerSig) = receiverValueType.value
3369 3624
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3370 3625
        let case ast::TypeSig::Nominal(nameNode) = innerSig
3371 3626
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3372 3627
        let receiverTargetName = try nodeName(self, nameNode);
3373 3628
3406 3661
        }
3407 3662
        let fnType = FnType {
3408 3663
            paramTypes: &paramTypes[..],
3409 3664
            returnType: retType,
3410 3665
            throwList: &throwList[..],
3666 +
            isUnsafe: ast::hasAttribute(attrMask, ast::Attribute::Unsafe),
3411 3667
            localCount: 0,
3412 3668
        };
3413 3669
        traitType.methods.append(TraitMethod {
3414 3670
            name: methodName,
3415 3671
            fnType: allocFnType(self, fnType),
3416 3672
            mutable,
3673 +
            receiverClass,
3417 3674
            index: traitType.methods.len as u32,
3418 3675
        }, a);
3419 3676
3420 3677
        setNodeType(self, methodNode, Type::Void);
3421 3678
    }
3490 3747
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
3491 3748
3492 3749
    // Match each instance method to a trait method.
3493 3750
    for methodNode in methods {
3494 3751
        let case ast::NodeValue::MethodDecl {
3495 -
            name, receiverName, receiverType, sig, body, ..
3752 +
            name, receiverName, receiverType, sig, body, attrs,
3496 3753
        } = methodNode.value else continue;
3497 3754
3498 3755
        let methodName = try nodeName(self, name);
3756 +
        let attrMask = resolveAttributes(self, attrs);
3499 3757
3500 3758
        // Find the matching trait method.
3501 3759
        let tm = findTraitMethod(traitInfo, methodName)
3502 3760
            else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName));
3761 +
        let instanceUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
3762 +
        if instanceUnsafe <> tm.fnType.isUnsafe {
3763 +
            throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch);
3764 +
        }
3503 3765
3504 3766
        // Determine receiver mutability and validate receiver type.
3505 3767
        // The receiver must be `*Type` or `*mut Type`.
3506 3768
        let case ast::NodeValue::TypeSig(typeSig) = receiverType.value
3507 3769
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3508 -
        let case ast::TypeSig::Pointer { mutable: receiverMut, valueType, .. } = typeSig
3770 +
        let case ast::TypeSig::Pointer {
3771 +
            class: receiverClass, valueType, mutable: receiverMut,
3772 +
        } = typeSig
3509 3773
            else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3774 +
        if receiverClass <> tm.receiverClass {
3775 +
            throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3776 +
        }
3510 3777
3511 3778
        // Validate that the receiver type annotation matches the
3512 3779
        // concrete type from the instance declaration.
3513 3780
        let annotatedTy = try infer(self, valueType);
3514 3781
        if not typesEqual(annotatedTy, concreteType) {
3527 3794
        }
3528 3795
3529 3796
        // Build the function type for the instance method.
3530 3797
        // The receiver becomes the first parameter.
3531 3798
        let receiverPtrType = Type::Pointer {
3532 -
            class: types::PointerClass::Owned,
3799 +
            class: receiverClass,
3533 3800
            target: allocType(self, concreteType),
3534 3801
            mutable: receiverMut,
3535 3802
        };
3536 3803
3537 3804
        // Validate that the instance method's signature matches the
3590 3857
        }
3591 3858
        let fnType = FnType {
3592 3859
            paramTypes: &paramTypes[..],
3593 3860
            returnType: tm.fnType.returnType,
3594 3861
            throwList: tm.fnType.throwList,
3862 +
            isUnsafe: tm.fnType.isUnsafe,
3595 3863
            localCount: 0,
3596 3864
        };
3597 3865
3598 3866
        // Create a symbol for the instance method without binding it into the
3599 3867
        // module scope. Instance methods are dispatched via v-table, so they
3600 3868
        // must not pollute the enclosing scope.
3601 3869
        let fnTy = Type::Fn(allocFnType(self, fnType));
3602 3870
        let mName = try nodeName(self, name);
3603 3871
        let sym = allocSymbol(self, SymbolData::Value {
3604 3872
            mutable: false, alignment: 0, type: fnTy, addressTaken: false,
3605 -
        }, mName, methodNode, 0);
3873 +
        }, mName, methodNode, attrMask);
3606 3874
3607 3875
        setNodeSymbol(self, methodNode, sym);
3608 3876
        setNodeType(self, methodNode, fnTy);
3609 3877
        setNodeType(self, name, fnTy);
3610 3878
3668 3936
) throws (ResolveError) {
3669 3937
    let sym = symbolFor(self, node)
3670 3938
        else throw emitError(self, node, ErrorKind::Internal);
3671 3939
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3672 3940
        else panic "resolveMethodBody: expected value symbol";
3941 +
    let isUnsafe = fnType.isUnsafe;
3942 +
    if isUnsafe {
3943 +
        set self.unsafeDepth += 1;
3944 +
    }
3673 3945
3674 3946
    // Enter function scope.
3675 3947
    enterFn(self, node, fnType);
3676 3948
3677 3949
    // Bind the receiver parameter.
3678 3950
    let receiverTy = *fnType.paramTypes[0];
3679 3951
    try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch e {
3680 3952
        exitFn(self);
3953 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3681 3954
        throw e;
3682 3955
    };
3683 3956
    // Bind the remaining parameters from the signature.
3684 3957
    for paramNode in sig.params {
3685 3958
        let paramTy = try infer(self, paramNode) catch e {
3686 3959
            exitFn(self);
3960 +
            if isUnsafe { set self.unsafeDepth -= 1; }
3687 3961
            throw e;
3688 3962
        };
3689 3963
    }
3690 3964
3691 3965
    // Resolve the body.
3692 3966
    let retTy = *fnType.returnType;
3693 3967
    let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3694 3968
        exitFn(self);
3969 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3695 3970
        throw e;
3696 3971
    };
3697 3972
    if retTy <> Type::Void and bodyTy <> Type::Never {
3698 3973
        exitFn(self);
3974 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3699 3975
        throw emitError(self, body, ErrorKind::FnMissingReturn);
3700 3976
    }
3701 3977
    exitFn(self);
3978 +
    if isUnsafe {
3979 +
        set self.unsafeDepth -= 1;
3980 +
    }
3981 +
    if self.linearEnabled {
3982 +
        try checkLinearFn(self, receiverName, sig.params, body);
3983 +
    }
3702 3984
}
3703 3985
3704 3986
/// Resolve a standalone method declaration (signature only).
3705 3987
/// 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
3709 -
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3710 -
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
3711 -
        else throw emitError(self, receiverType, ErrorKind::Internal);
3712 -
    let sym = symbolFor(self, nameNode)
3713 -
        else throw emitError(self, receiverType, ErrorKind::Internal);
3714 -
3715 -
    return sym.name;
3716 -
}
3717 3988
3989 +
/// Resolve and register a standalone method declaration.
3718 3990
fn resolveMethodDecl(
3719 3991
    self: *mut Resolver,
3720 3992
    node: *ast::Node,
3721 3993
    name: *ast::Node,
3722 3994
    receiverName: *ast::Node,
3725 3997
    attrs: ?ast::Attributes,
3726 3998
) throws (ResolveError) {
3727 3999
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
3728 4000
    // nominal type.
3729 4001
    let fullReceiverTy = try infer(self, receiverType);
3730 -
    let case Type::Pointer { target, mutable: receiverMut, .. } = fullReceiverTy
4002 +
    let case Type::Pointer {
4003 +
        class: receiverClass, target: receiverTarget, mutable: receiverMut,
4004 +
    } = fullReceiverTy
3731 4005
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3732 -
    let concreteType = *target;
4006 +
    let concreteType = *receiverTarget;
3733 4007
    let case Type::Nominal(nominalTy) = concreteType
3734 4008
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
3735 4009
    try ensureNominalResolved(self, nominalTy, receiverType);
3736 4010
3737 4011
    // Get the type name from the inner type node's symbol.
3738 4012
    let typeName = try receiverTypeName(self, receiverType);
3739 4013
    let methodName = try nodeName(self, name);
4014 +
    let attrMask = resolveAttributes(self, attrs);
3740 4015
3741 4016
    // Reject duplicate method for the same (type, name).
3742 4017
    if let _ = findMethod(self, concreteType, methodName) {
3743 4018
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3744 4019
    }
3747 4022
    let a = alloc::arenaAllocator(&mut self.arena);
3748 4023
    let mut paramTypes: *mut [*Type] = &mut [];
3749 4024
3750 4025
    // Receiver is the first parameter.
3751 4026
    let receiverPtrType = Type::Pointer {
3752 -
        class: types::PointerClass::Owned,
4027 +
        class: receiverClass,
3753 4028
        target: allocType(self, concreteType),
3754 4029
        mutable: receiverMut,
3755 4030
    };
3756 4031
    paramTypes.append(allocType(self, receiverPtrType), a);
3757 4032
3776 4051
    }
3777 4052
3778 4053
    let retTypePtr = allocType(self, returnType);
3779 4054
    let throwList = &throwTypes[..];
3780 4055
4056 +
    let isUnsafe = ast::hasAttribute(attrMask, ast::Attribute::Unsafe);
3781 4057
    // Full function type (receiver + params) for lowering.
3782 4058
    let fullFnType = FnType {
3783 -
        paramTypes: &paramTypes[..], returnType: retTypePtr, throwList, localCount: 0,
4059 +
        paramTypes: &paramTypes[..],
4060 +
        returnType: retTypePtr,
4061 +
        throwList,
4062 +
        isUnsafe,
4063 +
        localCount: 0,
3784 4064
    };
3785 4065
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
3786 4066
3787 4067
    // Function type excluding receiver, for call arg checking.
3788 4068
    let checkFnType = FnType {
3789 -
        paramTypes: &paramTypes[1..], returnType: retTypePtr, throwList, localCount: 0,
4069 +
        paramTypes: &paramTypes[1..],
4070 +
        returnType: retTypePtr,
4071 +
        throwList,
4072 +
        isUnsafe,
4073 +
        localCount: 0,
3790 4074
    };
3791 4075
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 4076
    // Create a symbol for the method without binding it into the module scope.
3803 4077
    let sym = allocSymbol(self, SymbolData::Value {
3804 4078
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
3805 4079
    }, methodName, node, attrMask);
3806 4080
3816 4090
        concreteType,
3817 4091
        concreteTypeName: typeName,
3818 4092
        name: methodName,
3819 4093
        fnType: allocFnType(self, checkFnType),
3820 4094
        mutable: receiverMut,
4095 +
        receiverClass,
3821 4096
        symbol: sym,
3822 4097
    };
3823 4098
    set self.methodsLen += 1;
3824 4099
}
3825 4100
3876 4151
    let mut variants: *mut [UnionVariant] = &mut [];
3877 4152
3878 4153
    // Create a temporary nominal type to replace the placeholder.This prevents infinite recursion
3879 4154
    // when a variant references this union type (e.g. record payloads with `*[Self]`).
3880 4155
    // TODO: It would be best to have a resolving state eg. `Visiting` for this situation.
4156 +
    let declaredLinear = try resolveLinearDerive(self, decl.derives);
3881 4157
    set *nominalTy = NominalType::Union(UnionType {
3882 4158
        variants: &[],
3883 4159
        layout: Layout { size: 0, alignment: 0 },
3884 4160
        valOffset: 0,
3885 -
        isAllVoid: true
4161 +
        isAllVoid: true,
4162 +
        declaredLinear,
3886 4163
    });
3887 4164
3888 -
    try visitList(self, decl.derives);
3889 -
3890 4165
    assert decl.variants.len <= MAX_UNION_VARIANTS, "resolveUnionBody: maximum union variants exceeded";
3891 4166
    let mut iota: u32 = 0;
3892 4167
    for variantNode, i in decl.variants {
3893 4168
        let case ast::NodeValue::UnionDeclVariant(variantDecl) = variantNode.value
3894 4169
            else panic "resolveUnionBody: invalid union variant";
3895 4170
        let variantName = try nodeName(self, variantDecl.name);
3896 4171
        // Resolve the variant's payload type if present.
3897 4172
        let mut variantType = Type::Void;
3898 -
        if let ty = try visitOptional(self, variantDecl.type, Type::Unknown) {
3899 -
            set variantType = ty;
4173 +
        if let typeNode = variantDecl.type {
4174 +
            set variantType = try infer(self, typeNode);
4175 +
            try ensureStorableType(self, typeNode, variantType);
3900 4176
        }
3901 4177
        // Process the variant's explicit discriminant value if present.
3902 4178
        try visitOptional(self, variantDecl.value, variantType);
3903 4179
        let tag = variantTag(variantDecl, &mut iota);
3904 4180
        // Create a symbol for this variant.
3917 4193
    set *nominalTy = NominalType::Union(UnionType {
3918 4194
        variants: &variants[..],
3919 4195
        layout: info.layout,
3920 4196
        valOffset: info.valOffset,
3921 4197
        isAllVoid: info.isAllVoid,
4198 +
        declaredLinear,
3922 4199
    });
3923 4200
}
3924 4201
3925 4202
/// Check if a module should be analyzed based on its attributes and build configuration.
3926 4203
fn shouldAnalyzeModule(self: *Resolver, attrs: ?ast::Attributes) -> bool {
4101 4378
    pattern: *ast::Node,
4102 4379
    scrutineeTy: Type,
4103 4380
    mode: IdentMode,
4104 4381
    matchBy: MatchBy
4105 4382
) throws (ResolveError) {
4383 +
    if let case Type::Pointer { target, .. } = scrutineeTy; isDestructuringPattern(pattern) {
4384 +
        try resolveCasePattern(self, pattern, *target, mode, matchBy);
4385 +
        return;
4386 +
    }
4106 4387
    // TODO: Collapse these nested matches.
4107 4388
    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 4389
        case Type::Nominal(info) => {
4117 4390
            try ensureNominalResolved(self, info, pattern);
4118 4391
4119 4392
            match *info {
4120 4393
                case NominalType::Union(unionType) => {
4209 4482
        }
4210 4483
    }
4211 4484
    // Extract item type and store pre-computed loop metadata for the lowerer.
4212 4485
    let mut itemTy: Type = undefined;
4213 4486
    match iterableTy {
4487 +
        case Type::Slice { item, .. } => {
4488 +
            set itemTy = *item;
4489 +
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4490 +
                elemType: item, length: nil, bindingName, indexName
4491 +
            });
4492 +
        }
4214 4493
        case Type::Range { start, .. } => {
4215 4494
            // Iterable ranges must have a start, and since we enforce type
4216 4495
            // equality for start and end, that is always the item type.
4217 4496
            let valType = start else {
4218 4497
                throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable);
4233 4512
                length: arrayInfo.length,
4234 4513
                bindingName,
4235 4514
                indexName,
4236 4515
            });
4237 4516
        }
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 4517
        else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable),
4245 4518
    }
4246 4519
    enterScope(self, node);
4247 4520
    try bindForLoopPattern(self, forStmt.binding, itemTy, false);
4248 4521
4776 5049
{
4777 5050
    let mut bindTy = ty;
4778 5051
    match matchBy {
4779 5052
        case MatchBy::Value => {}
4780 5053
        case MatchBy::Ref => set bindTy = Type::Pointer {
4781 -
            class: types::PointerClass::Owned, target: allocType(self, ty), mutable: false,
5054 +
            class: types::PointerClass::Ref,
5055 +
            target: allocType(self, ty),
5056 +
            mutable: false,
4782 5057
        },
4783 5058
        case MatchBy::MutRef => set bindTy = Type::Pointer {
4784 -
            class: types::PointerClass::Owned, target: allocType(self, ty), mutable: true,
5059 +
            class: types::PointerClass::Ref,
5060 +
            target: allocType(self, ty),
5061 +
            mutable: true,
4785 5062
        },
4786 5063
    }
4787 5064
    match binding.value {
4788 5065
        case ast::NodeValue::Placeholder => {
4789 5066
            // Nothing to do.
4900 5177
            });
4901 5178
        }
4902 5179
    }
4903 5180
}
4904 5181
5182 +
/// Return whether a case pattern introduces value bindings.
5183 +
fn casePatternIntroducesBindings(pattern: *ast::Node, nested: bool) -> bool {
5184 +
    match pattern.value {
5185 +
        case ast::NodeValue::Ident(_) => return nested,
5186 +
        case ast::NodeValue::Call(call) => {
5187 +
            for arg in call.args {
5188 +
                if casePatternIntroducesBindings(arg, true) {
5189 +
                    return true;
5190 +
                }
5191 +
            }
5192 +
        }
5193 +
        case ast::NodeValue::RecordLit(lit) => {
5194 +
            for fieldNode in lit.fields {
5195 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
5196 +
                    else continue;
5197 +
                if casePatternIntroducesBindings(field.value, true) {
5198 +
                    return true;
5199 +
                }
5200 +
            }
5201 +
        }
5202 +
        case ast::NodeValue::ArrayLit(items) => {
5203 +
            for item in items {
5204 +
                if casePatternIntroducesBindings(item, true) {
5205 +
                    return true;
5206 +
                }
5207 +
            }
5208 +
        }
5209 +
        else => {}
5210 +
    }
5211 +
    return false;
5212 +
}
5213 +
4905 5214
/// Analyze a `let-else` guard.
4906 5215
fn resolveLetElse(self: *mut Resolver, node: *ast::Node, letElse: ast::LetElse) -> Type
4907 5216
    throws (ResolveError)
4908 5217
{
4909 5218
    let pat = &letElse.pattern;
4915 5224
            let case Type::Optional(inner) = exprTy else {
4916 5225
                throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional);
4917 5226
            };
4918 5227
            let payloadTy = *inner;
4919 5228
            let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0);
4920 -
            // The `else` branch must be assignable to the payload type.
5229 +
            // The `else` branch supplies the binding when the optional is nil.
4921 5230
            try checkAssignable(self, letElse.elseBranch, payloadTy);
4922 -
4923 5231
            return setNodeType(self, node, Type::Void);
4924 5232
        }
4925 5233
        case ast::PatternKind::Case => {
4926 -
            // Analyze pattern against expression type.
4927 -
            try resolveCasePattern(self, pat.pattern, exprTy, IdentMode::Compare, MatchBy::Value);
5234 +
            // Resolve the failure path before introducing success-only bindings.
5235 +
            let elseTy = try checkAssignable(self, letElse.elseBranch, exprTy);
5236 +
            try resolveCasePattern(
5237 +
                self,
5238 +
                pat.pattern,
5239 +
                exprTy,
5240 +
                IdentMode::Compare,
5241 +
                MatchBy::Value,
5242 +
            );
5243 +
            if let guardExpr = pat.guard {
5244 +
                try checkBoolean(self, guardExpr);
5245 +
            }
5246 +
            if elseTy <> Type::Never and
5247 +
               casePatternIntroducesBindings(pat.pattern, false)
5248 +
            {
5249 +
                throw emitError(
5250 +
                    self,
5251 +
                    letElse.elseBranch,
5252 +
                    ErrorKind::LinearLetElseMustTerminate,
5253 +
                );
5254 +
            }
4928 5255
        }
4929 5256
    }
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 5257
    return setNodeType(self, node, Type::Void);
4937 5258
}
4938 5259
4939 5260
/// Analyze builtin function calls like `@sizeOf(T)` and `@alignOf(T)`.
4940 5261
fn resolveBuiltinCall(
4950 5271
                expected: 2,
4951 5272
                actual: args.len as u32,
4952 5273
            }));
4953 5274
        }
4954 5275
        let ptrType = try visit(self, args[0], Type::Unknown);
4955 -
        let case Type::Pointer { target, mutable, .. } = ptrType else {
5276 +
        let case Type::Pointer { class, target, mutable } = ptrType else {
4956 5277
            throw emitError(self, node, ErrorKind::ExpectedPointer);
4957 5278
        };
4958 5279
        let _ = try checkAssignable(self, args[1], Type::U32);
4959 5280
        if args.len == 3 {
4960 5281
            let _ = try checkAssignable(self, args[2], Type::U32);
4961 5282
        }
4962 -
        return setNodeType(self, node, Type::Slice {
4963 -
            class: types::PointerClass::Owned, item: target, mutable,
4964 -
        });
5283 +
        return setNodeType(self, node, Type::Slice { class, item: target, mutable });
4965 5284
    }
4966 5285
    if args.len <> 1 {
4967 5286
        throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch {
4968 5287
            expected: 1,
4969 5288
            actual: args.len as u32,
5028 5347
    throws (ResolveError)
5029 5348
{
5030 5349
    // Intercept method calls on slices before inferring the callee.
5031 5350
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
5032 5351
        let parentTy = try infer(self, access.parent);
5352 +
        if isUnsafePointerType(parentTy) {
5353 +
            try requireUnsafe(self, access.parent);
5354 +
        }
5033 5355
        let subjectTy = autoDeref(parentTy);
5034 5356
5035 5357
        if let case Type::Slice { item, mutable, .. } = subjectTy {
5036 5358
            let methodName = try nodeName(self, access.child);
5037 5359
            if methodName == "append" {
5038 -
                return try resolveSliceAppend(self, node, access.parent, parentTy, call.args, item, mutable);
5360 +
                return try resolveSliceAppend(
5361 +
                    self, node, access.parent, parentTy, call.args, item, mutable
5362 +
                );
5039 5363
            }
5040 5364
            if methodName == "delete" {
5041 -
                return try resolveSliceDelete(self, node, access.parent, call.args, item, mutable);
5365 +
                return try resolveSliceDelete(
5366 +
                    self, node, access.parent, call.args, item, mutable
5367 +
                );
5042 5368
            }
5043 5369
        }
5044 5370
    }
5045 5371
    let calleeTy = try infer(self, call.callee);
5372 +
    if let case Type::Fn(info) = calleeTy {
5373 +
        try checkUnsafeCall(self, call.callee, info);
5374 +
    }
5046 5375
5047 5376
    // Check if callee is a union variant and dispatch to constructor handler.
5048 5377
    // TODO: Move this out. We should decide on this earlier, based on the callee.
5049 5378
    if let calleeSym = symbolFor(self, call.callee) {
5050 5379
        if let case SymbolData::Variant { decl, .. } = calleeSym.data {
5077 5406
5078 5407
        if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy {
5079 5408
            let methodName = try nodeName(self, access.child);
5080 5409
            let method = findTraitMethod(traitInfo, methodName)
5081 5410
                else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName));
5082 -
5083 5411
            // Reject mutable-receiver methods called on immutable trait objects.
5084 5412
            if method.mutable and not objMutable {
5085 5413
                throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5086 5414
            }
5087 5415
            try checkCallArgs(self, node, call, method.fnType, ctx);
5088 5416
            setTraitMethodCall(self, node, traitInfo, method.index);
5089 -
5090 5417
            return setNodeType(self, node, *method.fnType.returnType);
5091 5418
        }
5092 5419
5093 5420
        // Check for a standalone method call on a concrete type.
5094 5421
        if let case Type::Nominal(_) = subjectTy {
5198 5525
            try checkSliceRangeIndices(self, range);
5199 5526
5200 5527
            let mut item: *Type = undefined;
5201 5528
            let mut capacity: ?u32 = nil;
5202 5529
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;
5530 +
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
5531 +
                if not sliceMutable {
5532 +
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
5208 5533
                }
5209 -
                case Type::Slice { item: i, mutable, .. } => {
5210 -
                    if not mutable { throw emitError(self, container, ErrorKind::ImmutableBinding); }
5211 -
                    set item = i;
5534 +
                set item = sliceItem;
5535 +
            } else {
5536 +
                match subjectTy {
5537 +
                    case Type::Array(a) => {
5538 +
                        try validateArraySliceBounds(self, range, a.length, node);
5539 +
                        set item = a.item;
5540 +
                        set capacity = a.length;
5541 +
                    }
5542 +
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5212 5543
                }
5213 -
                else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
5214 5544
            }
5215 5545
            // RHS is either a fill value or a source slice.
5216 5546
            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 });
5547 +
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
5548 +
                if *sourceItem <> *item {
5549 +
                    throw emitTypeMismatch(
5550 +
                        self,
5551 +
                        assign.right,
5552 +
                        TypeMismatch { expected: *item, actual: *sourceItem },
5553 +
                    );
5220 5554
                }
5221 5555
            } else {
5222 5556
                try checkAssignable(self, assign.right, *item);
5223 5557
            }
5224 5558
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
5310 5644
        let _ = try infer(self, container);
5311 5645
        try checkSliceRangeIndices(self, range);
5312 5646
        throw emitError(self, node, ErrorKind::SliceRequiresAddress);
5313 5647
    }
5314 5648
    let containerTy = try infer(self, container);
5649 +
    if isUnsafePointerType(containerTy) {
5650 +
        try requireUnsafe(self, container);
5651 +
    }
5315 5652
    try checkIndex(self, indexNode);
5316 5653
    let subjectTy = autoDeref(containerTy);
5654 +
    if let case Type::Slice { item, .. } = subjectTy {
5655 +
        return setNodeType(self, node, *item);
5656 +
    }
5317 5657
5318 5658
    match subjectTy {
5319 5659
        case Type::Array(arrayInfo) => {
5320 5660
            return setNodeType(self, node, *arrayInfo.item);
5321 5661
        }
5322 -
        case Type::Slice { item, .. } => {
5323 -
            return setNodeType(self, node, *item);
5324 -
        }
5325 5662
        else => {
5326 5663
            throw emitError(self, container, ErrorKind::ExpectedIndexable);
5327 5664
        }
5328 5665
    }
5329 5666
}
5680 6017
/// Analyze a field access expression.
5681 6018
fn resolveFieldAccess(self: *mut Resolver, node: *ast::Node, access: ast::Access) -> Type
5682 6019
    throws (ResolveError)
5683 6020
{
5684 6021
    let parentTy = try infer(self, access.parent);
6022 +
    if isUnsafePointerType(parentTy) {
6023 +
        try requireUnsafe(self, access.parent);
6024 +
    }
5685 6025
    let subjectTy = autoDeref(parentTy);
5686 -
5687 -
    match subjectTy {
5688 -
        case Type::Nominal(NominalType::Record(recordType)) => {
6026 +
    if let case Type::Slice { class, item, mutable } = subjectTy {
6027 +
        let fieldNode = access.child;
6028 +
        let fieldName = try nodeName(self, fieldNode);
6029 +
        if mem::eq(fieldName, PTR_FIELD) {
6030 +
            setRecordFieldIndex(self, fieldNode, 0);
6031 +
            return setNodeType(
6032 +
                self,
6033 +
                node,
6034 +
                Type::Pointer { class, target: item, mutable },
6035 +
            );
6036 +
        }
6037 +
        if mem::eq(fieldName, LEN_FIELD) {
6038 +
            setRecordFieldIndex(self, fieldNode, 1);
6039 +
            return setNodeType(self, node, Type::U32);
6040 +
        }
6041 +
        if mem::eq(fieldName, CAP_FIELD) {
6042 +
            setRecordFieldIndex(self, fieldNode, 2);
6043 +
            return setNodeType(self, node, Type::U32);
6044 +
        }
6045 +
        throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName));
6046 +
    }
6047 +
    if let case Type::TraitObject { traitInfo, .. } = subjectTy {
6048 +
        let fieldName = try nodeName(self, access.child);
6049 +
        let method = findTraitMethod(traitInfo, fieldName)
6050 +
            else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
6051 +
        return setNodeType(self, node, Type::Fn(method.fnType));
6052 +
    }
6053 +
6054 +
    match subjectTy {
6055 +
        case Type::Nominal(NominalType::Record(recordType)) => {
5689 6056
            let fieldNode = access.child;
5690 6057
            let fieldName = try nodeName(self, fieldNode);
5691 6058
            if let fieldIndex = findRecordField(&recordType, fieldName) {
5692 6059
                let fieldTy = recordType.fields[fieldIndex].fieldType;
5693 6060
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5709 6076
5710 6077
                return setNodeType(self, node, Type::U32);
5711 6078
            }
5712 6079
            throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName));
5713 6080
        }
5714 -
        case Type::Slice { item, mutable, .. } => {
5715 -
            let fieldNode = access.child;
5716 -
            let fieldName = try nodeName(self, fieldNode);
5717 6081
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 -
5742 -
            return setNodeType(self, node, Type::Fn(method.fnType));
5743 -
        }
5744 6082
        else => {
5745 6083
            // Check for standalone methods on any nominal type (e.g. unions).
5746 6084
            if let case Type::Nominal(_) = subjectTy {
5747 6085
                let fieldName = try nodeName(self, access.child);
5748 6086
                if let method = findMethod(self, subjectTy, fieldName) {
5849 6187
5850 6188
/// Analyze an address-of expression.
5851 6189
fn resolveAddressOf(self: *mut Resolver, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
5852 6190
    throws (ResolveError)
5853 6191
{
6192 +
    // Linear source treats every address expression as a call-scoped reference.
6193 +
    // Legacy packages keep their historical owning-address inference.
6194 +
    let class = types::PointerClass::Ref
6195 +
        if self.linearEnabled or isRefType(hint)
6196 +
        else types::PointerClass::Owned;
5854 6197
    if addr.mutable {
5855 6198
        if not try canBorrowMutFrom(self, addr.target) {
5856 6199
            throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
5857 6200
        }
5858 6201
    }
5864 6207
            try checkSliceRangeIndices(self, range);
5865 6208
5866 6209
            let mut item: *Type = undefined;
5867 6210
            let mut capacity: ?u32 = nil;
5868 6211
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;
6212 +
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
6213 +
                if addr.mutable and not sliceMutable {
6214 +
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
5874 6215
                }
5875 -
                case Type::Slice { item: sliceItem, mutable, .. } => {
5876 -
                    if addr.mutable and not mutable {
5877 -
                        throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
6216 +
                set item = sliceItem;
6217 +
            } else {
6218 +
                match subjectTy {
6219 +
                    case Type::Array(arrayInfo) => {
6220 +
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
6221 +
                        set item = arrayInfo.item;
6222 +
                        set capacity = arrayInfo.length;
6223 +
                    }
6224 +
                    else => {
6225 +
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
5878 6226
                    }
5879 -
                    set item = sliceItem;
5880 -
                }
5881 -
                else => {
5882 -
                    throw emitError(self, container, ErrorKind::ExpectedIndexable);
5883 6227
                }
5884 6228
            }
5885 -
            let sliceTy = Type::Slice {
5886 -
                class: types::PointerClass::Owned, item, mutable: addr.mutable,
5887 -
            };
6229 +
            let sliceTy = Type::Slice { class, item, mutable: addr.mutable };
5888 6230
            let alloc = allocType(self, sliceTy);
5889 6231
            setSliceRangeInfo(self, node, SliceRangeInfo {
5890 6232
                itemType: item,
5891 6233
                mutable: addr.mutable,
5892 6234
                capacity,
5918 6260
    if let case Type::Array(arrayInfo) = targetTy {
5919 6261
        match addr.target.value {
5920 6262
            case ast::NodeValue::ArrayLit(_),
5921 6263
                 ast::NodeValue::ArrayRepeatLit(_) =>
5922 6264
            {
5923 -
                let sliceTy = Type::Slice {
5924 -
                    class: types::PointerClass::Owned,
5925 -
                    item: arrayInfo.item,
5926 -
                    mutable: addr.mutable,
5927 -
                };
6265 +
                let sliceTy = Type::Slice { class, item: arrayInfo.item, mutable: addr.mutable };
5928 6266
                return setNodeType(self, node, *allocType(self, sliceTy));
5929 6267
            }
5930 6268
            else => {}
5931 6269
        }
5932 6270
    }
5933 6271
    let pointerTy = Type::Pointer {
5934 -
        class: types::PointerClass::Owned,
5935 -
        target: allocType(self, targetTy),
5936 -
        mutable: addr.mutable,
6272 +
        class, target: allocType(self, targetTy), mutable: addr.mutable,
5937 6273
    };
5938 6274
    return setNodeType(self, node, pointerTy);
5939 6275
}
5940 6276
5941 6277
/// Analyze a dereference expression.
5942 6278
fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type
5943 6279
    throws (ResolveError)
5944 6280
{
5945 6281
    let operandTy = try visit(self, targetNode, hint);
5946 -
    if let case Type::Pointer { target, .. } = operandTy {
6282 +
    if let case Type::Pointer { class, target, .. } = operandTy {
6283 +
        if class == types::PointerClass::Unsafe {
6284 +
            try requireUnsafe(self, targetNode);
6285 +
        }
5947 6286
        // Disallow dereferencing opaque pointers.
5948 6287
        if *target == Type::Opaque {
5949 6288
            throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref);
5950 6289
        }
5951 6290
        return setNodeType(self, node, *target);
5997 6336
        // Disallow slice to numeric; slices are fat pointers.
5998 6337
    } else if isAddressType(source) and isNumericType(target) {
5999 6338
        return true;
6000 6339
    }
6001 6340
    // 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 {
6341 +
    if let case Type::Pointer {
6342 +
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6343 +
    } = source {
6344 +
        if let case Type::Pointer {
6345 +
            class: targetClass, target: targetTarget, mutable: targetMutable,
6346 +
        } = target {
6347 +
            if sourceClass <> targetClass {
6348 +
                return false;
6349 +
            }
6350 +
            if targetMutable and not sourceMutable {
6351 +
                return false;
6352 +
            }
6004 6353
            if isOpaquePointer(source) or isOpaquePointer(target) {
6005 6354
                return true;
6006 6355
            }
6007 6356
            return isValidCast(*sourceTarget, *targetTarget);
6008 6357
        }
6009 6358
    }
6010 6359
    // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`,
6011 6360
    // or element types are castable.
6012 -
    if let case Type::Slice { item: sourceItem, .. } = source {
6013 -
        if let case Type::Slice { item: targetItem, .. } = target {
6361 +
    if let case Type::Slice {
6362 +
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6363 +
    } = source {
6364 +
        if let case Type::Slice {
6365 +
            class: targetClass, item: targetItem, mutable: targetMutable,
6366 +
        } = target {
6367 +
            if sourceClass <> targetClass {
6368 +
                return false;
6369 +
            }
6370 +
            if targetMutable and not sourceMutable {
6371 +
                return false;
6372 +
            }
6014 6373
            if isOpaqueSlice(source) or isOpaqueSlice(target) {
6015 6374
                return true;
6016 6375
            }
6017 6376
            if *targetItem == Type::U8 {
6018 6377
                return true;
6027 6386
fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type
6028 6387
    throws (ResolveError)
6029 6388
{
6030 6389
    let targetTy = try infer(self, expr.type);
6031 6390
    let sourceTy = try visit(self, expr.value, targetTy);
6391 +
    if isUnsafePointerType(sourceTy) or isUnsafePointerType(targetTy) {
6392 +
        try requireUnsafe(self, node);
6393 +
    }
6032 6394
6033 6395
    assert sourceTy <> Type::Unknown;
6034 6396
    assert targetTy <> Type::Unknown;
6035 6397
6036 -
    if isValidCast(sourceTy, targetTy) {
6398 +
    let mut valid = isValidCast(sourceTy, targetTy);
6399 +
    if let case Type::Pointer {
6400 +
        class: sourceClass, target: sourceTarget, mutable: sourceMutable,
6401 +
    } = sourceTy {
6402 +
        if let case Type::Pointer {
6403 +
            class: targetClass, target: targetTarget, mutable: targetMutable,
6404 +
        } = targetTy {
6405 +
            if sourceClass == types::PointerClass::Ref and
6406 +
               targetClass == types::PointerClass::Unsafe and
6407 +
               (not targetMutable or sourceMutable) and
6408 +
               isValidCast(*sourceTarget, *targetTarget)
6409 +
            {
6410 +
                set valid = true;
6411 +
            }
6412 +
        }
6413 +
    }
6414 +
    if let case Type::Slice {
6415 +
        class: sourceClass, item: sourceItem, mutable: sourceMutable,
6416 +
    } = sourceTy {
6417 +
        if let case Type::Slice {
6418 +
            class: targetClass, item: targetItem, mutable: targetMutable,
6419 +
        } = targetTy {
6420 +
            if sourceClass == types::PointerClass::Ref and
6421 +
               targetClass == types::PointerClass::Unsafe and
6422 +
               (not targetMutable or sourceMutable) and
6423 +
               isValidCast(*sourceItem, *targetItem)
6424 +
            {
6425 +
                set valid = true;
6426 +
            }
6427 +
        }
6428 +
    }
6429 +
    if valid {
6037 6430
        // Propagate the constant value after applying the cast's target-width
6038 6431
        // truncation and signed interpretation.
6039 6432
        if let value = constValueEntry(self, expr.value) {
6040 6433
            if let case ConstValue::Int(i) = value {
6041 6434
                setNodeConstValue(self, node, castConstInt(i, targetTy));
6478 6871
        case ast::BinaryOp::Eq,
6479 6872
             ast::BinaryOp::Ne =>
6480 6873
        {
6481 6874
            let leftTy = try infer(self, binop.left);
6482 6875
            let rightTy = try visit(self, binop.right, leftTy);
6876 +
            if isUnsafePointerType(leftTy) or isUnsafePointerType(rightTy) {
6877 +
                try requireUnsafe(self, node);
6878 +
            }
6483 6879
6484 6880
            if not isComparable(leftTy, rightTy) {
6485 6881
                throw emitTypeMismatch(self, binop.right, TypeMismatch {
6486 6882
                    expected: leftTy,
6487 6883
                    actual: rightTy,
6507 6903
            // Check for pointer arithmetic before numeric check.
6508 6904
            if binop.op == ast::BinaryOp::Add or binop.op == ast::BinaryOp::Sub {
6509 6905
                let leftTy = try infer(self, binop.left);
6510 6906
                let rightTy = try visit(self, binop.right, leftTy);
6511 6907
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);
6908 +
                // Allow arithmetic on owning pointers and unsafe pointers, but
6909 +
                // never on references.
6910 +
                if let case Type::Pointer { class: leftClass, target: leftTarget, .. } = leftTy {
6911 +
                    if *leftTarget == Type::Opaque {
6912 +
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6913 +
                    }
6914 +
                    if leftClass <> types::PointerClass::Ref
6915 +
                        and isNumericType(rightTy)
6916 +
                    {
6917 +
                        if leftClass == types::PointerClass::Unsafe {
6918 +
                            try requireUnsafe(self, node);
6919 +
                        }
6920 +
                        return setNodeType(self, node, leftTy);
6921 +
                    }
6522 6922
                }
6523 -
                if binop.op == ast::BinaryOp::Add {
6524 -
                    if let case Type::Pointer { .. } = rightTy; isNumericType(leftTy) {
6923 +
                if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy {
6924 +
                    if *rightTarget == Type::Opaque {
6925 +
                        throw emitError(self, node, ErrorKind::OpaquePointerArithmetic);
6926 +
                    }
6927 +
                    if binop.op == ast::BinaryOp::Add
6928 +
                        and rightClass <> types::PointerClass::Ref
6929 +
                        and isNumericType(leftTy)
6930 +
                    {
6931 +
                        if rightClass == types::PointerClass::Unsafe {
6932 +
                            try requireUnsafe(self, node);
6933 +
                        }
6525 6934
                        return setNodeType(self, node, rightTy);
6526 6935
                    }
6527 6936
                }
6528 6937
            }
6529 6938
            let leftTy = try checkNumeric(self, binop.left);
6601 7010
        },
6602 7011
    };
6603 7012
    return setNodeType(self, node, resultTy);
6604 7013
}
6605 7014
7015 +
6606 7016
/// Resolve a type signature node and set its type.
6607 7017
fn inferTypeSig(self: *mut Resolver, node: *ast::Node, sig: ast::TypeSig) -> Type
6608 7018
    throws (ResolveError)
6609 7019
{
6610 7020
    let resolved = try resolveTypeSig(self, node, sig);
6690 7100
                    actual: t.throwList.len,
6691 7101
                }));
6692 7102
            }
6693 7103
6694 7104
            for paramNode in t.params {
6695 -
                let paramTy = try infer(self, paramNode);
7105 +
                let paramTy = try resolveValueType(self, paramNode);
6696 7106
                paramTypes.append(allocType(self, paramTy), a);
6697 7107
            }
6698 7108
            for tyNode in t.throwList {
6699 -
                let throwTy = try infer(self, tyNode);
7109 +
                let throwTy = try resolveValueType(self, tyNode);
7110 +
                try ensureStorableType(self, tyNode, throwTy);
6700 7111
                throwList.append(allocType(self, throwTy), a);
6701 7112
            }
6702 7113
            let mut retType = allocType(self, Type::Void);
6703 7114
            if let ret = t.returnType {
6704 -
                set retType = allocType(self, try infer(self, ret));
7115 +
                let resolvedRet = try resolveValueType(self, ret);
7116 +
                try ensureStorableType(self, ret, resolvedRet);
7117 +
                set retType = allocType(self, resolvedRet);
6705 7118
            }
6706 7119
            let fnType = FnType {
6707 7120
                paramTypes: &paramTypes[..],
6708 7121
                returnType: retType,
6709 7122
                throwList: &throwList[..],
7123 +
                isUnsafe: false,
6710 7124
                localCount: 0,
6711 7125
            };
6712 7126
            return Type::Fn(allocFnType(self, fnType));
6713 7127
        }
7128 +
        // Resolve an opaque trait object signature.
6714 7129
        case ast::TypeSig::TraitObject { class, traitName, mutable } => {
6715 7130
            let sym = try resolveNamePath(self, traitName);
6716 7131
            let case SymbolData::Trait(traitInfo) = sym.data
6717 7132
                else throw emitError(self, traitName, ErrorKind::Internal);
6718 7133
            setNodeSymbol(self, traitName, sym);
6719 -
6720 -
            return Type::TraitObject {
6721 -
                class,
6722 -
                traitInfo,
6723 -
                mutable,
6724 -
            };
7134 +
            return Type::TraitObject { class, traitInfo, mutable };
6725 7135
        }
6726 7136
    }
6727 7137
}
6728 7138
6729 7139
/// Check if a type can be used for inferrence.
6730 7140
fn isTypeInferrable(type: Type) -> bool {
7141 +
    if let case Type::Pointer { target, .. } = type {
7142 +
        return isTypeInferrable(*target);
7143 +
    }
6731 7144
    match type {
6732 7145
        case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false,
6733 7146
        case Type::Array(ary) => return isTypeInferrable(*ary.item),
6734 7147
        case Type::Optional(opt) => return isTypeInferrable(*opt),
6735 -
        case Type::Pointer { target, .. } => return isTypeInferrable(*target),
6736 7148
        else => return true,
6737 7149
    }
6738 7150
}
6739 7151
6740 7152
/// Analyze a standalone expression by wrapping it in a synthetic function.
6898 7310
    for stmt in block.statements {
6899 7311
        try visitDecl(res, stmt);
6900 7312
    }
6901 7313
}
6902 7314
7315 +
/// Maximum number of linear bindings active in one function.
7316 +
constant MAX_LINEAR_BINDINGS: u32 = 32;
7317 +
/// Maximum nesting depth tracked for loops.
7318 +
constant MAX_LINEAR_LOOP_DEPTH: u32 = 16;
7319 +
7320 +
/// How an expression uses a linear result.
7321 +
union LinearUse {
7322 +
    Consume,
7323 +
    Observe,
7324 +
    Borrow,
7325 +
    Discard,
7326 +
    Place,
7327 +
}
7328 +
7329 +
/// Per-control-flow-path ownership state.
7330 +
record LinearEnv {
7331 +
    symbols: [?*mut Symbol; MAX_LINEAR_BINDINGS],
7332 +
    available: u64,
7333 +
    len: u32,
7334 +
    terminated: bool,
7335 +
}
7336 +
7337 +
/// Function-local exact-use checker state.
7338 +
record LinearChecker {
7339 +
    resolver: *mut Resolver,
7340 +
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
7341 +
    loopAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
7342 +
    loopExitAvailable: [u64; MAX_LINEAR_LOOP_DEPTH],
7343 +
    loopHasNaturalExit: [bool; MAX_LINEAR_LOOP_DEPTH],
7344 +
    loopBreakSeen: [bool; MAX_LINEAR_LOOP_DEPTH],
7345 +
    loopDepth: u32,
7346 +
}
7347 +
7348 +
/// Find a tracked binding by symbol identity.
7349 +
fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 {
7350 +
    for i in 0..env.len {
7351 +
        if let bound = env.symbols[i]; bound == sym {
7352 +
            return i;
7353 +
        }
7354 +
    }
7355 +
    return nil;
7356 +
}
7357 +
7358 +
/// Return whether a tracked binding is still available.
7359 +
fn linearBindingAvailable(env: *LinearEnv, index: u32) -> bool {
7360 +
    return (env.available & ((1 as u64) << (index as u64))) <> 0;
7361 +
}
7362 +
7363 +
/// Add a local binding when its resolved type is linear.
7364 +
fn addLinearBinding(checker: *mut LinearChecker, env: *mut LinearEnv, node: *ast::Node)
7365 +
    throws (ResolveError)
7366 +
{
7367 +
    let sym = symbolFor(checker.resolver, node) else return;
7368 +
    let case SymbolData::Value { type: ty, .. } = sym.data else return;
7369 +
    if not isLinear(ty) {
7370 +
        return;
7371 +
    }
7372 +
    if env.len >= MAX_LINEAR_BINDINGS {
7373 +
        throw emitError(checker.resolver, node, ErrorKind::Internal);
7374 +
    }
7375 +
    set env.symbols[env.len] = sym;
7376 +
    set env.available |= (1 as u64) << (env.len as u64);
7377 +
    set env.len += 1;
7378 +
}
7379 +
7380 +
/// Require all bindings introduced after `start` to have been consumed.
7381 +
fn finishLinearScope(
7382 +
    checker: *mut LinearChecker,
7383 +
    env: *mut LinearEnv,
7384 +
    start: u32,
7385 +
) throws (ResolveError) {
7386 +
    if not env.terminated {
7387 +
        for i in start..env.len {
7388 +
            if linearBindingAvailable(env, i) {
7389 +
                let sym = env.symbols[i] else panic "finishLinearScope: missing symbol";
7390 +
                throw emitError(
7391 +
                    checker.resolver,
7392 +
                    sym.node,
7393 +
                    ErrorKind::LinearNotConsumed(sym.name),
7394 +
                );
7395 +
            }
7396 +
        }
7397 +
    }
7398 +
    set env.len = start;
7399 +
}
7400 +
7401 +
/// Consume a tracked identifier exactly once.
7402 +
fn consumeLinearIdent(
7403 +
    checker: *mut LinearChecker,
7404 +
    env: *mut LinearEnv,
7405 +
    node: *ast::Node,
7406 +
) throws (ResolveError) {
7407 +
    let sym = symbolFor(checker.resolver, node) else return;
7408 +
    let index = findLinearBinding(env, sym) else return;
7409 +
    if not linearBindingAvailable(env, index) {
7410 +
        throw emitError(
7411 +
            checker.resolver,
7412 +
            node,
7413 +
            ErrorKind::LinearUseAfterConsume(sym.name),
7414 +
        );
7415 +
    }
7416 +
    set env.available &= ~((1 as u64) << (index as u64));
7417 +
}
7418 +
7419 +
/// Verify that two live branches agree on every outer binding.
7420 +
fn joinLinearBranches(
7421 +
    checker: *mut LinearChecker,
7422 +
    env: *mut LinearEnv,
7423 +
    left: LinearEnv,
7424 +
    right: LinearEnv,
7425 +
    node: *ast::Node,
7426 +
) throws (ResolveError) {
7427 +
    if left.terminated and right.terminated {
7428 +
        set *env = left;
7429 +
        set env.terminated = true;
7430 +
        return;
7431 +
    }
7432 +
    if left.terminated {
7433 +
        set *env = right;
7434 +
        return;
7435 +
    }
7436 +
    if right.terminated {
7437 +
        set *env = left;
7438 +
        return;
7439 +
    }
7440 +
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7441 +
    for i in 0..left.len {
7442 +
        if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) {
7443 +
            let sym = left.symbols[i] else panic "joinLinearBranches: missing symbol";
7444 +
            throw emitError(
7445 +
                checker.resolver,
7446 +
                node,
7447 +
                ErrorKind::LinearBranchMismatch(sym.name),
7448 +
            );
7449 +
        }
7450 +
    }
7451 +
    set *env = left;
7452 +
}
7453 +
7454 +
/// Require all current bindings to be consumed at a function exit.
7455 +
fn finishLinearExit(
7456 +
    checker: *mut LinearChecker,
7457 +
    env: *mut LinearEnv,
7458 +
) throws (ResolveError) {
7459 +
    for i in 0..env.len {
7460 +
        if linearBindingAvailable(env, i) {
7461 +
            let sym = env.symbols[i] else panic "finishLinearExit: missing symbol";
7462 +
            throw emitError(
7463 +
                checker.resolver,
7464 +
                sym.node,
7465 +
                ErrorKind::LinearNotConsumed(sym.name),
7466 +
            );
7467 +
        }
7468 +
    }
7469 +
    set env.terminated = true;
7470 +
}
7471 +
7472 +
/// Find the local root borrowed or consumed by an argument expression.
7473 +
fn linearRootSymbol(self: *mut Resolver, node: *ast::Node) -> ?*mut Symbol {
7474 +
    match node.value {
7475 +
        case ast::NodeValue::Ident(_) => return symbolFor(self, node),
7476 +
        case ast::NodeValue::AddressOf(addr) => return linearRootSymbol(self, addr.target),
7477 +
        case ast::NodeValue::FieldAccess(access) =>
7478 +
            return linearRootSymbol(self, access.parent),
7479 +
        case ast::NodeValue::Subscript { container, .. } =>
7480 +
            return linearRootSymbol(self, container),
7481 +
        case ast::NodeValue::Deref(target) => return linearRootSymbol(self, target),
7482 +
        else => return nil,
7483 +
    }
7484 +
}
7485 +
7486 +
/// Add the value identifiers introduced by a pattern.
7487 +
fn addLinearPatternBindings(
7488 +
    checker: *mut LinearChecker,
7489 +
    env: *mut LinearEnv,
7490 +
    pattern: *ast::Node,
7491 +
) throws (ResolveError) {
7492 +
    match pattern.value {
7493 +
        case ast::NodeValue::Ident(_) => try addLinearBinding(checker, env, pattern),
7494 +
        case ast::NodeValue::Call(call) => {
7495 +
            for arg in call.args {
7496 +
                try addLinearPatternBindings(checker, env, arg);
7497 +
            }
7498 +
        }
7499 +
        case ast::NodeValue::RecordLit(lit) => {
7500 +
            for fieldNode in lit.fields {
7501 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7502 +
                    else panic "addLinearPatternBindings: expected field";
7503 +
                try addLinearPatternBindings(checker, env, field.value);
7504 +
            }
7505 +
        }
7506 +
        case ast::NodeValue::ArrayLit(items) => {
7507 +
            for item in items {
7508 +
                try addLinearPatternBindings(checker, env, item);
7509 +
            }
7510 +
        }
7511 +
        else => {}
7512 +
    }
7513 +
}
7514 +
7515 +
/// Check a lexical block and exact-use of locals introduced in it.
7516 +
fn checkLinearBlock(
7517 +
    checker: *mut LinearChecker,
7518 +
    env: *mut LinearEnv,
7519 +
    node: *ast::Node,
7520 +
) throws (ResolveError) {
7521 +
    let start = env.len;
7522 +
    let case ast::NodeValue::Block(block) = node.value
7523 +
        else panic "checkLinearBlock: expected block";
7524 +
    for stmt in block.statements {
7525 +
        if env.terminated {
7526 +
            break;
7527 +
        }
7528 +
        try checkLinearNode(checker, env, stmt, LinearUse::Discard);
7529 +
    }
7530 +
    try finishLinearScope(checker, env, start);
7531 +
}
7532 +
7533 +
/// Push a repeated-control-flow boundary.
7534 +
fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) {
7535 +
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
7536 +
    let depth = checker.loopDepth;
7537 +
    set checker.loopMarks[depth] = env.len;
7538 +
    set checker.loopAvailable[depth] = env.available;
7539 +
    set checker.loopExitAvailable[depth] = env.available;
7540 +
    set checker.loopHasNaturalExit[depth] = false;
7541 +
    set checker.loopBreakSeen[depth] = false;
7542 +
    set checker.loopDepth += 1;
7543 +
}
7544 +
7545 +
/// Require a repeated body's outer bindings to match its entry state.
7546 +
fn checkLinearLoopBackEdge(
7547 +
    checker: *mut LinearChecker,
7548 +
    env: *LinearEnv,
7549 +
    node: *ast::Node,
7550 +
) throws (ResolveError) {
7551 +
    if env.terminated {
7552 +
        return;
7553 +
    }
7554 +
    assert checker.loopDepth > 0, "linear loop back edge outside loop";
7555 +
    let depth = checker.loopDepth - 1;
7556 +
    let mark = checker.loopMarks[depth];
7557 +
    let entryAvailable = checker.loopAvailable[depth];
7558 +
    for i in 0..mark {
7559 +
        let bit = (1 as u64) << (i as u64);
7560 +
        if (env.available & bit) <> (entryAvailable & bit) {
7561 +
            let sym = env.symbols[i] else panic "checkLinearLoopBackEdge: missing symbol";
7562 +
            throw emitError(
7563 +
                checker.resolver,
7564 +
                node,
7565 +
                ErrorKind::LinearBranchMismatch(sym.name),
7566 +
            );
7567 +
        }
7568 +
    }
7569 +
}
7570 +
7571 +
/// Record the ownership state of a loop's condition-false exit.
7572 +
fn setLinearLoopNaturalExit(checker: *mut LinearChecker, env: *LinearEnv) {
7573 +
    assert checker.loopDepth > 0, "linear loop exit outside loop";
7574 +
    let depth = checker.loopDepth - 1;
7575 +
    set checker.loopExitAvailable[depth] = env.available;
7576 +
    set checker.loopHasNaturalExit[depth] = true;
7577 +
}
7578 +
7579 +
/// Require a break exit to agree with every other exit from this loop.
7580 +
fn checkLinearLoopBreak(
7581 +
    checker: *mut LinearChecker,
7582 +
    env: *LinearEnv,
7583 +
    node: *ast::Node,
7584 +
) throws (ResolveError) {
7585 +
    assert checker.loopDepth > 0, "linear loop break outside loop";
7586 +
    let depth = checker.loopDepth - 1;
7587 +
    let mark = checker.loopMarks[depth];
7588 +
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
7589 +
        let expected = checker.loopExitAvailable[depth];
7590 +
        for i in 0..mark {
7591 +
            let bit = (1 as u64) << (i as u64);
7592 +
            if (env.available & bit) <> (expected & bit) {
7593 +
                let sym = env.symbols[i] else panic "checkLinearLoopBreak: missing symbol";
7594 +
                throw emitError(
7595 +
                    checker.resolver,
7596 +
                    node,
7597 +
                    ErrorKind::LinearBranchMismatch(sym.name),
7598 +
                );
7599 +
            }
7600 +
        }
7601 +
    } else {
7602 +
        set checker.loopExitAvailable[depth] = env.available;
7603 +
    }
7604 +
    set checker.loopBreakSeen[depth] = true;
7605 +
}
7606 +
7607 +
/// Pop a repeated-control-flow boundary.
7608 +
fn exitLinearLoop(checker: *mut LinearChecker) {
7609 +
    assert checker.loopDepth > 0, "exitLinearLoop: not in loop";
7610 +
    set checker.loopDepth -= 1;
7611 +
}
7612 +
7613 +
/// Check a conditional and merge its ownership states.
7614 +
fn checkLinearIf(
7615 +
    checker: *mut LinearChecker,
7616 +
    env: *mut LinearEnv,
7617 +
    node: *ast::Node,
7618 +
    conditional: ast::If,
7619 +
) throws (ResolveError) {
7620 +
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7621 +
    let base = *env;
7622 +
    let mut thenEnv = base;
7623 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7624 +
    let mut elseEnv = base;
7625 +
    if let branch = conditional.elseBranch {
7626 +
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7627 +
    }
7628 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7629 +
}
7630 +
7631 +
/// Check an expression conditional and merge its ownership states.
7632 +
fn checkLinearCondExpr(
7633 +
    checker: *mut LinearChecker,
7634 +
    env: *mut LinearEnv,
7635 +
    node: *ast::Node,
7636 +
    conditional: ast::CondExpr,
7637 +
    usage: LinearUse,
7638 +
) throws (ResolveError) {
7639 +
    try checkLinearNode(checker, env, conditional.condition, LinearUse::Consume);
7640 +
    let base = *env;
7641 +
    let mut thenEnv = base;
7642 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenExpr, usage);
7643 +
    let mut elseEnv = base;
7644 +
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
7645 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7646 +
}
7647 +
7648 +
/// Check a match expression, including ownership transferred into patterns.
7649 +
fn checkLinearMatch(
7650 +
    checker: *mut LinearChecker,
7651 +
    env: *mut LinearEnv,
7652 +
    node: *ast::Node,
7653 +
    matchExpr: ast::Match,
7654 +
) throws (ResolveError) {
7655 +
    try checkLinearNode(checker, env, matchExpr.subject, LinearUse::Consume);
7656 +
    let base = *env;
7657 +
    let mut haveResult = false;
7658 +
    let mut result = base;
7659 +
    for prongNode in matchExpr.prongs {
7660 +
        let case ast::NodeValue::MatchProng(prong) = prongNode.value
7661 +
            else panic "checkLinearMatch: expected prong";
7662 +
        let mut branch = base;
7663 +
        let bindingsStart = branch.len;
7664 +
        match prong.arm {
7665 +
            case ast::ProngArm::Case(patterns) => {
7666 +
                for pattern in patterns {
7667 +
                    try addLinearPatternBindings(checker, &mut branch, pattern);
7668 +
                }
7669 +
            }
7670 +
            case ast::ProngArm::Binding(binding) => {
7671 +
                try addLinearPatternBindings(checker, &mut branch, binding);
7672 +
            }
7673 +
            case ast::ProngArm::Else => {}
7674 +
        }
7675 +
        if prong.guard <> nil and branch.len > bindingsStart {
7676 +
            throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7677 +
        }
7678 +
        if let guard = prong.guard {
7679 +
            try checkLinearNode(checker, &mut branch, guard, LinearUse::Consume);
7680 +
        }
7681 +
        try checkLinearNode(checker, &mut branch, prong.body, LinearUse::Discard);
7682 +
        try finishLinearScope(checker, &mut branch, bindingsStart);
7683 +
        if haveResult {
7684 +
            try joinLinearBranches(checker, &mut result, result, branch, node);
7685 +
        } else {
7686 +
            set result = branch;
7687 +
            set haveResult = true;
7688 +
        }
7689 +
    }
7690 +
    if haveResult {
7691 +
        set *env = result;
7692 +
    }
7693 +
}
7694 +
7695 +
/// Check call-scoped loans and argument ownership transfers.
7696 +
fn checkLinearCall(
7697 +
    checker: *mut LinearChecker,
7698 +
    env: *mut LinearEnv,
7699 +
    node: *ast::Node,
7700 +
    call: ast::Call,
7701 +
) throws (ResolveError) {
7702 +
    try checkLinearNode(checker, env, call.callee, LinearUse::Observe);
7703 +
    let calleeTy = typeFor(checker.resolver, call.callee) else {
7704 +
        throw emitError(checker.resolver, call.callee, ErrorKind::Internal);
7705 +
    };
7706 +
    let case Type::Fn(info) = calleeTy else {
7707 +
        for arg in call.args {
7708 +
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
7709 +
        }
7710 +
        return;
7711 +
    };
7712 +
    let mut roots: [?*mut Symbol; MAX_FN_PARAMS + 1] = undefined;
7713 +
    let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined;
7714 +
    let mut rootsLen: u32 = 0;
7715 +
7716 +
    // Method function types exclude their implicit receiver. Account for it
7717 +
    // explicitly so owning receivers are consumed and reference receivers
7718 +
    // participate in call-scoped loan conflict checks.
7719 +
    if let case ast::NodeValue::FieldAccess(access) = call.callee.value {
7720 +
        let mut receiverClass = types::PointerClass::Unsafe;
7721 +
        let mut receiverMutable = false;
7722 +
        let mut haveReceiver = false;
7723 +
        match checker.resolver.nodeData.entries[node.id].extra {
7724 +
            case NodeExtra::TraitMethodCall { traitInfo, methodIndex } => {
7725 +
                let method = &traitInfo.methods[methodIndex];
7726 +
                set receiverClass = method.receiverClass;
7727 +
                set receiverMutable = method.mutable;
7728 +
                set haveReceiver = true;
7729 +
            }
7730 +
            case NodeExtra::MethodCall { method } => {
7731 +
                set receiverClass = method.receiverClass;
7732 +
                set receiverMutable = method.mutable;
7733 +
                set haveReceiver = true;
7734 +
            }
7735 +
            else => {}
7736 +
        }
7737 +
        if haveReceiver {
7738 +
            if receiverClass <> types::PointerClass::Unsafe {
7739 +
                let root = linearRootSymbol(checker.resolver, access.parent);
7740 +
                if let rootSym = root {
7741 +
                    set roots[rootsLen] = rootSym;
7742 +
                    set exclusive[rootsLen] =
7743 +
                        receiverClass == types::PointerClass::Owned or receiverMutable;
7744 +
                    set rootsLen += 1;
7745 +
                }
7746 +
            }
7747 +
            if receiverClass == types::PointerClass::Ref {
7748 +
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
7749 +
            } else if receiverClass == types::PointerClass::Owned {
7750 +
                try checkLinearNode(checker, env, access.parent, LinearUse::Consume);
7751 +
            }
7752 +
        }
7753 +
    }
7754 +
7755 +
    for arg, i in call.args {
7756 +
        let expected = *info.paramTypes[i];
7757 +
        let root = linearRootSymbol(checker.resolver, arg);
7758 +
        let mut argExclusive = isLinear(expected);
7759 +
        if let case Type::Pointer { class: types::PointerClass::Ref, mutable, .. } = expected {
7760 +
            set argExclusive = mutable;
7761 +
        } else if let case Type::Slice { class: types::PointerClass::Ref, mutable, .. } = expected {
7762 +
            set argExclusive = mutable;
7763 +
        } else if let case Type::TraitObject {
7764 +
            class: types::PointerClass::Ref, mutable, ..
7765 +
        } = expected {
7766 +
            set argExclusive = mutable;
7767 +
        }
7768 +
        if not isUnsafePointerType(expected) {
7769 +
            if let rootSym = root {
7770 +
                for j in 0..rootsLen {
7771 +
                    if let previous = roots[j] {
7772 +
                        if previous == rootSym and (exclusive[j] or argExclusive) {
7773 +
                            throw emitError(
7774 +
                                checker.resolver,
7775 +
                                arg,
7776 +
                                ErrorKind::BorrowConflict(rootSym.name),
7777 +
                            );
7778 +
                        }
7779 +
                    }
7780 +
                }
7781 +
                set roots[rootsLen] = rootSym;
7782 +
                set exclusive[rootsLen] = argExclusive;
7783 +
                set rootsLen += 1;
7784 +
            }
7785 +
        }
7786 +
        if isRefType(expected) {
7787 +
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
7788 +
        } else {
7789 +
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
7790 +
        }
7791 +
    }
7792 +
}
7793 +
7794 +
/// Check a pattern conditional. Linear scrutinees require an exhaustive match.
7795 +
fn checkLinearIfLet(
7796 +
    checker: *mut LinearChecker,
7797 +
    env: *mut LinearEnv,
7798 +
    node: *ast::Node,
7799 +
    conditional: ast::IfLet,
7800 +
) throws (ResolveError) {
7801 +
    if let subjectTy = typeFor(checker.resolver, conditional.pattern.scrutinee);
7802 +
        isLinear(subjectTy)
7803 +
    {
7804 +
        throw emitError(
7805 +
            checker.resolver,
7806 +
            conditional.pattern.scrutinee,
7807 +
            ErrorKind::LinearPartialMove,
7808 +
        );
7809 +
    }
7810 +
    try checkLinearNode(
7811 +
        checker,
7812 +
        env,
7813 +
        conditional.pattern.scrutinee,
7814 +
        LinearUse::Consume,
7815 +
    );
7816 +
    let base = *env;
7817 +
    let mut thenEnv = base;
7818 +
    let bindingsStart = thenEnv.len;
7819 +
    try addLinearPatternBindings(checker, &mut thenEnv, conditional.pattern.pattern);
7820 +
    if let guard = conditional.pattern.guard {
7821 +
        try checkLinearNode(checker, &mut thenEnv, guard, LinearUse::Consume);
7822 +
    }
7823 +
    try checkLinearNode(checker, &mut thenEnv, conditional.thenBranch, LinearUse::Discard);
7824 +
    try finishLinearScope(checker, &mut thenEnv, bindingsStart);
7825 +
    let mut elseEnv = base;
7826 +
    if let branch = conditional.elseBranch {
7827 +
        try checkLinearNode(checker, &mut elseEnv, branch, LinearUse::Discard);
7828 +
    }
7829 +
    try joinLinearBranches(checker, env, thenEnv, elseEnv, node);
7830 +
}
7831 +
7832 +
/// Check one expression or statement under an ownership-use context.
7833 +
fn checkLinearNode(
7834 +
    checker: *mut LinearChecker,
7835 +
    env: *mut LinearEnv,
7836 +
    node: *ast::Node,
7837 +
    usage: LinearUse,
7838 +
) throws (ResolveError) {
7839 +
    if env.terminated {
7840 +
        return;
7841 +
    }
7842 +
    match node.value {
7843 +
        case ast::NodeValue::Ident(_) => {
7844 +
            if usage == LinearUse::Consume {
7845 +
                try consumeLinearIdent(checker, env, node);
7846 +
            }
7847 +
        }
7848 +
        case ast::NodeValue::ExprStmt(expr) => {
7849 +
            if let exprTy = typeFor(checker.resolver, expr) {
7850 +
                if isLinear(exprTy) {
7851 +
                    throw emitError(checker.resolver, expr, ErrorKind::LinearDiscard);
7852 +
                }
7853 +
            }
7854 +
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
7855 +
        }
7856 +
        case ast::NodeValue::Block(_) => try checkLinearBlock(checker, env, node),
7857 +
        case ast::NodeValue::Let(binding) => {
7858 +
            if let case ast::NodeValue::Undef = binding.value.value {
7859 +
                if let bindingTy = typeFor(checker.resolver, binding.ident);
7860 +
                    isLinear(bindingTy)
7861 +
                {
7862 +
                    throw emitError(
7863 +
                        checker.resolver,
7864 +
                        binding.value,
7865 +
                        ErrorKind::LinearUndefined,
7866 +
                    );
7867 +
                }
7868 +
            }
7869 +
            try checkLinearNode(checker, env, binding.value, LinearUse::Consume);
7870 +
            try addLinearBinding(checker, env, node);
7871 +
        }
7872 +
        case ast::NodeValue::Assign(assign) => {
7873 +
            let mut target: ?u32 = nil;
7874 +
            if let leftTy = typeFor(checker.resolver, assign.left) {
7875 +
                if isLinear(leftTy) {
7876 +
                    if let case ast::NodeValue::Ident(_) = assign.left.value {
7877 +
                        if let sym = symbolFor(checker.resolver, assign.left) {
7878 +
                            set target = findLinearBinding(env, sym);
7879 +
                        }
7880 +
                    }
7881 +
                    if target == nil {
7882 +
                        throw emitError(
7883 +
                            checker.resolver,
7884 +
                            assign.left,
7885 +
                            ErrorKind::LinearOverwrite,
7886 +
                        );
7887 +
                    }
7888 +
                }
7889 +
            }
7890 +
            try checkLinearNode(checker, env, assign.left, LinearUse::Place);
7891 +
            try checkLinearNode(checker, env, assign.right, LinearUse::Consume);
7892 +
            if let index = target {
7893 +
                if linearBindingAvailable(env, index) {
7894 +
                    throw emitError(
7895 +
                        checker.resolver,
7896 +
                        assign.left,
7897 +
                        ErrorKind::LinearOverwrite,
7898 +
                    );
7899 +
                }
7900 +
                set env.available |= (1 as u64) << (index as u64);
7901 +
            }
7902 +
        }
7903 +
        case ast::NodeValue::Call(call) => try checkLinearCall(checker, env, node, call),
7904 +
        case ast::NodeValue::AddressOf(addr) => {
7905 +
            try checkLinearNode(checker, env, addr.target, LinearUse::Borrow);
7906 +
        }
7907 +
        case ast::NodeValue::Deref(target) => {
7908 +
            if let resultTy = typeFor(checker.resolver, node) {
7909 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7910 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7911 +
                }
7912 +
            }
7913 +
            try checkLinearNode(checker, env, target, LinearUse::Observe);
7914 +
        }
7915 +
        case ast::NodeValue::FieldAccess(access) => {
7916 +
            if let resultTy = typeFor(checker.resolver, node) {
7917 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7918 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7919 +
                }
7920 +
            }
7921 +
            try checkLinearNode(checker, env, access.parent, LinearUse::Observe);
7922 +
        }
7923 +
        case ast::NodeValue::ScopeAccess(_) => {}
7924 +
        case ast::NodeValue::Subscript { container, index } => {
7925 +
            if let resultTy = typeFor(checker.resolver, node) {
7926 +
                if isLinear(resultTy) and usage == LinearUse::Consume {
7927 +
                    throw emitError(checker.resolver, node, ErrorKind::LinearPartialMove);
7928 +
                }
7929 +
            }
7930 +
            try checkLinearNode(checker, env, container, LinearUse::Observe);
7931 +
            try checkLinearNode(checker, env, index, LinearUse::Consume);
7932 +
        }
7933 +
        case ast::NodeValue::RecordLit(lit) => {
7934 +
            for fieldNode in lit.fields {
7935 +
                let case ast::NodeValue::RecordLitField(field) = fieldNode.value
7936 +
                    else panic "checkLinearNode: expected field";
7937 +
                try checkLinearNode(checker, env, field.value, LinearUse::Consume);
7938 +
            }
7939 +
        }
7940 +
        case ast::NodeValue::ArrayLit(items) => {
7941 +
            for item in items {
7942 +
                try checkLinearNode(checker, env, item, LinearUse::Consume);
7943 +
            }
7944 +
        }
7945 +
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
7946 +
            if let itemTy = typeFor(checker.resolver, repeat.item) {
7947 +
                if isLinear(itemTy) {
7948 +
                    throw emitError(
7949 +
                        checker.resolver,
7950 +
                        repeat.item,
7951 +
                        ErrorKind::LinearDiscard,
7952 +
                    );
7953 +
                }
7954 +
            }
7955 +
            try checkLinearNode(checker, env, repeat.item, LinearUse::Consume);
7956 +
            try checkLinearNode(checker, env, repeat.count, LinearUse::Consume);
7957 +
        }
7958 +
        case ast::NodeValue::BinOp(op) => {
7959 +
            try checkLinearNode(checker, env, op.left, LinearUse::Consume);
7960 +
            try checkLinearNode(checker, env, op.right, LinearUse::Consume);
7961 +
        }
7962 +
        case ast::NodeValue::UnOp(op) => {
7963 +
            try checkLinearNode(checker, env, op.value, LinearUse::Consume);
7964 +
        }
7965 +
        case ast::NodeValue::As(expr) => {
7966 +
            try checkLinearNode(checker, env, expr.value, LinearUse::Consume);
7967 +
        }
7968 +
        case ast::NodeValue::Range(range) => {
7969 +
            if let start = range.start {
7970 +
                try checkLinearNode(checker, env, start, LinearUse::Consume);
7971 +
            }
7972 +
            if let end = range.end {
7973 +
                try checkLinearNode(checker, env, end, LinearUse::Consume);
7974 +
            }
7975 +
        }
7976 +
        case ast::NodeValue::BuiltinCall { args, .. } => {
7977 +
            for arg in args {
7978 +
                try checkLinearNode(checker, env, arg, LinearUse::Consume);
7979 +
            }
7980 +
        }
7981 +
        case ast::NodeValue::If(conditional) => {
7982 +
            try checkLinearIf(checker, env, node, conditional);
7983 +
        }
7984 +
        case ast::NodeValue::CondExpr(conditional) => {
7985 +
            try checkLinearCondExpr(checker, env, node, conditional, usage);
7986 +
        }
7987 +
        case ast::NodeValue::IfLet(conditional) => {
7988 +
            try checkLinearIfLet(checker, env, node, conditional);
7989 +
        }
7990 +
        case ast::NodeValue::LetElse(binding) => {
7991 +
            if let subjectTy = typeFor(checker.resolver, binding.pattern.scrutinee);
7992 +
                isLinear(subjectTy)
7993 +
            {
7994 +
                throw emitError(
7995 +
                    checker.resolver,
7996 +
                    binding.pattern.scrutinee,
7997 +
                    ErrorKind::LinearPartialMove,
7998 +
                );
7999 +
            }
8000 +
            try checkLinearNode(
8001 +
                checker,
8002 +
                env,
8003 +
                binding.pattern.scrutinee,
8004 +
                LinearUse::Consume,
8005 +
            );
8006 +
            let base = *env;
8007 +
            let mut guardedEnv = base;
8008 +
            if let guard = binding.pattern.guard {
8009 +
                try checkLinearNode(checker, &mut guardedEnv, guard, LinearUse::Consume);
8010 +
            }
8011 +
            let mut successEnv = guardedEnv;
8012 +
            try addLinearPatternBindings(
8013 +
                checker,
8014 +
                &mut successEnv,
8015 +
                binding.pattern.pattern,
8016 +
            );
8017 +
            let mut fallbackEnv = base;
8018 +
            try checkLinearNode(
8019 +
                checker,
8020 +
                &mut fallbackEnv,
8021 +
                binding.elseBranch,
8022 +
                LinearUse::Consume,
8023 +
            );
8024 +
            if binding.pattern.guard <> nil {
8025 +
                let mut guardFallbackEnv = guardedEnv;
8026 +
                try checkLinearNode(
8027 +
                    checker,
8028 +
                    &mut guardFallbackEnv,
8029 +
                    binding.elseBranch,
8030 +
                    LinearUse::Consume,
8031 +
                );
8032 +
                try joinLinearBranches(
8033 +
                    checker,
8034 +
                    &mut fallbackEnv,
8035 +
                    fallbackEnv,
8036 +
                    guardFallbackEnv,
8037 +
                    binding.elseBranch,
8038 +
                );
8039 +
            }
8040 +
            if let case ast::PatternKind::Binding = binding.pattern.kind {
8041 +
                try addLinearPatternBindings(
8042 +
                    checker,
8043 +
                    &mut fallbackEnv,
8044 +
                    binding.pattern.pattern,
8045 +
                );
8046 +
            }
8047 +
            try joinLinearBranches(checker, env, successEnv, fallbackEnv, node);
8048 +
        }
8049 +
        case ast::NodeValue::Match(matchExpr) => {
8050 +
            try checkLinearMatch(checker, env, node, matchExpr);
8051 +
        }
8052 +
        case ast::NodeValue::Try(tryExpr) => {
8053 +
            try checkLinearNode(checker, env, tryExpr.expr, usage);
8054 +
            let success = *env;
8055 +
            for catchNode in tryExpr.catches {
8056 +
                let case ast::NodeValue::CatchClause(catchClause) = catchNode.value
8057 +
                    else panic "checkLinearNode: expected catch";
8058 +
                let mut branch = success;
8059 +
                let start = branch.len;
8060 +
                if let binding = catchClause.binding {
8061 +
                    try addLinearBinding(checker, &mut branch, binding);
8062 +
                }
8063 +
                try checkLinearNode(checker, &mut branch, catchClause.body, usage);
8064 +
                try finishLinearScope(checker, &mut branch, start);
8065 +
                try joinLinearBranches(checker, env, *env, branch, node);
8066 +
            }
8067 +
        }
8068 +
        case ast::NodeValue::While(whileStmt) => {
8069 +
            enterLinearLoop(checker, env);
8070 +
            try checkLinearNode(checker, env, whileStmt.condition, LinearUse::Consume);
8071 +
            let conditionExit = *env;
8072 +
            setLinearLoopNaturalExit(checker, &conditionExit);
8073 +
            let mut bodyEnv = conditionExit;
8074 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8075 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8076 +
            exitLinearLoop(checker);
8077 +
            set *env = conditionExit;
8078 +
            if let elseBranch = whileStmt.elseBranch {
8079 +
                let mut elseEnv = conditionExit;
8080 +
                try checkLinearNode(
8081 +
                    checker,
8082 +
                    &mut elseEnv,
8083 +
                    elseBranch,
8084 +
                    LinearUse::Discard,
8085 +
                );
8086 +
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8087 +
            }
8088 +
        }
8089 +
        case ast::NodeValue::WhileLet(whileStmt) => {
8090 +
            if let subjectTy = typeFor(checker.resolver, whileStmt.pattern.scrutinee);
8091 +
                isLinear(subjectTy)
8092 +
            {
8093 +
                throw emitError(
8094 +
                    checker.resolver,
8095 +
                    whileStmt.pattern.scrutinee,
8096 +
                    ErrorKind::LinearPartialMove,
8097 +
                );
8098 +
            }
8099 +
            let base = *env;
8100 +
            enterLinearLoop(checker, env);
8101 +
            let mut bodyEnv = base;
8102 +
            try checkLinearNode(
8103 +
                checker,
8104 +
                &mut bodyEnv,
8105 +
                whileStmt.pattern.scrutinee,
8106 +
                LinearUse::Consume,
8107 +
            );
8108 +
            let mut conditionExit = bodyEnv;
8109 +
            let start = bodyEnv.len;
8110 +
            try addLinearPatternBindings(checker, &mut bodyEnv, whileStmt.pattern.pattern);
8111 +
            if let guard = whileStmt.pattern.guard {
8112 +
                try checkLinearNode(checker, &mut bodyEnv, guard, LinearUse::Consume);
8113 +
                let mut guardExit = bodyEnv;
8114 +
                try finishLinearScope(checker, &mut guardExit, start);
8115 +
                try joinLinearBranches(
8116 +
                    checker,
8117 +
                    &mut conditionExit,
8118 +
                    conditionExit,
8119 +
                    guardExit,
8120 +
                    guard,
8121 +
                );
8122 +
            }
8123 +
            setLinearLoopNaturalExit(checker, &conditionExit);
8124 +
            try checkLinearNode(checker, &mut bodyEnv, whileStmt.body, LinearUse::Discard);
8125 +
            try finishLinearScope(checker, &mut bodyEnv, start);
8126 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, whileStmt.body);
8127 +
            exitLinearLoop(checker);
8128 +
            set *env = conditionExit;
8129 +
            if let elseBranch = whileStmt.elseBranch {
8130 +
                let mut elseEnv = conditionExit;
8131 +
                try checkLinearNode(
8132 +
                    checker,
8133 +
                    &mut elseEnv,
8134 +
                    elseBranch,
8135 +
                    LinearUse::Discard,
8136 +
                );
8137 +
                try joinLinearBranches(checker, env, conditionExit, elseEnv, node);
8138 +
            }
8139 +
        }
8140 +
        case ast::NodeValue::For(forStmt) => {
8141 +
            if let iterableTy = typeFor(checker.resolver, forStmt.iterable) {
8142 +
                if isLinear(iterableTy) {
8143 +
                    throw emitError(
8144 +
                        checker.resolver,
8145 +
                        forStmt.iterable,
8146 +
                        ErrorKind::LinearPartialMove,
8147 +
                    );
8148 +
                }
8149 +
            }
8150 +
            try checkLinearNode(checker, env, forStmt.iterable, LinearUse::Consume);
8151 +
            let base = *env;
8152 +
            enterLinearLoop(checker, env);
8153 +
            setLinearLoopNaturalExit(checker, &base);
8154 +
            let mut bodyEnv = base;
8155 +
            let start = bodyEnv.len;
8156 +
            try addLinearBinding(checker, &mut bodyEnv, forStmt.binding);
8157 +
            if let index = forStmt.index {
8158 +
                try addLinearBinding(checker, &mut bodyEnv, index);
8159 +
            }
8160 +
            try checkLinearNode(checker, &mut bodyEnv, forStmt.body, LinearUse::Discard);
8161 +
            try finishLinearScope(checker, &mut bodyEnv, start);
8162 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, forStmt.body);
8163 +
            exitLinearLoop(checker);
8164 +
            set *env = base;
8165 +
            if let elseBranch = forStmt.elseBranch {
8166 +
                let mut elseEnv = base;
8167 +
                try checkLinearNode(
8168 +
                    checker,
8169 +
                    &mut elseEnv,
8170 +
                    elseBranch,
8171 +
                    LinearUse::Discard,
8172 +
                );
8173 +
                try joinLinearBranches(checker, env, base, elseEnv, node);
8174 +
            }
8175 +
        }
8176 +
        case ast::NodeValue::Loop { body } => {
8177 +
            let base = *env;
8178 +
            enterLinearLoop(checker, env);
8179 +
            let mut bodyEnv = base;
8180 +
            try checkLinearNode(checker, &mut bodyEnv, body, LinearUse::Discard);
8181 +
            try checkLinearLoopBackEdge(checker, &bodyEnv, body);
8182 +
            let depth = checker.loopDepth - 1;
8183 +
            let breakSeen = checker.loopBreakSeen[depth];
8184 +
            let exitAvailable = checker.loopExitAvailable[depth];
8185 +
            exitLinearLoop(checker);
8186 +
            set *env = base;
8187 +
            if breakSeen {
8188 +
                set env.available = exitAvailable;
8189 +
            } else {
8190 +
                set env.terminated = true;
8191 +
            }
8192 +
        }
8193 +
        case ast::NodeValue::Break => {
8194 +
            assert checker.loopDepth > 0, "linear loop control outside loop";
8195 +
            let start = checker.loopMarks[checker.loopDepth - 1];
8196 +
            try finishLinearScope(checker, env, start);
8197 +
            try checkLinearLoopBreak(checker, env, node);
8198 +
            set env.terminated = true;
8199 +
        }
8200 +
        case ast::NodeValue::Continue => {
8201 +
            assert checker.loopDepth > 0, "linear loop control outside loop";
8202 +
            let start = checker.loopMarks[checker.loopDepth - 1];
8203 +
            try finishLinearScope(checker, env, start);
8204 +
            try checkLinearLoopBackEdge(checker, env, node);
8205 +
            set env.terminated = true;
8206 +
        }
8207 +
        case ast::NodeValue::Return { value } => {
8208 +
            if let expr = value {
8209 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8210 +
            }
8211 +
            try finishLinearExit(checker, env);
8212 +
        }
8213 +
        case ast::NodeValue::Throw { expr } => {
8214 +
            try checkLinearNode(checker, env, expr, LinearUse::Consume);
8215 +
            try finishLinearExit(checker, env);
8216 +
        }
8217 +
        case ast::NodeValue::Panic { message } => {
8218 +
            if let expr = message {
8219 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8220 +
            }
8221 +
            set env.terminated = true;
8222 +
        }
8223 +
        case ast::NodeValue::Assert { condition, message } => {
8224 +
            try checkLinearNode(checker, env, condition, LinearUse::Consume);
8225 +
            if let expr = message {
8226 +
                try checkLinearNode(checker, env, expr, LinearUse::Consume);
8227 +
            }
8228 +
        }
8229 +
        else => {}
8230 +
    }
8231 +
}
8232 +
8233 +
/// Check exact-use ownership for one resolved function.
8234 +
fn checkLinearFn(
8235 +
    self: *mut Resolver,
8236 +
    receiver: ?*ast::Node,
8237 +
    params: *mut [*ast::Node],
8238 +
    body: *ast::Node,
8239 +
) throws (ResolveError) {
8240 +
    let mut checker = LinearChecker {
8241 +
        resolver: self,
8242 +
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
8243 +
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8244 +
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8245 +
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
8246 +
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
8247 +
        loopDepth: 0,
8248 +
    };
8249 +
    let mut env = LinearEnv {
8250 +
        symbols: [nil; MAX_LINEAR_BINDINGS],
8251 +
        available: 0,
8252 +
        len: 0,
8253 +
        terminated: false,
8254 +
    };
8255 +
    if let receiverNode = receiver {
8256 +
        try addLinearBinding(&mut checker, &mut env, receiverNode);
8257 +
    }
8258 +
    for paramNode in params {
8259 +
        let case ast::NodeValue::FnParam(_) = paramNode.value
8260 +
            else panic "checkLinearFn: expected parameter";
8261 +
        try addLinearBinding(&mut checker, &mut env, paramNode);
8262 +
    }
8263 +
    try checkLinearNode(&mut checker, &mut env, body, LinearUse::Discard);
8264 +
    try finishLinearScope(&mut checker, &mut env, 0);
8265 +
}
8266 +
6903 8267
/// Analyze module definitions. This pass analyzes function bodies, recursing into sub-modules.
6904 8268
fn resolveModuleDefs(self: *mut Resolver, block: *ast::Block) throws (ResolveError) {
6905 8269
    for stmt in block.statements {
6906 8270
        try visitDef(self, stmt);
6907 8271
    }
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 +594 -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 +
/// Explicit `Linear` markers enable exact-use checking.
5168 +
@test fn testLinearValueConsumedOnce() throws (testing::TestError) {
5169 +
    let program = "union Token: Linear { Value(u32) } fn consume(token: Token) { match token { case Token::Value(_) => {} } } fn run(token: Token) { consume(token); }";
5170 +
    try expectAnalyzeOk(program);
5171 +
}
5172 +
5173 +
/// A linear binding must be consumed before its scope exits.
5174 +
@test fn testLinearValueNotConsumed() throws (testing::TestError) {
5175 +
    let mut a = testResolver();
5176 +
    let program = "record Token: Linear { value: u32 } fn run(token: Token) {}";
5177 +
    let result = try resolveProgramStr(&mut a, program);
5178 +
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("token"));
5179 +
}
5180 +
5181 +
/// A second by-value use of a linear binding is rejected.
5182 +
@test fn testLinearValueConsumedTwice() throws (testing::TestError) {
5183 +
    let mut a = testResolver();
5184 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { consume(token); consume(token); }";
5185 +
    let result = try resolveProgramStr(&mut a, program);
5186 +
    try expectErrorKind(&result, super::ErrorKind::LinearUseAfterConsume("token"));
5187 +
}
5188 +
5189 +
/// Both live branches must leave an outer linear binding in the same state.
5190 +
@test fn testLinearBranchMismatch() throws (testing::TestError) {
5191 +
    let mut a = testResolver();
5192 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token, flag: bool) { if flag { consume(token); } }";
5193 +
    let result = try resolveProgramStr(&mut a, program);
5194 +
    try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5195 +
}
5196 +
5197 +
/// A loop cannot consume a binding created outside the repeated body.
5198 +
@test fn testLinearLoopConsume() throws (testing::TestError) {
5199 +
    let mut a = testResolver();
5200 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token, flag: bool) { while flag { consume(token); } consume(token); }";
5201 +
    let result = try resolveProgramStr(&mut a, program);
5202 +
    try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5203 +
}
5204 +
5205 +
/// Effects from the condition remain on the condition-false loop exit.
5206 +
@test fn testLinearWhileConditionConsumptionPreserved() throws (testing::TestError) {
5207 +
    let mut a = testResolver();
5208 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn take(token: Token) -> bool { consume(token); return false; } fn run(token: Token) { while take(token) { return; } consume(token); }";
5209 +
    let result = try resolveProgramStr(&mut a, program);
5210 +
    try expectErrorKind(&result, super::ErrorKind::LinearUseAfterConsume("token"));
5211 +
}
5212 +
5213 +
/// A break exit agrees with ownership effects already applied by the condition.
5214 +
@test fn testLinearWhileBreakUsesConditionExit() throws (testing::TestError) {
5215 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn take(token: Token) -> bool { consume(token); return true; } fn run(token: Token) { while take(token) { break; } }";
5216 +
    try expectAnalyzeOk(program);
5217 +
}
5218 +
5219 +
/// Guard-failure effects must agree with the pattern-failure loop exit.
5220 +
@test fn testLinearWhileLetGuardExitMismatch() throws (testing::TestError) {
5221 +
    let mut a = testResolver();
5222 +
    let program = "union Token: Linear { Value } union Opt { Some(u32), None } fn consume(token: Token) { match token { case Token::Value => {} } } fn take(token: Token) -> bool { consume(token); return false; } fn run(value: Opt, token: Token) { while let case Opt::Some(_) = value; take(token) { return; } consume(token); }";
5223 +
    let result = try resolveProgramStr(&mut a, program);
5224 +
    try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5225 +
}
5226 +
5227 +
/// A linear field cannot be moved out independently of its container.
5228 +
@test fn testLinearPartialMove() throws (testing::TestError) {
5229 +
    let mut a = testResolver();
5230 +
    let program = "union Token: Linear { Value } record Wrapper { token: Token } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(wrapper: Wrapper) { consume(wrapper.token); }";
5231 +
    let result = try resolveProgramStr(&mut a, program);
5232 +
    try expectErrorKind(&result, super::ErrorKind::LinearPartialMove);
5233 +
}
5234 +
5235 +
/// Assignment cannot discard the previous value of a linear place.
5236 +
@test fn testLinearOverwrite() throws (testing::TestError) {
5237 +
    let mut a = testResolver();
5238 +
    let program = "union Token: Linear { Value } fn run() { let mut token = Token::Value; set token = Token::Value; }";
5239 +
    let result = try resolveProgramStr(&mut a, program);
5240 +
    try expectErrorKind(&result, super::ErrorKind::LinearOverwrite);
5241 +
}
5242 +
5243 +
/// A consumed linear binding may be initialized with a new owning value.
5244 +
@test fn testLinearReinitializeConsumedBinding() throws (testing::TestError) {
5245 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run() { let mut token = Token::Value; consume(token); set token = Token::Value; consume(token); }";
5246 +
    try expectAnalyzeOk(program);
5247 +
}
5248 +
5249 +
/// Assignment may consume and replace the same live linear binding.
5250 +
@test fn testLinearTransformAssignment() throws (testing::TestError) {
5251 +
    let program = "union Token: Linear { Value } fn transform(token: Token) -> Token { return token; } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { let mut current = token; set current = transform(current); consume(current); }";
5252 +
    try expectAnalyzeOk(program);
5253 +
}
5254 +
5255 +
/// A loop back edge cannot change an outer binding's availability.
5256 +
@test fn testLinearLoopReinitializeMismatch() throws (testing::TestError) {
5257 +
    let mut a = testResolver();
5258 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { let mut current = token; consume(current); loop { set current = Token::Value; } }";
5259 +
    let result = try resolveProgramStr(&mut a, program);
5260 +
    try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("current"));
5261 +
}
5262 +
5263 +
/// A break propagates its ownership state to the loop exit.
5264 +
@test fn testLinearBreakReinitializeMismatch() throws (testing::TestError) {
5265 +
    let mut a = testResolver();
5266 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { let mut current = token; consume(current); loop { set current = Token::Value; break; } }";
5267 +
    let result = try resolveProgramStr(&mut a, program);
5268 +
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("current"));
5269 +
}
5270 +
5271 +
/// `undefined` cannot manufacture a linear value.
5272 +
@test fn testLinearUndefined() throws (testing::TestError) {
5273 +
    let mut a = testResolver();
5274 +
    let program = "record Token: Linear { value: u32 } fn run() { let token: Token = undefined; }";
5275 +
    let result = try resolveProgramStr(&mut a, program);
5276 +
    try expectErrorKind(&result, super::ErrorKind::LinearUndefined);
5277 +
}
5278 +
5279 +
/// Owning pointers are structurally linear regardless of pointee type.
5280 +
@test fn testOwningPointerIsLinear() throws (testing::TestError) {
5281 +
    let mut a = testResolver();
5282 +
    let program = "record Marker: Linear {} fn run(pointer: *u32) {}";
5283 +
    let result = try resolveProgramStr(&mut a, program);
5284 +
    try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("pointer"));
5285 +
}
5286 +
5287 +
/// A call-scoped reference may borrow a linear value without consuming it.
5288 +
@test fn testLinearRefBorrow() throws (testing::TestError) {
5289 +
    let program = "union Token: Linear { Value } fn inspect(token: &Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { inspect(&token); consume(token); }";
5290 +
    try expectAnalyzeOk(program);
5291 +
}
5292 +
5293 +
/// References cannot escape through return types.
5294 +
@test fn testRefReturnRejected() throws (testing::TestError) {
5295 +
    let mut a = testResolver();
5296 +
    let program = "record Marker: Linear {} fn bad(value: &u32) -> &u32 { return value; }";
5297 +
    let result = try resolveProgramStr(&mut a, program);
5298 +
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5299 +
}
5300 +
5301 +
/// Address expressions cannot be captured in local bindings.
5302 +
@test fn testRefBindingRejected() throws (testing::TestError) {
5303 +
    let mut a = testResolver();
5304 +
    let program = "record Marker: Linear {} fn bad() { let value: u32 = 1; let saved = &value; }";
5305 +
    let result = try resolveProgramStr(&mut a, program);
5306 +
    try expectErrorKind(&result, super::ErrorKind::RefBinding);
5307 +
}
5308 +
5309 +
/// An exclusive loan cannot overlap another loan of the same root.
5310 +
@test fn testLinearBorrowConflict() throws (testing::TestError) {
5311 +
    let mut a = testResolver();
5312 +
    let program = "union Token: Linear { Value } fn borrow(first: &mut Token, second: &Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run() { let mut token = Token::Value; borrow(&mut token, &token); consume(token); }";
5313 +
    let result = try resolveProgramStr(&mut a, program);
5314 +
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token"));
5315 +
}
5316 +
5317 +
/// Mutable slice references rooted at the same local conflict.
5318 +
@test fn testLinearSliceRefBorrowConflict() throws (testing::TestError) {
5319 +
    let mut a = testResolver();
5320 +
    let program = "record Marker: Linear {} fn borrow(first: &mut [u32], second: &[u32]) {} fn run() { let mut values: [u32; 2] = [1, 2]; borrow(&mut values[..], &values[..]); }";
5321 +
    let result = try resolveProgramStr(&mut a, program);
5322 +
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("values"));
5323 +
}
5324 +
5325 +
/// Linear checking preserves value-producing `let-else` fallbacks.
5326 +
@test fn testLinearLetElseFallbackValue() throws (testing::TestError) {
5327 +
    let program = "record Marker: Linear {} fn run(value: ?u32) { let item = value else 1; item; }";
5328 +
    try expectAnalyzeOk(program);
5329 +
}
5330 +
5331 +
/// Case-pattern fallbacks must terminate instead of synthesizing bindings.
5332 +
@test fn testCaseLetElseFallbackMustTerminate() throws (testing::TestError) {
5333 +
    let mut a = testResolver();
5334 +
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else value; item; }";
5335 +
    let result = try resolveProgramStr(&mut a, program);
5336 +
    try expectErrorKind(&result, super::ErrorKind::LinearLetElseMustTerminate);
5337 +
}
5338 +
5339 +
/// Case bindings are unavailable on the pattern-failure path.
5340 +
@test fn testCaseLetElseFallbackCannotUseBinding() throws (testing::TestError) {
5341 +
    let mut a = testResolver();
5342 +
    let program = "union Value { Item(u32) } fn run(value: Value) { let case Value::Item(item) = value else item; }";
5343 +
    let result = try resolveProgramStr(&mut a, program);
5344 +
    try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("item"));
5345 +
}
5346 +
5347 +
/// `let-else` fallback effects must agree with the success path.
5348 +
@test fn testLinearLetElseFallbackBranchMismatch() throws (testing::TestError) {
5349 +
    let mut a = testResolver();
5350 +
    let program = "union Token: Linear { Value } fn consumeValue(token: Token) -> u32 { match token { case Token::Value => {} } return 1; } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(value: ?u32, token: Token) { let item = value else consumeValue(token); consume(token); item; }";
5351 +
    let result = try resolveProgramStr(&mut a, program);
5352 +
    try expectErrorKind(&result, super::ErrorKind::LinearBranchMismatch("token"));
5353 +
}
5354 +
5355 +
/// Guard effects remain visible on the successful continuation.
5356 +
@test fn testLinearLetElseGuardFailureMismatch() throws (testing::TestError) {
5357 +
    let mut a = testResolver();
5358 +
    let program = "union Token: Linear { Value } union Opt { Some(u32), None } fn consume(token: Token) { match token { case Token::Value => {} } } fn take(token: Token) -> bool { consume(token); return false; } fn run(value: Opt, token: Token) { let case Opt::Some(_) = value if take(token) else panic; consume(token); }";
5359 +
    let result = try resolveProgramStr(&mut a, program);
5360 +
    try expectErrorKind(&result, super::ErrorKind::LinearUseAfterConsume("token"));
5361 +
}
5362 +
5363 +
/// Differing guard and pattern failure states are valid when both terminate.
5364 +
@test fn testLinearLetElseGuardTerminatingFallback() throws (testing::TestError) {
5365 +
    let program = "union Token: Linear { Value } union Opt { Some(u32), None } fn consume(token: Token) { match token { case Token::Value => {} } } fn take(token: Token) -> bool { consume(token); return false; } fn run(value: Opt, token: Token) { let case Opt::Some(_) = value if take(token) else panic; }";
5366 +
    try expectAnalyzeOk(program);
5367 +
}
5368 +
5369 +
/// Mutable trait-object references rooted at the same local conflict.
5370 +
@test fn testLinearTraitObjectRefBorrowConflict() throws (testing::TestError) {
5371 +
    let mut a = testResolver();
5372 +
    let program = "record Marker: Linear {} record Value { number: u32 } trait Read { fn (&Read) get() -> u32; } instance Read for Value { fn (value: &Value) get() -> u32 { return value.number; } } fn borrow(first: &mut opaque Read, second: &opaque Read) {} fn run(value: &mut Value) { borrow(value, value); }";
5373 +
    let result = try resolveProgramStr(&mut a, program);
5374 +
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("value"));
5375 +
}
5376 +
5377 +
/// Unsafe pointer dereference requires an unsafe declaration.
5378 +
@test fn testUnsafePointerOperationRejected() throws (testing::TestError) {
5379 +
    let mut a = testResolver();
5380 +
    let program = "record Marker: Linear {} fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5381 +
    let result = try resolveProgramStr(&mut a, program);
5382 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5383 +
}
5384 +
5385 +
/// Unsafe pointers remain freely copyable inside an unsafe declaration.
5386 +
@test fn testUnsafePointerOperationAllowed() throws (testing::TestError) {
5387 +
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; }";
5388 +
    try expectAnalyzeOk(program);
5389 +
}
5390 +
5391 +
/// Safe code cannot call a function that accepts unsafe operations.
5392 +
@test fn testUnsafeFunctionCallRejected() throws (testing::TestError) {
5393 +
    let mut a = testResolver();
5394 +
    let program = "record Marker: Linear {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } fn run(pointer: *unsafe u32) -> u32 { return load(pointer); }";
5395 +
    let result = try resolveProgramStr(&mut a, program);
5396 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5397 +
}
5398 +
5399 +
/// Unsafe function values retain their call-site safety requirement.
5400 +
@test fn testUnsafeFunctionAliasCallRejected() throws (testing::TestError) {
5401 +
    let mut a = testResolver();
5402 +
    let program = "unsafe fn dangerous() -> u32 { return 42; } fn run() -> u32 { let alias = dangerous; return alias(); }";
5403 +
    let result = try resolveProgramStr(&mut a, program);
5404 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5405 +
}
5406 +
5407 +
/// Matching branch consumption is accepted on every live path.
5408 +
@test fn testLinearBranchConsumption() throws (testing::TestError) {
5409 +
    let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token, flag: bool) { if flag { consume(token); } else { consume(token); } }";
5410 +
    try expectAnalyzeOk(program);
5411 +
}
5412 +
5413 +
/// Arrays and optionals inherit linearity from their elements.
5414 +
@test fn testStructuralLinearContainers() throws (testing::TestError) {
5415 +
    {
5416 +
        let mut a = testResolver();
5417 +
        let program = "union Token: Linear { Value } fn run(values: [Token; 1]) {}";
5418 +
        let result = try resolveProgramStr(&mut a, program);
5419 +
        try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("values"));
5420 +
    } {
5421 +
        let mut a = testResolver();
5422 +
        let program = "union Token: Linear { Value } fn run(value: ?Token) {}";
5423 +
        let result = try resolveProgramStr(&mut a, program);
5424 +
        try expectErrorKind(&result, super::ErrorKind::LinearNotConsumed("value"));
5425 +
    }
5426 +
}
5427 +
5428 +
/// References cannot be embedded in aggregate fields.
5429 +
@test fn testRefFieldRejected() throws (testing::TestError) {
5430 +
    let mut a = testResolver();
5431 +
    let program = "record Marker: Linear {} record Bad { value: &u32 }";
5432 +
    let result = try resolveProgramStr(&mut a, program);
5433 +
    try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5434 +
}
5435 +
5436 +
/// Trait methods may use reference receivers.
5437 +
@test fn testTraitRefReceiver() throws (testing::TestError) {
5438 +
    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); }";
5439 +
    try expectAnalyzeOk(program);
5440 +
}
5441 +
5442 +
/// Trait implementations must preserve the receiver pointer class.
5443 +
@test fn testTraitReceiverClassMismatch() throws (testing::TestError) {
5444 +
    let mut a = testResolver();
5445 +
    let program = "record Value { number: i32 } trait Read { fn (&Read) get() -> i32; } instance Read for Value { fn (value: *Value) get() -> i32 { return value.number; } }";
5446 +
    let result = try resolveProgramStr(&mut a, program);
5447 +
    try expectErrorKind(&result, super::ErrorKind::TraitReceiverMismatch);
5448 +
}
5449 +
5450 +
/// Linear temporaries cannot be discarded or duplicated by array repetition.
5451 +
@test fn testLinearDiscardRejected() throws (testing::TestError) {
5452 +
    {
5453 +
        let mut a = testResolver();
5454 +
        let program = "union Token: Linear { Value } fn run() { Token::Value; }";
5455 +
        let result = try resolveProgramStr(&mut a, program);
5456 +
        try expectErrorKind(&result, super::ErrorKind::LinearDiscard);
5457 +
    } {
5458 +
        let mut a = testResolver();
5459 +
        let program = "union Token: Linear { Value } fn run() { let values = [Token::Value; 2]; }";
5460 +
        let result = try resolveProgramStr(&mut a, program);
5461 +
        try expectErrorKind(&result, super::ErrorKind::LinearDiscard);
5462 +
    }
5463 +
}
5464 +
5465 +
/// Partial conditional and repeated destructuring cannot consume a linear scrutinee.
5466 +
@test fn testLinearPartialControlFlowRejected() throws (testing::TestError) {
5467 +
    {
5468 +
        let mut a = testResolver();
5469 +
        let program = "union Token: Linear { Value } fn run(token: Token) { if let case Token::Value = token {} }";
5470 +
        let result = try resolveProgramStr(&mut a, program);
5471 +
        try expectErrorKind(&result, super::ErrorKind::LinearPartialMove);
5472 +
    } {
5473 +
        let mut a = testResolver();
5474 +
        let program = "union Token: Linear { Value } fn run(token: Token) { while let case Token::Value = token {} }";
5475 +
        let result = try resolveProgramStr(&mut a, program);
5476 +
        try expectErrorKind(&result, super::ErrorKind::LinearPartialMove);
5477 +
    } {
5478 +
        let mut a = testResolver();
5479 +
        let program = "union Token: Linear { Value } fn run(tokens: [Token; 1]) { for token in tokens { match token { case Token::Value => {} } } }";
5480 +
        let result = try resolveProgramStr(&mut a, program);
5481 +
        try expectErrorKind(&result, super::ErrorKind::LinearPartialMove);
5482 +
    }
5483 +
}
5484 +
5485 +
/// The compiler-known marker cannot be derived more than once.
5486 +
@test fn testDuplicateLinearMarkerRejected() throws (testing::TestError) {
5487 +
    let mut a = testResolver();
5488 +
    let program = "record Token: Linear + Linear { value: u32 }";
5489 +
    let result = try resolveProgramStr(&mut a, program);
5490 +
    try expectErrorKind(&result, super::ErrorKind::DuplicateBinding("Linear"));
5491 +
}
5492 +
5493 +
/// References are rejected from every nested or storable type position.
5494 +
@test fn testNestedRefPositionsRejected() throws (testing::TestError) {
5495 +
    {
5496 +
        let mut a = testResolver();
5497 +
        let program = "record Marker: Linear {} union Bad { Value(&u32) }";
5498 +
        let result = try resolveProgramStr(&mut a, program);
5499 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5500 +
    } {
5501 +
        let mut a = testResolver();
5502 +
        let program = "record Marker: Linear {} fn bad(value: ?&u32) {}";
5503 +
        let result = try resolveProgramStr(&mut a, program);
5504 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5505 +
    } {
5506 +
        let mut a = testResolver();
5507 +
        let program = "record Marker: Linear {} fn bad(value: [&u32; 1]) {}";
5508 +
        let result = try resolveProgramStr(&mut a, program);
5509 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5510 +
    } {
5511 +
        let mut a = testResolver();
5512 +
        let program = "record Marker: Linear {} fn bad(value: *&u32) {}";
5513 +
        let result = try resolveProgramStr(&mut a, program);
5514 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5515 +
    } {
5516 +
        let mut a = testResolver();
5517 +
        let program = "record Marker: Linear {} static BAD: &u32 = undefined;";
5518 +
        let result = try resolveProgramStr(&mut a, program);
5519 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5520 +
    } {
5521 +
        let mut a = testResolver();
5522 +
        let program = "record Marker: Linear {} fn bad(callback: fn() -> &u32) {}";
5523 +
        let result = try resolveProgramStr(&mut a, program);
5524 +
        try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition);
5525 +
    }
5526 +
}
5527 +
5528 +
/// Function pointer parameter references remain call-scoped and valid.
5529 +
@test fn testFunctionPointerRefParameterAllowed() throws (testing::TestError) {
5530 +
    let program = "record Marker: Linear {} fn invoke(callback: fn(&u32), value: &u32) { callback(value); }";
5531 +
    try expectAnalyzeOk(program);
5532 +
}
5533 +
5534 +
/// Shared loans may overlap, while exclusive and consuming uses may not.
5535 +
@test fn testBorrowLoanCombinations() throws (testing::TestError) {
5536 +
    {
5537 +
        let program = "union Token: Linear { Value } fn inspect(first: &Token, second: &Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run(token: Token) { inspect(&token, &token); consume(token); }";
5538 +
        try expectAnalyzeOk(program);
5539 +
    } {
5540 +
        let mut a = testResolver();
5541 +
        let program = "union Token: Linear { Value } fn inspect(first: &mut Token, second: &mut Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run() { let mut token = Token::Value; inspect(&mut token, &mut token); consume(token); }";
5542 +
        let result = try resolveProgramStr(&mut a, program);
5543 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token"));
5544 +
    } {
5545 +
        let mut a = testResolver();
5546 +
        let program = "union Token: Linear { Value } fn consume(token: Token) { match token { case Token::Value => {} } } fn inspect(first: &Token, second: Token) { consume(second); } fn run(token: Token) { inspect(&token, token); }";
5547 +
        let result = try resolveProgramStr(&mut a, program);
5548 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token"));
5549 +
    } {
5550 +
        let program = "union Token: Linear { Value } fn inspect(first: &mut Token, second: &mut Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run() { let mut first = Token::Value; let mut second = Token::Value; inspect(&mut first, &mut second); consume(first); consume(second); }";
5551 +
        try expectAnalyzeOk(program);
5552 +
    }
5553 +
}
5554 +
5555 +
/// Implicit method receivers participate in ownership and loan accounting.
5556 +
@test fn testLinearMethodReceiverAccounting() throws (testing::TestError) {
5557 +
    {
5558 +
        let program = "union Token: Linear { Value } fn (token: *Token) pass() -> *Token { return token; } fn run(token: Token) -> *Token { return token.pass(); }";
5559 +
        try expectAnalyzeOk(program);
5560 +
    } {
5561 +
        let mut a = testResolver();
5562 +
        let program = "union Token: Linear { Value } fn (token: &mut Token) inspect(other: &Token) {} fn consume(token: Token) { match token { case Token::Value => {} } } fn run() { let mut token = Token::Value; token.inspect(&token); consume(token); }";
5563 +
        let result = try resolveProgramStr(&mut a, program);
5564 +
        try expectErrorKind(&result, super::ErrorKind::BorrowConflict("token"));
5565 +
    }
5566 +
}
5567 +
5568 +
/// Pointer and slice casts cannot change reference ownership.
5569 +
@test fn testRefCastClassPreserved() throws (testing::TestError) {
5570 +
    {
5571 +
        let mut a = testResolver();
5572 +
        let program = "record Marker: Linear {} fn cast(value: &u32) { value as *u32; }";
5573 +
        let result = try resolveProgramStr(&mut a, program);
5574 +
        let err = try expectError(&result);
5575 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5576 +
            else throw testing::TestError::Failed;
5577 +
    } {
5578 +
        let mut a = testResolver();
5579 +
        let program = "record Marker: Linear {} fn cast(values: &[u32]) { values as *[u32]; }";
5580 +
        let result = try resolveProgramStr(&mut a, program);
5581 +
        let err = try expectError(&result);
5582 +
        let case super::ErrorKind::InvalidAsCast(_) = err.kind
5583 +
            else throw testing::TestError::Failed;
5584 +
    }
5585 +
}
5586 +
5587 +
/// Every operation that interprets an unsafe address requires an unsafe declaration.
5588 +
@test fn testUnsafePointerOperationsRejected() throws (testing::TestError) {
5589 +
    {
5590 +
        let mut a = testResolver();
5591 +
        let program = "record Marker: Linear {} fn cast(pointer: *unsafe u32) -> u64 { return pointer as u64; }";
5592 +
        let result = try resolveProgramStr(&mut a, program);
5593 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5594 +
    } {
5595 +
        let mut a = testResolver();
5596 +
        let program = "record Marker: Linear {} fn compare(pointer: *unsafe u32) -> bool { return pointer == pointer; }";
5597 +
        let result = try resolveProgramStr(&mut a, program);
5598 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5599 +
    } {
5600 +
        let mut a = testResolver();
5601 +
        let program = "record Marker: Linear {} fn offset(pointer: *unsafe u32) -> *unsafe u32 { return pointer + 1; }";
5602 +
        let result = try resolveProgramStr(&mut a, program);
5603 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5604 +
    } {
5605 +
        let mut a = testResolver();
5606 +
        let program = "record Marker: Linear {} fn index(values: *unsafe [u32]) -> u32 { return values[0]; }";
5607 +
        let result = try resolveProgramStr(&mut a, program);
5608 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5609 +
    } {
5610 +
        let mut a = testResolver();
5611 +
        let program = "record Marker: Linear {} record Cell { value: u32 } fn field(cell: *unsafe Cell) -> u32 { return cell.value; }";
5612 +
        let result = try resolveProgramStr(&mut a, program);
5613 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5614 +
    } {
5615 +
        let mut a = testResolver();
5616 +
        let program = "record Marker: Linear {} fn store(pointer: *unsafe mut u32) { set *pointer = 1; }";
5617 +
        let result = try resolveProgramStr(&mut a, program);
5618 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5619 +
    } {
5620 +
        let mut a = testResolver();
5621 +
        let program = "record Marker: Linear {} fn cast() { let value: u32 = 0; let pointer = &value as *unsafe u32; }";
5622 +
        let result = try resolveProgramStr(&mut a, program);
5623 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5624 +
    }
5625 +
}
5626 +
5627 +
/// Unsafe declarations may compose unsafe operations and calls.
5628 +
@test fn testUnsafePointerOperationsAllowed() throws (testing::TestError) {
5629 +
    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); }";
5630 +
    try expectAnalyzeOk(program);
5631 +
}
5632 +
5633 +
/// Unsafe code may drop a checked reference to an unsafe pointer.
5634 +
@test fn testUnsafePointerFromReference() throws (testing::TestError) {
5635 +
    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); }";
5636 +
    try expectAnalyzeOk(program);
5637 +
}
5638 +
5639 +
/// Dropping a reference to an unsafe pointer cannot add mutability.
5640 +
@test fn testUnsafePointerCastCannotAddMutability() throws (testing::TestError) {
5641 +
    let mut a = testResolver();
5642 +
    let program = "record Marker: Linear {} unsafe fn run(value: &u32) { value as *unsafe mut u32; }";
5643 +
    let result = try resolveProgramStr(&mut a, program);
5644 +
    let err = try expectError(&result);
5645 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5646 +
        else throw testing::TestError::Failed;
5647 +
}
5648 +
5649 +
/// Recursive cast validation cannot hide a checked-to-unsafe transition.
5650 +
@test fn testNestedUnsafePointerCastRejected() throws (testing::TestError) {
5651 +
    let mut a = testResolver();
5652 +
    let program = "record Marker: Linear {} fn run(value: **u32) { value as **unsafe u32; }";
5653 +
    let result = try resolveProgramStr(&mut a, program);
5654 +
    let err = try expectError(&result);
5655 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5656 +
        else throw testing::TestError::Failed;
5657 +
}
5658 +
5659 +
/// Unsafe code may drop a checked slice reference to an unsafe slice.
5660 +
@test fn testUnsafeSliceFromReference() throws (testing::TestError) {
5661 +
    let program = "record Marker: Linear {} unsafe fn run(values: &[u32]) { let raw: *unsafe [u32] = values as *unsafe [u32]; }";
5662 +
    try expectAnalyzeOk(program);
5663 +
}
5664 +
5665 +
/// Slice casts cannot add mutability.
5666 +
@test fn testSliceCastCannotAddMutability() throws (testing::TestError) {
5667 +
    let mut a = testResolver();
5668 +
    let program = "record Marker: Linear {} fn run(values: &[u32]) { values as &mut [u32]; }";
5669 +
    let result = try resolveProgramStr(&mut a, program);
5670 +
    let err = try expectError(&result);
5671 +
    let case super::ErrorKind::InvalidAsCast(_) = err.kind
5672 +
        else throw testing::TestError::Failed;
5673 +
}
5674 +
5675 +
/// Mutable unsafe receivers do not create checked exclusive loans.
5676 +
@test fn testUnsafeReceiverDoesNotBorrowExclusively() throws (testing::TestError) {
5677 +
    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); }";
5678 +
    try expectAnalyzeOk(program);
5679 +
}
5680 +
5681 +
/// Unsafe instance-method attributes enable unsafe operations in the body.
5682 +
@test fn testUnsafeInstanceMethodBody() throws (testing::TestError) {
5683 +
    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; } }";
5684 +
    try expectAnalyzeOk(program);
5685 +
}
5686 +
5687 +
/// Unsafe instance methods cannot implement safe trait contracts.
5688 +
@test fn testUnsafeInstanceMethodSafetyMismatch() throws (testing::TestError) {
5689 +
    let mut a = testResolver();
5690 +
    let program = "record Value {} trait Read { fn (&Read) get(); } instance Read for Value { unsafe fn (value: &Value) get() {} }";
5691 +
    let result = try resolveProgramStr(&mut a, program);
5692 +
    try expectErrorKind(&result, super::ErrorKind::TraitMethodSafetyMismatch);
5693 +
}
5694 +
5695 +
/// Unsafe trait methods retain their call-site requirement through dispatch.
5696 +
@test fn testUnsafeTraitMethodCallRejected() throws (testing::TestError) {
5697 +
    let mut a = testResolver();
5698 +
    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(); }";
5699 +
    let result = try resolveProgramStr(&mut a, program);
5700 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
5701 +
}