resolver: Initialize active ownership state

d3a86b214d8493da46b920eaf31b90750f64c1affaa1b43f981fd9256ecd9f0c
Symbol and loop slots are initialized before their active counts
increase. Express that invariant with non-optional symbol pointers
and initialize only active slots to avoid redundant stores and checks.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent 76d18fbc
lib/std/lang/resolver.rad +20 -16
795 795
    /// Use the value as an assignment target.
796 796
    Place,
797 797
}
798 798
799 799
/// Per-control-flow-path ownership state.
800 +
/// Read only the initialized symbol prefix below `len`.
800 801
record LinearEnv: Copy {
801 -
    /// Symbols tracked on this control-flow path.
802 -
    symbols: [?*mut Symbol; MAX_LINEAR_BINDINGS],
802 +
    /// Symbol pointers. Entries below `len` are initialized and not optional.
803 +
    symbols: [*mut Symbol; MAX_LINEAR_BINDINGS],
803 804
    /// Bit set for each binding that remains available.
804 805
    available: u64,
805 -
    /// Number of entries in `symbols`.
806 +
    /// Number of initialized entries in `symbols`.
806 807
    len: u32,
807 808
    /// Whether this control-flow path has terminated.
808 809
    terminated: bool,
809 810
}
810 811
811 812
/// Function-local exact-use checker state.
813 +
/// Read loop arrays only at indices below `loopDepth`.
814 +
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
812 815
record LinearChecker: Copy {
813 816
    /// Resolver that owns the symbols and diagnostics.
814 817
    resolver: *mut Resolver,
815 818
    /// Binding count at entry to each active loop.
816 819
    loopMarks: [u32; MAX_LINEAR_LOOP_DEPTH],
7438 7441
}
7439 7442
7440 7443
/// Find a tracked binding by symbol identity.
7441 7444
fn findLinearBinding(env: *LinearEnv, sym: *mut Symbol) -> ?u32 {
7442 7445
    for i in 0..env.len {
7443 -
        if let bound = env.symbols[i]; bound == sym {
7446 +
        if env.symbols[i] == sym {
7444 7447
            return i;
7445 7448
        }
7446 7449
    }
7447 7450
    return nil;
7448 7451
}
7483 7486
    start: u32,
7484 7487
) throws (ResolveError) {
7485 7488
    if not env.terminated {
7486 7489
        for i in start..env.len {
7487 7490
            if linearBindingAvailable(env, i) {
7488 -
                let sym = env.symbols[i] else panic "finishLinearScope: missing symbol";
7491 +
                let sym = env.symbols[i];
7489 7492
                let case SymbolData::Value { type: ty, .. } = sym.data
7490 7493
                    else panic "finishLinearScope: expected value symbol";
7491 7494
                if isLinear(ty) {
7492 7495
                    throw emitError(
7493 7496
                        checker.resolver,
7542 7545
    }
7543 7546
    assert left.len == right.len, "joinLinearBranches: scope mismatch";
7544 7547
    let mut result = left;
7545 7548
    for i in 0..left.len {
7546 7549
        if linearBindingAvailable(&left, i) <> linearBindingAvailable(&right, i) {
7547 -
            let sym = left.symbols[i] else panic "joinLinearBranches: missing symbol";
7550 +
            let sym = left.symbols[i];
7548 7551
            let case SymbolData::Value { type: ty, .. } = sym.data
7549 7552
                else panic "joinLinearBranches: expected value symbol";
7550 7553
            if isLinear(ty) {
7551 7554
                throw emitError(
7552 7555
                    checker.resolver,
7565 7568
    checker: *mut LinearChecker,
7566 7569
    env: *mut LinearEnv,
7567 7570
) throws (ResolveError) {
7568 7571
    for i in 0..env.len {
7569 7572
        if linearBindingAvailable(env, i) {
7570 -
            let sym = env.symbols[i] else panic "finishLinearExit: missing symbol";
7573 +
            let sym = env.symbols[i];
7571 7574
            let case SymbolData::Value { type: ty, .. } = sym.data
7572 7575
                else panic "finishLinearExit: expected value symbol";
7573 7576
            if isLinear(ty) {
7574 7577
                throw emitError(
7575 7578
                    checker.resolver,
7642 7645
    }
7643 7646
    try finishLinearScope(checker, env, start);
7644 7647
}
7645 7648
7646 7649
/// Push a repeated-control-flow boundary.
7650 +
/// Initialize all loop state at this depth before increasing `loopDepth`.
7647 7651
fn enterLinearLoop(checker: *mut LinearChecker, env: *LinearEnv) {
7648 7652
    assert checker.loopDepth < MAX_LINEAR_LOOP_DEPTH, "linear loop nesting overflow";
7649 7653
    let depth = checker.loopDepth;
7650 7654
    set checker.loopMarks[depth] = env.len;
7651 7655
    set checker.loopAvailable[depth] = env.available;
7669 7673
    let mark = checker.loopMarks[depth];
7670 7674
    let entryAvailable = checker.loopAvailable[depth];
7671 7675
    for i in 0..mark {
7672 7676
        let bit = (1 as u64) << (i as u64);
7673 7677
        if (env.available & bit) <> (entryAvailable & bit) {
7674 -
            let sym = env.symbols[i] else panic "checkLinearLoopBackEdge: missing symbol";
7678 +
            let sym = env.symbols[i];
7675 7679
            throw emitError(
7676 7680
                checker.resolver,
7677 7681
                node,
7678 7682
                ErrorKind::LinearBranchMismatch(sym.name),
7679 7683
            );
7701 7705
    if checker.loopHasNaturalExit[depth] or checker.loopBreakSeen[depth] {
7702 7706
        let expected = checker.loopExitAvailable[depth];
7703 7707
        for i in 0..mark {
7704 7708
            let bit = (1 as u64) << (i as u64);
7705 7709
            if (env.available & bit) <> (expected & bit) {
7706 -
                let sym = env.symbols[i] else panic "checkLinearLoopBreak: missing symbol";
7710 +
                let sym = env.symbols[i];
7707 7711
                throw emitError(
7708 7712
                    checker.resolver,
7709 7713
                    node,
7710 7714
                    ErrorKind::LinearBranchMismatch(sym.name),
7711 7715
                );
7785 7789
            }
7786 7790
            case ast::ProngArm::Else => {}
7787 7791
        }
7788 7792
        if prong.guard <> nil {
7789 7793
            for i in bindingsStart..branch.len {
7790 -
                let sym = branch.symbols[i] else panic "checkLinearMatch: missing symbol";
7794 +
                let sym = branch.symbols[i];
7791 7795
                let case SymbolData::Value { type: ty, .. } = sym.data
7792 7796
                    else panic "checkLinearMatch: expected value symbol";
7793 7797
                if isLinear(ty) {
7794 7798
                    throw emitError(checker.resolver, prongNode, ErrorKind::LinearDiscard);
7795 7799
                }
8376 8380
    params: *mut [*ast::Node],
8377 8381
    body: *ast::Node,
8378 8382
) throws (ResolveError) {
8379 8383
    let mut checker = LinearChecker {
8380 8384
        resolver: self,
8381 -
        loopMarks: [0; MAX_LINEAR_LOOP_DEPTH],
8382 -
        loopAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8383 -
        loopExitAvailable: [0; MAX_LINEAR_LOOP_DEPTH],
8384 -
        loopHasNaturalExit: [false; MAX_LINEAR_LOOP_DEPTH],
8385 -
        loopBreakSeen: [false; MAX_LINEAR_LOOP_DEPTH],
8385 +
        loopMarks: undefined,
8386 +
        loopAvailable: undefined,
8387 +
        loopExitAvailable: undefined,
8388 +
        loopHasNaturalExit: undefined,
8389 +
        loopBreakSeen: undefined,
8386 8390
        loopDepth: 0,
8387 8391
    };
8388 8392
    let mut env = LinearEnv {
8389 -
        symbols: [nil; MAX_LINEAR_BINDINGS],
8393 +
        symbols: undefined,
8390 8394
        available: 0,
8391 8395
        len: 0,
8392 8396
        terminated: false,
8393 8397
    };
8394 8398
    if let receiverNode = receiver {