5
5
6
6
use std:: iter;
7
7
8
- use hir_def:: DefWithBodyId ;
8
+ use hir_def:: { DefWithBodyId , HasModule } ;
9
9
use la_arena:: ArenaMap ;
10
10
use stdx:: never;
11
11
use triomphe:: Arc ;
12
12
13
- use crate :: { db:: HirDatabase , ClosureId } ;
13
+ use crate :: {
14
+ db:: HirDatabase , mir:: Operand , utils:: ClosureSubst , ClosureId , Interner , Ty , TyExt , TypeFlags ,
15
+ } ;
14
16
15
17
use super :: {
16
18
BasicBlockId , BorrowKind , LocalId , MirBody , MirLowerError , MirSpan , Place , ProjectionElem ,
@@ -24,10 +26,17 @@ pub enum MutabilityReason {
24
26
Not ,
25
27
}
26
28
29
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
30
+ pub struct MovedOutOfRef {
31
+ pub ty : Ty ,
32
+ pub span : MirSpan ,
33
+ }
34
+
27
35
#[ derive( Debug , Clone , PartialEq , Eq ) ]
28
36
pub struct BorrowckResult {
29
37
pub mir_body : Arc < MirBody > ,
30
38
pub mutability_of_locals : ArenaMap < LocalId , MutabilityReason > ,
39
+ pub moved_out_of_ref : Vec < MovedOutOfRef > ,
31
40
}
32
41
33
42
fn all_mir_bodies (
@@ -68,12 +77,115 @@ pub fn borrowck_query(
68
77
let r = all_mir_bodies ( db, def)
69
78
. map ( |body| {
70
79
let body = body?;
71
- Ok ( BorrowckResult { mutability_of_locals : mutability_of_locals ( & body) , mir_body : body } )
80
+ Ok ( BorrowckResult {
81
+ mutability_of_locals : mutability_of_locals ( & body) ,
82
+ moved_out_of_ref : moved_out_of_ref ( db, & body) ,
83
+ mir_body : body,
84
+ } )
72
85
} )
73
86
. collect :: < Result < Vec < _ > , MirLowerError > > ( ) ?;
74
87
Ok ( r. into ( ) )
75
88
}
76
89
90
+ fn moved_out_of_ref ( db : & dyn HirDatabase , body : & MirBody ) -> Vec < MovedOutOfRef > {
91
+ let mut result = vec ! [ ] ;
92
+ let mut for_operand = |op : & Operand , span : MirSpan | match op {
93
+ Operand :: Copy ( p) | Operand :: Move ( p) => {
94
+ let mut ty: Ty = body. locals [ p. local ] . ty . clone ( ) ;
95
+ let mut is_dereference_of_ref = false ;
96
+ for proj in & p. projection {
97
+ if * proj == ProjectionElem :: Deref && ty. as_reference ( ) . is_some ( ) {
98
+ is_dereference_of_ref = true ;
99
+ }
100
+ ty = proj. projected_ty (
101
+ ty,
102
+ db,
103
+ |c, subst, f| {
104
+ let ( def, _) = db. lookup_intern_closure ( c. into ( ) ) ;
105
+ let infer = db. infer ( def) ;
106
+ let ( captures, _) = infer. closure_info ( & c) ;
107
+ let parent_subst = ClosureSubst ( subst) . parent_subst ( ) ;
108
+ captures
109
+ . get ( f)
110
+ . expect ( "broken closure field" )
111
+ . ty
112
+ . clone ( )
113
+ . substitute ( Interner , parent_subst)
114
+ } ,
115
+ body. owner . module ( db. upcast ( ) ) . krate ( ) ,
116
+ ) ;
117
+ }
118
+ if is_dereference_of_ref
119
+ && !ty. clone ( ) . is_copy ( db, body. owner )
120
+ && !ty. data ( Interner ) . flags . intersects ( TypeFlags :: HAS_ERROR )
121
+ {
122
+ result. push ( MovedOutOfRef { span, ty } ) ;
123
+ }
124
+ }
125
+ Operand :: Constant ( _) | Operand :: Static ( _) => ( ) ,
126
+ } ;
127
+ for ( _, block) in body. basic_blocks . iter ( ) {
128
+ for statement in & block. statements {
129
+ match & statement. kind {
130
+ StatementKind :: Assign ( _, r) => match r {
131
+ Rvalue :: ShallowInitBoxWithAlloc ( _) => ( ) ,
132
+ Rvalue :: ShallowInitBox ( o, _)
133
+ | Rvalue :: UnaryOp ( _, o)
134
+ | Rvalue :: Cast ( _, o, _)
135
+ | Rvalue :: Repeat ( o, _)
136
+ | Rvalue :: Use ( o) => for_operand ( o, statement. span ) ,
137
+ Rvalue :: CopyForDeref ( _)
138
+ | Rvalue :: Discriminant ( _)
139
+ | Rvalue :: Len ( _)
140
+ | Rvalue :: Ref ( _, _) => ( ) ,
141
+ Rvalue :: CheckedBinaryOp ( _, o1, o2) => {
142
+ for_operand ( o1, statement. span ) ;
143
+ for_operand ( o2, statement. span ) ;
144
+ }
145
+ Rvalue :: Aggregate ( _, ops) => {
146
+ for op in ops {
147
+ for_operand ( op, statement. span ) ;
148
+ }
149
+ }
150
+ } ,
151
+ StatementKind :: Deinit ( _)
152
+ | StatementKind :: StorageLive ( _)
153
+ | StatementKind :: StorageDead ( _)
154
+ | StatementKind :: Nop => ( ) ,
155
+ }
156
+ }
157
+ match & block. terminator {
158
+ Some ( terminator) => match & terminator. kind {
159
+ TerminatorKind :: SwitchInt { discr, .. } => for_operand ( discr, terminator. span ) ,
160
+ TerminatorKind :: FalseEdge { .. }
161
+ | TerminatorKind :: FalseUnwind { .. }
162
+ | TerminatorKind :: Goto { .. }
163
+ | TerminatorKind :: Resume
164
+ | TerminatorKind :: GeneratorDrop
165
+ | TerminatorKind :: Abort
166
+ | TerminatorKind :: Return
167
+ | TerminatorKind :: Unreachable
168
+ | TerminatorKind :: Drop { .. } => ( ) ,
169
+ TerminatorKind :: DropAndReplace { value, .. } => {
170
+ for_operand ( value, terminator. span ) ;
171
+ }
172
+ TerminatorKind :: Call { func, args, .. } => {
173
+ for_operand ( func, terminator. span ) ;
174
+ args. iter ( ) . for_each ( |x| for_operand ( x, terminator. span ) ) ;
175
+ }
176
+ TerminatorKind :: Assert { cond, .. } => {
177
+ for_operand ( cond, terminator. span ) ;
178
+ }
179
+ TerminatorKind :: Yield { value, .. } => {
180
+ for_operand ( value, terminator. span ) ;
181
+ }
182
+ } ,
183
+ None => ( ) ,
184
+ }
185
+ }
186
+ result
187
+ }
188
+
77
189
fn is_place_direct ( lvalue : & Place ) -> bool {
78
190
!lvalue. projection . iter ( ) . any ( |x| * x == ProjectionElem :: Deref )
79
191
}
0 commit comments