compiler: Initialize call argument loan scratch state
0bf6dec3deea1cc366d2cb3bc870d9895be6f09c15bf10c76dab953e41ba9370
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 | + | } |