]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/build/expr/into.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / src / librustc_mir / build / expr / into.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
5 use crate::hair::*;
6 use rustc::mir::*;
7 use rustc::ty;
8
9 use rustc_target::spec::abi::Abi;
10
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12 /// Compile `expr`, storing the result into `destination`, which
13 /// is assumed to be uninitialized.
14 pub fn into_expr(
15 &mut self,
16 destination: &Place<'tcx>,
17 mut block: BasicBlock,
18 expr: Expr<'tcx>,
19 ) -> BlockAnd<()> {
20 debug!(
21 "into_expr(destination={:?}, block={:?}, expr={:?})",
22 destination, block, expr
23 );
24
25 // since we frequently have to reference `self` from within a
26 // closure, where `self` would be shadowed, it's easier to
27 // just use the name `this` uniformly
28 let this = self;
29 let expr_span = expr.span;
30 let source_info = this.source_info(expr_span);
31
32 let expr_is_block_or_scope = match expr.kind {
33 ExprKind::Block { .. } => true,
34 ExprKind::Scope { .. } => true,
35 _ => false,
36 };
37
38 if !expr_is_block_or_scope {
39 this.block_context.push(BlockFrame::SubExpr);
40 }
41
42 let block_and = match expr.kind {
43 ExprKind::Scope {
44 region_scope,
45 lint_level,
46 value,
47 } => {
48 let region_scope = (region_scope, source_info);
49 this.in_scope(region_scope, lint_level, |this| {
50 this.into(destination, block, value)
51 })
52 }
53 ExprKind::Block { body: ast_block } => {
54 this.ast_block(destination, block, ast_block, source_info)
55 }
56 ExprKind::Match { scrutinee, arms } => {
57 this.match_expr(destination, expr_span, block, scrutinee, arms)
58 }
59 ExprKind::NeverToAny { source } => {
60 let source = this.hir.mirror(source);
61 let is_call = match source.kind {
62 ExprKind::Call { .. } => true,
63 _ => false,
64 };
65
66 unpack!(block = this.as_local_rvalue(block, source));
67
68 // This is an optimization. If the expression was a call then we already have an
69 // unreachable block. Don't bother to terminate it and create a new one.
70 if is_call {
71 block.unit()
72 } else {
73 this.cfg
74 .terminate(block, source_info, TerminatorKind::Unreachable);
75 let end_block = this.cfg.start_new_block();
76 end_block.unit()
77 }
78 }
79 ExprKind::LogicalOp { op, lhs, rhs } => {
80 // And:
81 //
82 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
83 // | | (false)
84 // +----------false-----------+------------------> [false_block]
85 //
86 // Or:
87 //
88 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
89 // | (true) | (false)
90 // [true_block] [false_block]
91
92 let (true_block, false_block, mut else_block, join_block) = (
93 this.cfg.start_new_block(),
94 this.cfg.start_new_block(),
95 this.cfg.start_new_block(),
96 this.cfg.start_new_block(),
97 );
98
99 let lhs = unpack!(block = this.as_local_operand(block, lhs));
100 let blocks = match op {
101 LogicalOp::And => (else_block, false_block),
102 LogicalOp::Or => (true_block, else_block),
103 };
104 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
105 this.cfg.terminate(block, source_info, term);
106
107 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
108 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
109 this.cfg.terminate(else_block, source_info, term);
110
111 this.cfg.push_assign_constant(
112 true_block,
113 source_info,
114 destination,
115 Constant {
116 span: expr_span,
117 ty: this.hir.bool_ty(),
118 user_ty: None,
119 literal: this.hir.true_literal(),
120 },
121 );
122
123 this.cfg.push_assign_constant(
124 false_block,
125 source_info,
126 destination,
127 Constant {
128 span: expr_span,
129 ty: this.hir.bool_ty(),
130 user_ty: None,
131 literal: this.hir.false_literal(),
132 },
133 );
134
135 this.cfg.terminate(
136 true_block,
137 source_info,
138 TerminatorKind::Goto { target: join_block },
139 );
140 this.cfg.terminate(
141 false_block,
142 source_info,
143 TerminatorKind::Goto { target: join_block },
144 );
145
146 join_block.unit()
147 }
148 ExprKind::Loop { body } => {
149 // [block]
150 // |
151 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
152 // | ^ |
153 // false link | |
154 // | +-----------------------------------------+
155 // +-> [diverge_cleanup]
156 // The false link is required to make sure borrowck considers unwinds through the
157 // body, even when the exact code in the body cannot unwind
158
159 let loop_block = this.cfg.start_new_block();
160 let exit_block = this.cfg.start_new_block();
161
162 // start the loop
163 this.cfg.terminate(
164 block,
165 source_info,
166 TerminatorKind::Goto { target: loop_block },
167 );
168
169 this.in_breakable_scope(
170 Some(loop_block),
171 exit_block,
172 destination.clone(),
173 move |this| {
174 // conduct the test, if necessary
175 let body_block = this.cfg.start_new_block();
176 let diverge_cleanup = this.diverge_cleanup();
177 this.cfg.terminate(
178 loop_block,
179 source_info,
180 TerminatorKind::FalseUnwind {
181 real_target: body_block,
182 unwind: Some(diverge_cleanup),
183 },
184 );
185
186 // The “return” value of the loop body must always be an unit. We therefore
187 // introduce a unit temporary as the destination for the loop body.
188 let tmp = this.get_unit_temp();
189 // Execute the body, branching back to the test.
190 let body_block_end = unpack!(this.into(&tmp, body_block, body));
191 this.cfg.terminate(
192 body_block_end,
193 source_info,
194 TerminatorKind::Goto { target: loop_block },
195 );
196 },
197 );
198 exit_block.unit()
199 }
200 ExprKind::Call { ty, fun, args, from_hir_call } => {
201 let intrinsic = match ty.sty {
202 ty::FnDef(def_id, _) => {
203 let f = ty.fn_sig(this.hir.tcx());
204 if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
205 Some(this.hir.tcx().item_name(def_id).as_str())
206 } else {
207 None
208 }
209 }
210 _ => None,
211 };
212 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
213 let fun = unpack!(block = this.as_local_operand(block, fun));
214 if intrinsic == Some("move_val_init") {
215 // `move_val_init` has "magic" semantics - the second argument is
216 // always evaluated "directly" into the first one.
217
218 let mut args = args.into_iter();
219 let ptr = args.next().expect("0 arguments to `move_val_init`");
220 let val = args.next().expect("1 argument to `move_val_init`");
221 assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
222
223 let ptr = this.hir.mirror(ptr);
224 let ptr_ty = ptr.ty;
225 // Create an *internal* temp for the pointer, so that unsafety
226 // checking won't complain about the raw pointer assignment.
227 let ptr_temp = this.local_decls.push(LocalDecl {
228 mutability: Mutability::Mut,
229 ty: ptr_ty,
230 user_ty: UserTypeProjections::none(),
231 name: None,
232 source_info,
233 visibility_scope: source_info.scope,
234 internal: true,
235 is_user_variable: None,
236 is_block_tail: None,
237 });
238 let ptr_temp = Place::from(ptr_temp);
239 let block = unpack!(this.into(&ptr_temp, block, ptr));
240 this.into(&ptr_temp.deref(), block, val)
241 } else {
242 let args: Vec<_> = args
243 .into_iter()
244 .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
245 .collect();
246
247 let success = this.cfg.start_new_block();
248 let cleanup = this.diverge_cleanup();
249 this.cfg.terminate(
250 block,
251 source_info,
252 TerminatorKind::Call {
253 func: fun,
254 args,
255 cleanup: Some(cleanup),
256 // FIXME(varkor): replace this with an uninhabitedness-based check.
257 // This requires getting access to the current module to call
258 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
259 destination: if expr.ty.is_never() {
260 None
261 } else {
262 Some((destination.clone(), success))
263 },
264 from_hir_call,
265 },
266 );
267 success.unit()
268 }
269 }
270 ExprKind::Use { source } => {
271 this.into(destination, block, source)
272 }
273
274 // These cases don't actually need a destination
275 ExprKind::Assign { .. }
276 | ExprKind::AssignOp { .. }
277 | ExprKind::Continue { .. }
278 | ExprKind::Break { .. }
279 | ExprKind::InlineAsm { .. }
280 | ExprKind::Return { .. } => {
281 unpack!(block = this.stmt_expr(block, expr, None));
282 this.cfg.push_assign_unit(block, source_info, destination);
283 block.unit()
284 }
285
286 // Avoid creating a temporary
287 ExprKind::VarRef { .. } |
288 ExprKind::SelfRef |
289 ExprKind::StaticRef { .. } |
290 ExprKind::PlaceTypeAscription { .. } |
291 ExprKind::ValueTypeAscription { .. } => {
292 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
293
294 let place = unpack!(block = this.as_place(block, expr));
295 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
296 this.cfg
297 .push_assign(block, source_info, destination, rvalue);
298 block.unit()
299 }
300 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
301 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
302
303 // Create a "fake" temporary variable so that we check that the
304 // value is Sized. Usually, this is caught in type checking, but
305 // in the case of box expr there is no such check.
306 if destination.projection.is_some() {
307 this.local_decls
308 .push(LocalDecl::new_temp(expr.ty, expr.span));
309 }
310
311 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
312
313 let place = unpack!(block = this.as_place(block, expr));
314 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
315 this.cfg
316 .push_assign(block, source_info, destination, rvalue);
317 block.unit()
318 }
319
320 // these are the cases that are more naturally handled by some other mode
321 ExprKind::Unary { .. }
322 | ExprKind::Binary { .. }
323 | ExprKind::Box { .. }
324 | ExprKind::Cast { .. }
325 | ExprKind::Pointer { .. }
326 | ExprKind::Repeat { .. }
327 | ExprKind::Borrow { .. }
328 | ExprKind::Array { .. }
329 | ExprKind::Tuple { .. }
330 | ExprKind::Adt { .. }
331 | ExprKind::Closure { .. }
332 | ExprKind::Literal { .. }
333 | ExprKind::Yield { .. } => {
334 debug_assert!(match Category::of(&expr.kind).unwrap() {
335 // should be handled above
336 Category::Rvalue(RvalueFunc::Into) => false,
337
338 // must be handled above or else we get an
339 // infinite loop in the builder; see
340 // e.g., `ExprKind::VarRef` above
341 Category::Place => false,
342
343 _ => true,
344 });
345
346 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
347 this.cfg.push_assign(block, source_info, destination, rvalue);
348 block.unit()
349 }
350 };
351
352 if !expr_is_block_or_scope {
353 let popped = this.block_context.pop();
354 assert!(popped.is_some());
355 }
356
357 block_and
358 }
359 }