compiler: Track region loan expiry with checked source identities
9c9a3c129965e4881736d64b3ee30d2f2800244258350f630a3ba3bc8c293085
1 parent
186b07ed
lib/std/lang/resolver.rad
+34 -15
| 991 | 991 | return MatchSubject { effectiveTy: *target, by }; |
|
| 992 | 992 | } |
|
| 993 | 993 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 994 | 994 | } |
|
| 995 | 995 | ||
| 996 | + | /// Source nodes that define the identities in one region environment. |
|
| 997 | + | export union RegionDeclarations: Copy { |
|
| 998 | + | /// Declaration parameters, including any non-region constraints. |
|
| 999 | + | Parameters(*[*ast::Node]), |
|
| 1000 | + | /// Single region introduced by a lexical block. |
|
| 1001 | + | Block(*ast::Node), |
|
| 1002 | + | } |
|
| 1003 | + | ||
| 996 | 1004 | /// Region names introduced by a declaration or lexical block. |
|
| 997 | 1005 | export record RegionScope: Copy { |
|
| 1006 | + | /// Immutable source declarations that supply region identities. |
|
| 1007 | + | declarations: RegionDeclarations, |
|
| 998 | 1008 | /// Entries in declaration order. |
|
| 999 | 1009 | entries: *unsafe [*unsafe mut types::Region], |
|
| 1000 | 1010 | /// Enclosing lexical region environment. |
|
| 1001 | 1011 | parent: ?*RegionScope, |
|
| 1002 | 1012 | } |
| 1139 | 1149 | set *region = types::Region { id: node.id, origin: types::RegionOrigin::Parameter, name, parent: nil }; |
|
| 1140 | 1150 | set entries[index] = region; |
|
| 1141 | 1151 | set index += 1; |
|
| 1142 | 1152 | } |
|
| 1143 | 1153 | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
|
| 1144 | - | set *scope = RegionScope { entries, parent: nil }; |
|
| 1154 | + | set *scope = RegionScope { declarations: RegionDeclarations::Parameters(nodes), entries, parent: nil }; |
|
| 1145 | 1155 | let frozen: *RegionScope = scope; |
|
| 1146 | 1156 | set index = 0; |
|
| 1147 | 1157 | for node in nodes { |
|
| 1148 | 1158 | let case ast::NodeValue::Region { parent, .. } = node.value else continue; |
|
| 1149 | 1159 | if let parentNode = parent { |
| 2738 | 2748 | scope: ?*RegionScope, dependency: *unsafe types::Region, destination: ?*unsafe types::Region |
|
| 2739 | 2749 | ) -> bool { |
|
| 2740 | 2750 | if let region = destination { |
|
| 2741 | 2751 | return types::regionContains(dependency, region); |
|
| 2742 | 2752 | } |
|
| 2743 | - | return regionInScope(scope, dependency); |
|
| 2753 | + | return regionInScope(scope, dependency.id); |
|
| 2744 | 2754 | } |
|
| 2745 | 2755 | ||
| 2746 | 2756 | /// Require stored references to cover the lifetime of checked destination storage. |
|
| 2747 | 2757 | unsafe fn validateRegionalStore 'arena (self: &mut Resolver 'arena, place: *ast::Node, value: *ast::Node, ty: Type) |
|
| 2748 | 2758 | throws (ResolveError) |
| 3987 | 3997 | let entries = try! alloc::allocRawSlice( |
|
| 3988 | 3998 | self.arena, @sizeOf(*unsafe mut types::Region), @alignOf(*unsafe mut types::Region), 1 |
|
| 3989 | 3999 | ) as *unsafe mut [*unsafe mut types::Region]; |
|
| 3990 | 4000 | set entries[0] = region; |
|
| 3991 | 4001 | let scope = try! alloc::alloc(&mut *self.arena, @sizeOf(RegionScope), @alignOf(RegionScope)) as *mut RegionScope; |
|
| 3992 | - | set *scope = RegionScope { entries, parent: self.regionScope }; |
|
| 4002 | + | set *scope = RegionScope { declarations: RegionDeclarations::Block(node), entries, parent: self.regionScope }; |
|
| 3993 | 4003 | return scope; |
|
| 3994 | 4004 | } |
|
| 3995 | 4005 | ||
| 3996 | 4006 | /// Qualify an existing-place borrow with its block's region. |
|
| 3997 | 4007 | unsafe fn qualifyBlockBorrow 'arena ( |
| 10061 | 10071 | ||
| 10062 | 10072 | /// A projection loan that remains active until its named region ends. |
|
| 10063 | 10073 | record RegionalLoan: Copy { |
|
| 10064 | 10074 | /// Address expression that supplies access to the loan. |
|
| 10065 | 10075 | source: *ast::Node, |
|
| 10066 | - | /// Declared lifetime of the projection. |
|
| 10067 | - | region: *unsafe types::Region, |
|
| 10076 | + | /// Source identity of the projection's declared lifetime. |
|
| 10077 | + | regionId: u32, |
|
| 10068 | 10078 | /// Storage protected by the projection. |
|
| 10069 | 10079 | place: BorrowPlace, |
|
| 10070 | 10080 | /// Whether accesses through other references are excluded. |
|
| 10071 | 10081 | exclusive: bool, |
|
| 10072 | 10082 | } |
|
| 10073 | 10083 | ||
| 10074 | 10084 | /// Return whether a region identity is visible in a lexical environment. |
|
| 10075 | - | unsafe fn regionInScope(scope: ?*RegionScope, region: *unsafe types::Region) -> bool { |
|
| 10085 | + | fn regionInScope(scope: ?*RegionScope, regionId: u32) -> bool { |
|
| 10076 | 10086 | let mut cursor = scope; |
|
| 10077 | 10087 | while let current = cursor { |
|
| 10078 | - | for entry in current.entries { |
|
| 10079 | - | if entry.id == region.id { |
|
| 10080 | - | return true; |
|
| 10088 | + | match current.declarations { |
|
| 10089 | + | case RegionDeclarations::Parameters(nodes) => { |
|
| 10090 | + | for node in nodes { |
|
| 10091 | + | if let case ast::NodeValue::Region { .. } = node.value; node.id == regionId { |
|
| 10092 | + | return true; |
|
| 10093 | + | } |
|
| 10094 | + | } |
|
| 10095 | + | } |
|
| 10096 | + | case RegionDeclarations::Block(node) => { |
|
| 10097 | + | if node.id == regionId { |
|
| 10098 | + | return true; |
|
| 10099 | + | } |
|
| 10081 | 10100 | } |
|
| 10082 | 10101 | } |
|
| 10083 | 10102 | set cursor = current.parent; |
|
| 10084 | 10103 | } |
|
| 10085 | 10104 | return false; |
|
| 10086 | 10105 | } |
|
| 10087 | 10106 | ||
| 10088 | 10107 | /// Retain only loans whose regions remain active at a control-flow destination. |
|
| 10089 | - | unsafe fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking { |
|
| 10108 | + | fn regionalLoansInScope 'arena 'checking (checker: &LinearChecker 'arena 'checking, mask: u64, scope: ?*RegionScope) -> u64 where 'arena: 'checking { |
|
| 10090 | 10109 | let mut result: u64 = 0; |
|
| 10091 | 10110 | for i in 0..checker.regionalLen { |
|
| 10092 | 10111 | let bit = (1 as u64) << (i as u64); |
|
| 10093 | - | if (mask & bit) <> 0 and regionInScope(scope, checker.regional[i].region) { |
|
| 10112 | + | if (mask & bit) <> 0 and regionInScope(scope, checker.regional[i].regionId) { |
|
| 10094 | 10113 | set result |= bit; |
|
| 10095 | 10114 | } |
|
| 10096 | 10115 | } |
|
| 10097 | 10116 | return result; |
|
| 10098 | 10117 | } |
| 10107 | 10126 | } |
|
| 10108 | 10127 | return result; |
|
| 10109 | 10128 | } |
|
| 10110 | 10129 | ||
| 10111 | 10130 | /// Reclaim ended-region entries and preserve loans for enclosing regions. |
|
| 10112 | - | unsafe fn compactRegionalLoans 'arena 'checking ( |
|
| 10131 | + | fn compactRegionalLoans 'arena 'checking ( |
|
| 10113 | 10132 | checker: &mut LinearChecker 'arena 'checking, env: &mut LinearEnv, |
|
| 10114 | 10133 | scope: ?*RegionScope |
|
| 10115 | 10134 | ) where 'arena: 'checking { |
|
| 10116 | 10135 | let oldLen = checker.regionalLen; |
|
| 10117 | 10136 | let mut mapping: [u64; MAX_REGIONAL_LOANS] = [0; MAX_REGIONAL_LOANS]; |
|
| 10118 | 10137 | let mut next: u32 = 0; |
|
| 10119 | 10138 | for i in 0..oldLen { |
|
| 10120 | 10139 | let loan = checker.regional[i]; |
|
| 10121 | - | if regionInScope(scope, loan.region) { |
|
| 10140 | + | if regionInScope(scope, loan.regionId) { |
|
| 10122 | 10141 | set checker.regional[next] = loan; |
|
| 10123 | 10142 | set mapping[i] = (1 as u64) << (next as u64); |
|
| 10124 | 10143 | set next += 1; |
|
| 10125 | 10144 | } |
|
| 10126 | 10145 | } |
| 10172 | 10191 | } |
|
| 10173 | 10192 | if checker.regionalLen >= MAX_REGIONAL_LOANS { |
|
| 10174 | 10193 | throw emitError(checker.resolver, node, ErrorKind::RegionalLoanOverflow); |
|
| 10175 | 10194 | } |
|
| 10176 | 10195 | let index = checker.regionalLen; |
|
| 10177 | - | set checker.regional[index] = RegionalLoan { source: node, region, place, exclusive: ast::isExclusiveAddress(address) }; |
|
| 10196 | + | set checker.regional[index] = RegionalLoan { source: node, regionId: region.id, place, exclusive: ast::isExclusiveAddress(address) }; |
|
| 10178 | 10197 | set checker.regionalLen += 1; |
|
| 10179 | 10198 | set env.regionalLoans |= (1 as u64) << (index as u64); |
|
| 10180 | 10199 | } |
|
| 10181 | 10200 | ||
| 10182 | 10201 | /// Return whether an initializer copies a shared reference with a named region. |
| 10525 | 10544 | } |
|
| 10526 | 10545 | } |
|
| 10527 | 10546 | } |
|
| 10528 | 10547 | ||
| 10529 | 10548 | /// Record the ownership state of a loop's condition-false exit. |
|
| 10530 | - | unsafe fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
|
| 10549 | + | fn setLinearLoopNaturalExit 'arena 'checking (checker: &mut LinearChecker 'arena 'checking, env: &LinearEnv) where 'arena: 'checking { |
|
| 10531 | 10550 | assert checker.loopDepth > 0, "linear loop exit outside loop"; |
|
| 10532 | 10551 | let depth = checker.loopDepth - 1; |
|
| 10533 | 10552 | set checker.loopExitLoans[depth] |= regionalLoansInScope(checker, env.regionalLoans, checker.loopRegions[depth]); |
|
| 10534 | 10553 | set checker.loopExitAvailable[depth] = env.available; |
|
| 10535 | 10554 | set checker.loopHasNaturalExit[depth] = true; |