compiler: Initialize call argument loan scratch state

0bf6dec3deea1cc366d2cb3bc870d9895be6f09c15bf10c76dab953e41ba9370
Alexis Sellier committed ago 1 parent 44c1050d
lib/std/lang/resolver.rad +17 -9
971 971
    place: BorrowPlace,
972 972
    /// Whether other reads of the source are excluded.
973 973
    exclusive: bool,
974 974
}
975 975
976 +
/// Argument metadata retained during call-scoped conflict checks.
977 +
record CallArgument: Copy {
978 +
    /// Receiver or explicit argument expression.
979 +
    node: *ast::Node,
980 +
    /// Whether overlapping argument access is excluded.
981 +
    exclusive: bool,
982 +
}
983 +
976 984
/// Function-local exact-use checker state.
977 985
/// Read loop arrays only at indices below `loopDepth`.
978 986
/// `enterLinearLoop` initializes each slot before it increases `loopDepth`.
979 987
record LinearChecker: 'arena + 'checking where 'arena: 'checking {
980 988
    /// Resolver that owns the symbols and diagnostics.
10888 10896
        for arg in call.args {
10889 10897
            try checkLinearNode(checker, env, arg, LinearUse::Consume);
10890 10898
        }
10891 10899
        return;
10892 10900
    };
10893 -
    let mut arguments: [*ast::Node; MAX_FN_PARAMS + 1] = undefined;
10894 -
    let mut exclusive: [bool; MAX_FN_PARAMS + 1] = undefined;
10901 +
    let mut arguments: [?CallArgument; MAX_FN_PARAMS + 1] = [nil; MAX_FN_PARAMS + 1];
10895 10902
    let mut argumentsLen: u32 = 0;
10896 10903
10897 10904
    // Method function types exclude their implicit receiver. Account for it
10898 10905
    // explicitly so owning receivers are consumed and reference receivers
10899 10906
    // participate in call-scoped loan conflict checks.
10920 10927
                receiverMutable or receiverClass == types::PointerClass::Owned);
10921 10928
            if receiverMutable or receiverClass == types::PointerClass::Owned {
10922 10929
                try checkPatternLoan(checker, access.parent);
10923 10930
            }
10924 10931
            if receiverClass <> types::PointerClass::Unsafe {
10925 -
                set arguments[argumentsLen] = access.parent;
10926 -
                set exclusive[argumentsLen] =
10927 -
                    receiverClass == types::PointerClass::Owned or receiverMutable;
10932 +
                set arguments[argumentsLen] = CallArgument {
10933 +
                    node: access.parent,
10934 +
                    exclusive: receiverClass == types::PointerClass::Owned or receiverMutable,
10935 +
                };
10928 10936
                set argumentsLen += 1;
10929 10937
            }
10930 10938
            if types::isReference(receiverClass) {
10931 10939
                try checkLinearNode(checker, env, access.parent, LinearUse::Borrow);
10932 10940
                if createsExplicitBorrow(access.parent) {
10944 10952
        if argExclusive {
10945 10953
            try checkPatternLoan(checker, arg);
10946 10954
        }
10947 10955
        if not isUnsafePointerType(expected) {
10948 10956
            for j in 0..argumentsLen {
10949 -
                if exclusive[j] or argExclusive {
10950 -
                    if let name = callArgumentConflict(checker.resolver, arguments[j], arg) {
10957 +
                let previous = arguments[j] else panic "checkLinearCall: missing active argument";
10958 +
                if previous.exclusive or argExclusive {
10959 +
                    if let name = callArgumentConflict(checker.resolver, previous.node, arg) {
10951 10960
                        throw emitError(checker.resolver, arg, ErrorKind::BorrowConflict(name));
10952 10961
                    }
10953 10962
                }
10954 10963
            }
10955 -
            set arguments[argumentsLen] = arg;
10956 -
            set exclusive[argumentsLen] = argExclusive;
10964 +
            set arguments[argumentsLen] = CallArgument { node: arg, exclusive: argExclusive };
10957 10965
            set argumentsLen += 1;
10958 10966
        }
10959 10967
        try checkLocalLoans(checker, env, arg, argExclusive);
10960 10968
        if isBorrowedReferenceParameter(expected) {
10961 10969
            try checkLinearNode(checker, env, arg, LinearUse::Borrow);
lib/std/lang/resolver/tests/regions.rad +24 -0
1066 1066
            try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition);
1067 1067
        }
1068 1068
    }
1069 1069
}
1070 1070
1071 +
/// Call tracking includes all explicit arguments and the implicit receiver.
1072 +
@test unsafe fn testCallLoanScratchCapacity() throws (testing::TestError) {
1073 +
    let mut arena = super::testArena();
1074 +
    let storage: 'test = &mut arena in {
1075 +
        let mut res = super::testResolver(storage);
1076 +
        let result = try super::resolveProgramStr(&mut res,
1077 +
            "record R: Copy { value: u32 } fn (r: &mut R) take(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut r = R { value: 0 }; let mut other: u32 = 0; r.take(0, 0, 0, 0, 0, 0, 0, &mut other); set r.value = 1; set other = 2; }"
1078 +
        );
1079 +
        try super::expectNoErrors(&result);
1080 +
    }
1081 +
    for program in [
1082 +
        "record R: Copy { value: u32 } fn (r: &mut R) take(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut r = R { value: 0 }; r.take(0, 0, 0, 0, 0, 0, 0, &mut r.value); }",
1083 +
        "fn take(a: &mut u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut value: u32 = 0; take(&mut value, 0, 0, 0, 0, 0, 0, &mut value); }",
1084 +
    ] {
1085 +
        let mut errorArena = super::testArena();
1086 +
        let errorStorage: 'error = &mut errorArena in {
1087 +
            let mut res = super::testResolver(errorStorage);
1088 +
            let result = try super::resolveProgramStr(&mut res, program);
1089 +
            let error = try super::expectError(&result);
1090 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed;
1091 +
        }
1092 +
    }
1093 +
}
1094 +
1071 1095
/// Loan compaction preserves outer-region conflicts after child slots expire.
1072 1096
@test unsafe fn testRegionalLoanCompactionConflict() throws (testing::TestError) {
1073 1097
    for program in [
1074 1098
        "fn f 'r (p: &'r mut u32) { let value: u32 = 0; let child: 'child = &value where 'r: 'child in { let local = &*child; let retained = &*p; } set *p = 1; }",
1075 1099
        "fn f 'r (p: &'r mut u32) { let value: u32 = 0; loop { let child: 'child = &value where 'r: 'child in { let local = &*child; let retained = &*p; } break; } set *p = 1; }",
test/tests/regions.call.scratch.rad added +35 -0
1 +
//! returns: 0
2 +
3 +
/// Mutable receiver used with a separate mutable argument.
4 +
record Counter: Copy {
5 +
    /// Accumulated count.
6 +
    value: u32,
7 +
}
8 +
9 +
/// Update disjoint receiver and argument storage.
10 +
fn (counter: &mut Counter) add(other: &mut u32) {
11 +
    set counter.value += 1;
12 +
    set *other += 2;
13 +
}
14 +
15 +
/// Fill all explicit argument slots and update the two borrowed values.
16 +
fn update(a: &mut u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {
17 +
    set *a += b + c + d;
18 +
    set *h += e + f + g;
19 +
}
20 +
21 +
/// Exercise receiver loans, callback calls, and per-call scratch reuse.
22 +
@default fn main() -> u32 {
23 +
    let mut counter = Counter { value: 0 };
24 +
    let mut left: u32 = 0;
25 +
    let mut right: u32 = 0;
26 +
    counter.add(&mut left);
27 +
    counter.add(&mut right);
28 +
    assert counter.value == 2;
29 +
    let callback = update;
30 +
    callback(&mut left, 1, 2, 3, 4, 5, 6, &mut right);
31 +
    update(&mut right, 1, 1, 1, 2, 2, 2, &mut left);
32 +
    assert left == 14;
33 +
    assert right == 20;
34 +
    return 0;
35 +
}