]>
Commit | Line | Data |
---|---|---|
0731742a | 1 | //! See docs in `build/expr/mod.rs`. |
e9174d1e | 2 | |
49aad941 | 3 | use rustc_index::{Idx, IndexVec}; |
064997fb | 4 | use rustc_middle::ty::util::IntTypeExt; |
353b0b11 | 5 | use rustc_target::abi::{Abi, FieldIdx, Primitive}; |
e9174d1e | 6 | |
5869c6ff | 7 | use crate::build::expr::as_place::PlaceBase; |
9fa01778 | 8 | use crate::build::expr::category::{Category, RvalueFunc}; |
04454e1e | 9 | use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; |
c295e0f8 | 10 | use rustc_hir::lang_items::LangItem; |
ba9703b0 | 11 | use rustc_middle::middle::region; |
353b0b11 | 12 | use rustc_middle::mir::interpret::Scalar; |
ba9703b0 | 13 | use rustc_middle::mir::AssertKind; |
6a06907d | 14 | use rustc_middle::mir::Place; |
ba9703b0 | 15 | use rustc_middle::mir::*; |
17df50a5 | 16 | use rustc_middle::thir::*; |
2b03887a | 17 | use rustc_middle::ty::cast::{mir_cast_kind, CastTy}; |
49aad941 | 18 | use rustc_middle::ty::layout::IntegerExt; |
ba9703b0 | 19 | use rustc_middle::ty::{self, Ty, UpvarSubsts}; |
dfeec247 | 20 | use rustc_span::Span; |
e9174d1e | 21 | |
dc9dc135 | 22 | impl<'a, 'tcx> Builder<'a, 'tcx> { |
60c5eb7d XL |
23 | /// Returns an rvalue suitable for use until the end of the current |
24 | /// scope expression. | |
25 | /// | |
26 | /// The operand returned from this function will *not be valid* after | |
27 | /// an ExprKind::Scope is passed, so please do *not* return it from | |
28 | /// functions to avoid bad miscompiles. | |
923072b8 | 29 | pub(crate) fn as_local_rvalue( |
6a06907d XL |
30 | &mut self, |
31 | block: BasicBlock, | |
17df50a5 | 32 | expr: &Expr<'tcx>, |
6a06907d | 33 | ) -> BlockAnd<Rvalue<'tcx>> { |
7cac9316 | 34 | let local_scope = self.local_scope(); |
fc512014 | 35 | self.as_rvalue(block, Some(local_scope), expr) |
8bb4bdeb XL |
36 | } |
37 | ||
e9174d1e | 38 | /// Compile `expr`, yielding an rvalue. |
923072b8 | 39 | pub(crate) fn as_rvalue( |
b7449926 XL |
40 | &mut self, |
41 | mut block: BasicBlock, | |
42 | scope: Option<region::Scope>, | |
17df50a5 | 43 | expr: &Expr<'tcx>, |
b7449926 | 44 | ) -> BlockAnd<Rvalue<'tcx>> { |
dfeec247 | 45 | debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr); |
e9174d1e SL |
46 | |
47 | let this = self; | |
48 | let expr_span = expr.span; | |
3157f602 | 49 | let source_info = this.source_info(expr_span); |
e9174d1e SL |
50 | |
51 | match expr.kind { | |
f9f354fc | 52 | ExprKind::ThreadLocalRef(did) => block.and(Rvalue::ThreadLocalRef(did)), |
dfeec247 | 53 | ExprKind::Scope { region_scope, lint_level, value } => { |
ea8adc8c | 54 | let region_scope = (region_scope, source_info); |
17df50a5 XL |
55 | this.in_scope(region_scope, lint_level, |this| { |
56 | this.as_rvalue(block, scope, &this.thir[value]) | |
57 | }) | |
e9174d1e SL |
58 | } |
59 | ExprKind::Repeat { value, count } => { | |
9ffffee4 | 60 | if Some(0) == count.try_eval_target_usize(this.tcx, this.param_env) { |
923072b8 FG |
61 | this.build_zero_repeat(block, value, scope, source_info) |
62 | } else { | |
63 | let value_operand = unpack!( | |
64 | block = this.as_operand( | |
65 | block, | |
66 | scope, | |
67 | &this.thir[value], | |
353b0b11 | 68 | LocalInfo::Boring, |
923072b8 FG |
69 | NeedsTemporary::No |
70 | ) | |
71 | ); | |
72 | block.and(Rvalue::Repeat(value_operand, count)) | |
73 | } | |
e9174d1e | 74 | } |
e9174d1e | 75 | ExprKind::Binary { op, lhs, rhs } => { |
04454e1e | 76 | let lhs = unpack!( |
353b0b11 FG |
77 | block = this.as_operand( |
78 | block, | |
79 | scope, | |
80 | &this.thir[lhs], | |
81 | LocalInfo::Boring, | |
82 | NeedsTemporary::Maybe | |
83 | ) | |
04454e1e FG |
84 | ); |
85 | let rhs = unpack!( | |
353b0b11 FG |
86 | block = this.as_operand( |
87 | block, | |
88 | scope, | |
89 | &this.thir[rhs], | |
90 | LocalInfo::Boring, | |
91 | NeedsTemporary::No | |
92 | ) | |
04454e1e | 93 | ); |
b7449926 | 94 | this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) |
e9174d1e SL |
95 | } |
96 | ExprKind::Unary { op, arg } => { | |
04454e1e | 97 | let arg = unpack!( |
353b0b11 FG |
98 | block = this.as_operand( |
99 | block, | |
100 | scope, | |
101 | &this.thir[arg], | |
102 | LocalInfo::Boring, | |
103 | NeedsTemporary::No | |
104 | ) | |
04454e1e | 105 | ); |
3157f602 | 106 | // Check for -MIN on signed integers |
6a06907d XL |
107 | if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() { |
108 | let bool_ty = this.tcx.types.bool; | |
3157f602 XL |
109 | |
110 | let minval = this.minval_literal(expr_span, expr.ty); | |
cc61c64b | 111 | let is_min = this.temp(bool_ty, expr_span); |
3157f602 | 112 | |
b7449926 XL |
113 | this.cfg.push_assign( |
114 | block, | |
115 | source_info, | |
ba9703b0 | 116 | is_min, |
94222f64 | 117 | Rvalue::BinaryOp(BinOp::Eq, Box::new((arg.to_copy(), minval))), |
b7449926 | 118 | ); |
3157f602 | 119 | |
b7449926 XL |
120 | block = this.assert( |
121 | block, | |
122 | Operand::Move(is_min), | |
123 | false, | |
f035d41b | 124 | AssertKind::OverflowNeg(arg.to_copy()), |
b7449926 XL |
125 | expr_span, |
126 | ); | |
3157f602 | 127 | } |
e9174d1e SL |
128 | block.and(Rvalue::UnaryOp(op, arg)) |
129 | } | |
3b2f2976 | 130 | ExprKind::Box { value } => { |
17df50a5 | 131 | let value = &this.thir[value]; |
c295e0f8 XL |
132 | let tcx = this.tcx; |
133 | ||
134 | // `exchange_malloc` is unsafe but box is safe, so need a new scope. | |
135 | let synth_scope = this.new_source_scope( | |
136 | expr_span, | |
137 | LintLevel::Inherited, | |
138 | Some(Safety::BuiltinUnsafe), | |
139 | ); | |
140 | let synth_info = SourceInfo { span: expr_span, scope: synth_scope }; | |
141 | ||
142 | let size = this.temp(tcx.types.usize, expr_span); | |
143 | this.cfg.push_assign( | |
144 | block, | |
145 | synth_info, | |
146 | size, | |
147 | Rvalue::NullaryOp(NullOp::SizeOf, value.ty), | |
148 | ); | |
149 | ||
150 | let align = this.temp(tcx.types.usize, expr_span); | |
151 | this.cfg.push_assign( | |
152 | block, | |
153 | synth_info, | |
154 | align, | |
155 | Rvalue::NullaryOp(NullOp::AlignOf, value.ty), | |
156 | ); | |
157 | ||
158 | // malloc some memory of suitable size and align: | |
159 | let exchange_malloc = Operand::function_handle( | |
160 | tcx, | |
161 | tcx.require_lang_item(LangItem::ExchangeMalloc, Some(expr_span)), | |
9c376795 | 162 | [], |
c295e0f8 XL |
163 | expr_span, |
164 | ); | |
165 | let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span); | |
166 | let success = this.cfg.start_new_block(); | |
167 | this.cfg.terminate( | |
168 | block, | |
169 | synth_info, | |
170 | TerminatorKind::Call { | |
171 | func: exchange_malloc, | |
172 | args: vec![Operand::Move(size), Operand::Move(align)], | |
923072b8 FG |
173 | destination: storage, |
174 | target: Some(success), | |
353b0b11 | 175 | unwind: UnwindAction::Continue, |
c295e0f8 XL |
176 | from_hir_call: false, |
177 | fn_span: expr_span, | |
178 | }, | |
179 | ); | |
180 | this.diverge_from(block); | |
181 | block = success; | |
182 | ||
ea8adc8c | 183 | // The `Box<T>` temporary created here is not a part of the HIR, |
fc512014 | 184 | // and therefore is not considered during generator auto-trait |
ea8adc8c | 185 | // determination. See the comment about `box` at `yield_in_scope`. |
f9f354fc | 186 | let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span).internal()); |
b7449926 XL |
187 | this.cfg.push( |
188 | block, | |
dfeec247 | 189 | Statement { source_info, kind: StatementKind::StorageLive(result) }, |
b7449926 | 190 | ); |
3b2f2976 XL |
191 | if let Some(scope) = scope { |
192 | // schedule a shallow free of that memory, lest we unwind: | |
dfeec247 | 193 | this.schedule_drop_storage_and_value(expr_span, scope, result); |
3b2f2976 XL |
194 | } |
195 | ||
c295e0f8 XL |
196 | // Transmute `*mut u8` to the box (thus far, uninitialized): |
197 | let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value.ty); | |
ba9703b0 | 198 | this.cfg.push_assign(block, source_info, Place::from(result), box_); |
3b2f2976 XL |
199 | |
200 | // initialize the box contents: | |
532ac7d7 | 201 | unpack!( |
6a06907d XL |
202 | block = this.expr_into_dest( |
203 | this.tcx.mk_place_deref(Place::from(result)), | |
204 | block, | |
205 | value | |
206 | ) | |
532ac7d7 | 207 | ); |
dc9dc135 | 208 | block.and(Rvalue::Use(Operand::Move(Place::from(result)))) |
e9174d1e SL |
209 | } |
210 | ExprKind::Cast { source } => { | |
923072b8 | 211 | let source = &this.thir[source]; |
064997fb FG |
212 | |
213 | // Casting an enum to an integer is equivalent to computing the discriminant and casting the | |
214 | // discriminant. Previously every backend had to repeat the logic for this operation. Now we | |
215 | // create all the steps directly in MIR with operations all backends need to support anyway. | |
216 | let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() { | |
217 | let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx); | |
f2b60f7d | 218 | let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not)); |
487cf647 | 219 | let layout = this.tcx.layout_of(this.param_env.and(source.ty)); |
064997fb FG |
220 | let discr = this.temp(discr_ty, source.span); |
221 | this.cfg.push_assign( | |
222 | block, | |
223 | source_info, | |
224 | discr, | |
f2b60f7d | 225 | Rvalue::Discriminant(temp.into()), |
064997fb | 226 | ); |
487cf647 FG |
227 | let (op,ty) = (Operand::Move(discr), discr_ty); |
228 | ||
49aad941 FG |
229 | if let Abi::Scalar(scalar) = layout.unwrap().abi |
230 | && !scalar.is_always_valid(&this.tcx) | |
231 | && let Primitive::Int(int_width, _signed) = scalar.primitive() | |
232 | { | |
233 | let unsigned_ty = int_width.to_ty(this.tcx, false); | |
234 | let unsigned_place = this.temp(unsigned_ty, expr_span); | |
235 | this.cfg.push_assign( | |
236 | block, | |
237 | source_info, | |
238 | unsigned_place, | |
239 | Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty)); | |
240 | ||
241 | let bool_ty = this.tcx.types.bool; | |
242 | let range = scalar.valid_range(&this.tcx); | |
243 | let merge_op = | |
244 | if range.start <= range.end { | |
245 | BinOp::BitAnd | |
246 | } else { | |
247 | BinOp::BitOr | |
248 | }; | |
249 | ||
250 | let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> { | |
251 | let range_val = | |
252 | ConstantKind::from_bits(this.tcx, range, ty::ParamEnv::empty().and(unsigned_ty)); | |
253 | let lit_op = this.literal_operand(expr.span, range_val); | |
254 | let is_bin_op = this.temp(bool_ty, expr_span); | |
255 | this.cfg.push_assign( | |
256 | block, | |
257 | source_info, | |
258 | is_bin_op, | |
259 | Rvalue::BinaryOp(bin_op, Box::new((Operand::Copy(unsigned_place), lit_op))), | |
260 | ); | |
261 | is_bin_op | |
262 | }; | |
263 | let assert_place = if range.start == 0 { | |
264 | comparer(range.end, BinOp::Le) | |
265 | } else { | |
266 | let start_place = comparer(range.start, BinOp::Ge); | |
267 | let end_place = comparer(range.end, BinOp::Le); | |
268 | let merge_place = this.temp(bool_ty, expr_span); | |
269 | this.cfg.push_assign( | |
270 | block, | |
271 | source_info, | |
272 | merge_place, | |
273 | Rvalue::BinaryOp(merge_op, Box::new((Operand::Move(start_place), Operand::Move(end_place)))), | |
274 | ); | |
275 | merge_place | |
276 | }; | |
277 | this.cfg.push( | |
278 | block, | |
279 | Statement { | |
280 | source_info, | |
281 | kind: StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( | |
282 | Operand::Move(assert_place), | |
283 | ))), | |
284 | }, | |
285 | ); | |
487cf647 FG |
286 | } |
287 | ||
288 | (op,ty) | |
064997fb | 289 | |
064997fb FG |
290 | } else { |
291 | let ty = source.ty; | |
292 | let source = unpack!( | |
353b0b11 | 293 | block = this.as_operand(block, scope, source, LocalInfo::Boring, NeedsTemporary::No) |
064997fb FG |
294 | ); |
295 | (source, ty) | |
296 | }; | |
297 | let from_ty = CastTy::from_ty(ty); | |
923072b8 | 298 | let cast_ty = CastTy::from_ty(expr.ty); |
f2b60f7d | 299 | debug!("ExprKind::Cast from_ty={from_ty:?}, cast_ty={:?}/{cast_ty:?}", expr.ty,); |
2b03887a | 300 | let cast_kind = mir_cast_kind(ty, expr.ty); |
923072b8 | 301 | block.and(Rvalue::Cast(cast_kind, source, expr.ty)) |
e9174d1e | 302 | } |
48663c56 | 303 | ExprKind::Pointer { cast, source } => { |
04454e1e | 304 | let source = unpack!( |
353b0b11 FG |
305 | block = this.as_operand( |
306 | block, | |
307 | scope, | |
308 | &this.thir[source], | |
309 | LocalInfo::Boring, | |
310 | NeedsTemporary::No | |
311 | ) | |
04454e1e | 312 | ); |
48663c56 | 313 | block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty)) |
e9174d1e | 314 | } |
17df50a5 | 315 | ExprKind::Array { ref fields } => { |
94b46f34 | 316 | // (*) We would (maybe) be closer to codegen if we |
e9174d1e SL |
317 | // handled this and other aggregate cases via |
318 | // `into()`, not `as_rvalue` -- in that case, instead | |
319 | // of generating | |
320 | // | |
321 | // let tmp1 = ...1; | |
322 | // let tmp2 = ...2; | |
323 | // dest = Rvalue::Aggregate(Foo, [tmp1, tmp2]) | |
324 | // | |
325 | // we could just generate | |
326 | // | |
327 | // dest.f = ...1; | |
328 | // dest.g = ...2; | |
329 | // | |
330 | // The problem is that then we would need to: | |
331 | // | |
332 | // (a) have a more complex mechanism for handling | |
333 | // partial cleanup; | |
334 | // (b) distinguish the case where the type `Foo` has a | |
335 | // destructor, in which case creating an instance | |
336 | // as a whole "arms" the destructor, and you can't | |
337 | // write individual fields; and, | |
338 | // (c) handle the case where the type Foo has no | |
339 | // fields. We don't want `let x: ();` to compile | |
340 | // to the same MIR as `let x = ();`. | |
341 | ||
342 | // first process the set of fields | |
6a06907d | 343 | let el_ty = expr.ty.sequence_element_type(this.tcx); |
353b0b11 | 344 | let fields: IndexVec<FieldIdx, _> = fields |
b7449926 | 345 | .into_iter() |
17df50a5 | 346 | .copied() |
04454e1e FG |
347 | .map(|f| { |
348 | unpack!( | |
349 | block = this.as_operand( | |
350 | block, | |
351 | scope, | |
352 | &this.thir[f], | |
353b0b11 | 353 | LocalInfo::Boring, |
04454e1e FG |
354 | NeedsTemporary::Maybe |
355 | ) | |
356 | ) | |
357 | }) | |
b7449926 | 358 | .collect(); |
e9174d1e | 359 | |
94222f64 | 360 | block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields)) |
e9174d1e | 361 | } |
17df50a5 | 362 | ExprKind::Tuple { ref fields } => { |
b7449926 | 363 | // see (*) above |
e9174d1e | 364 | // first process the set of fields |
353b0b11 | 365 | let fields: IndexVec<FieldIdx, _> = fields |
b7449926 | 366 | .into_iter() |
17df50a5 | 367 | .copied() |
04454e1e FG |
368 | .map(|f| { |
369 | unpack!( | |
370 | block = this.as_operand( | |
371 | block, | |
372 | scope, | |
373 | &this.thir[f], | |
353b0b11 | 374 | LocalInfo::Boring, |
04454e1e FG |
375 | NeedsTemporary::Maybe |
376 | ) | |
377 | ) | |
378 | }) | |
b7449926 | 379 | .collect(); |
e9174d1e | 380 | |
94222f64 | 381 | block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields)) |
e9174d1e | 382 | } |
f2b60f7d FG |
383 | ExprKind::Closure(box ClosureExpr { |
384 | closure_id, | |
385 | substs, | |
386 | ref upvars, | |
387 | movability, | |
388 | ref fake_reads, | |
389 | }) => { | |
6a06907d XL |
390 | // Convert the closure fake reads, if any, from `ExprRef` to mir `Place` |
391 | // and push the fake reads. | |
392 | // This must come before creating the operands. This is required in case | |
393 | // there is a fake read and a borrow of the same path, since otherwise the | |
394 | // fake read might interfere with the borrow. Consider an example like this | |
395 | // one: | |
396 | // ``` | |
397 | // let mut x = 0; | |
398 | // let c = || { | |
399 | // &mut x; // mutable borrow of `x` | |
400 | // match x { _ => () } // fake read of `x` | |
401 | // }; | |
402 | // ``` | |
cdc7bbd5 | 403 | // |
17df50a5 XL |
404 | for (thir_place, cause, hir_id) in fake_reads.into_iter() { |
405 | let place_builder = | |
406 | unpack!(block = this.as_place_builder(block, &this.thir[*thir_place])); | |
407 | ||
487cf647 | 408 | if let Some(mir_place) = place_builder.try_to_place(this) { |
17df50a5 XL |
409 | this.cfg.push_fake_read( |
410 | block, | |
411 | this.source_info(this.tcx.hir().span(*hir_id)), | |
412 | *cause, | |
413 | mir_place, | |
414 | ); | |
6a06907d XL |
415 | } |
416 | } | |
417 | ||
94b46f34 | 418 | // see (*) above |
353b0b11 | 419 | let operands: IndexVec<FieldIdx, _> = upvars |
8faf50e0 | 420 | .into_iter() |
17df50a5 | 421 | .copied() |
8faf50e0 | 422 | .map(|upvar| { |
17df50a5 | 423 | let upvar = &this.thir[upvar]; |
8faf50e0 XL |
424 | match Category::of(&upvar.kind) { |
425 | // Use as_place to avoid creating a temporary when | |
426 | // moving a variable into a closure, so that | |
427 | // borrowck knows which variables to mark as being | |
428 | // used as mut. This is OK here because the upvar | |
429 | // expressions have no side effects and act on | |
430 | // disjoint places. | |
431 | // This occurs when capturing by copy/move, while | |
432 | // by reference captures use as_operand | |
433 | Some(Category::Place) => { | |
434 | let place = unpack!(block = this.as_place(block, upvar)); | |
435 | this.consume_by_copy_or_move(place) | |
436 | } | |
437 | _ => { | |
438 | // Turn mutable borrow captures into unique | |
439 | // borrow captures when capturing an immutable | |
440 | // variable. This is sound because the mutation | |
441 | // that caused the capture will cause an error. | |
442 | match upvar.kind { | |
443 | ExprKind::Borrow { | |
b7449926 | 444 | borrow_kind: |
dfeec247 | 445 | BorrowKind::Mut { allow_two_phase_borrow: false }, |
8faf50e0 | 446 | arg, |
b7449926 XL |
447 | } => unpack!( |
448 | block = this.limit_capture_mutability( | |
17df50a5 XL |
449 | upvar.span, |
450 | upvar.ty, | |
451 | scope, | |
452 | block, | |
453 | &this.thir[arg], | |
b7449926 XL |
454 | ) |
455 | ), | |
c295e0f8 | 456 | _ => { |
04454e1e FG |
457 | unpack!( |
458 | block = this.as_operand( | |
459 | block, | |
460 | scope, | |
461 | upvar, | |
353b0b11 | 462 | LocalInfo::Boring, |
04454e1e FG |
463 | NeedsTemporary::Maybe |
464 | ) | |
465 | ) | |
c295e0f8 | 466 | } |
8faf50e0 XL |
467 | } |
468 | } | |
469 | } | |
dfeec247 XL |
470 | }) |
471 | .collect(); | |
6a06907d | 472 | |
94b46f34 XL |
473 | let result = match substs { |
474 | UpvarSubsts::Generator(substs) => { | |
48663c56 XL |
475 | // We implicitly set the discriminant to 0. See |
476 | // librustc_mir/transform/deaggregator.rs for details. | |
94b46f34 | 477 | let movability = movability.unwrap(); |
9ffffee4 FG |
478 | Box::new(AggregateKind::Generator( |
479 | closure_id.to_def_id(), | |
480 | substs, | |
481 | movability, | |
482 | )) | |
94222f64 XL |
483 | } |
484 | UpvarSubsts::Closure(substs) => { | |
9ffffee4 | 485 | Box::new(AggregateKind::Closure(closure_id.to_def_id(), substs)) |
94b46f34 | 486 | } |
ea8adc8c XL |
487 | }; |
488 | block.and(Rvalue::Aggregate(result, operands)) | |
e9174d1e | 489 | } |
b7449926 | 490 | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { |
a1dfa0c6 | 491 | block = unpack!(this.stmt_expr(block, expr, None)); |
94222f64 | 492 | block.and(Rvalue::Use(Operand::Constant(Box::new(Constant { |
ba9703b0 XL |
493 | span: expr_span, |
494 | user_ty: None, | |
04454e1e | 495 | literal: ConstantKind::zero_sized(this.tcx.types.unit), |
94222f64 | 496 | })))) |
a7813a04 | 497 | } |
04454e1e | 498 | |
49aad941 FG |
499 | ExprKind::OffsetOf { container, fields } => { |
500 | block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields), container)) | |
501 | } | |
502 | ||
04454e1e | 503 | ExprKind::Literal { .. } |
5e7ed085 FG |
504 | | ExprKind::NamedConst { .. } |
505 | | ExprKind::NonHirLiteral { .. } | |
064997fb | 506 | | ExprKind::ZstLiteral { .. } |
5e7ed085 | 507 | | ExprKind::ConstParam { .. } |
29967ef6 | 508 | | ExprKind::ConstBlock { .. } |
04454e1e FG |
509 | | ExprKind::StaticRef { .. } => { |
510 | let constant = this.as_constant(expr); | |
511 | block.and(Rvalue::Use(Operand::Constant(Box::new(constant)))) | |
512 | } | |
513 | ||
514 | ExprKind::Yield { .. } | |
b7449926 XL |
515 | | ExprKind::Block { .. } |
516 | | ExprKind::Match { .. } | |
5869c6ff | 517 | | ExprKind::If { .. } |
b7449926 | 518 | | ExprKind::NeverToAny { .. } |
48663c56 | 519 | | ExprKind::Use { .. } |
60c5eb7d | 520 | | ExprKind::Borrow { .. } |
dfeec247 | 521 | | ExprKind::AddressOf { .. } |
60c5eb7d | 522 | | ExprKind::Adt { .. } |
b7449926 XL |
523 | | ExprKind::Loop { .. } |
524 | | ExprKind::LogicalOp { .. } | |
525 | | ExprKind::Call { .. } | |
526 | | ExprKind::Field { .. } | |
94222f64 | 527 | | ExprKind::Let { .. } |
b7449926 XL |
528 | | ExprKind::Deref { .. } |
529 | | ExprKind::Index { .. } | |
530 | | ExprKind::VarRef { .. } | |
fc512014 | 531 | | ExprKind::UpvarRef { .. } |
b7449926 XL |
532 | | ExprKind::Break { .. } |
533 | | ExprKind::Continue { .. } | |
534 | | ExprKind::Return { .. } | |
f9f354fc | 535 | | ExprKind::InlineAsm { .. } |
0bf4aa26 XL |
536 | | ExprKind::PlaceTypeAscription { .. } |
537 | | ExprKind::ValueTypeAscription { .. } => { | |
e9174d1e SL |
538 | // these do not have corresponding `Rvalue` variants, |
539 | // so make an operand and then return that | |
5869c6ff XL |
540 | debug_assert!(!matches!( |
541 | Category::of(&expr.kind), | |
04454e1e | 542 | Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant) |
5869c6ff | 543 | )); |
353b0b11 FG |
544 | let operand = unpack!( |
545 | block = | |
546 | this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No) | |
547 | ); | |
e9174d1e SL |
548 | block.and(Rvalue::Use(operand)) |
549 | } | |
550 | } | |
551 | } | |
3157f602 | 552 | |
923072b8 | 553 | pub(crate) fn build_binary_op( |
b7449926 XL |
554 | &mut self, |
555 | mut block: BasicBlock, | |
556 | op: BinOp, | |
557 | span: Span, | |
558 | ty: Ty<'tcx>, | |
559 | lhs: Operand<'tcx>, | |
560 | rhs: Operand<'tcx>, | |
561 | ) -> BlockAnd<Rvalue<'tcx>> { | |
3157f602 | 562 | let source_info = self.source_info(span); |
6a06907d | 563 | let bool_ty = self.tcx.types.bool; |
353b0b11 FG |
564 | let rvalue = match op { |
565 | BinOp::Add | BinOp::Sub | BinOp::Mul if self.check_overflow && ty.is_integral() => { | |
566 | let result_tup = self.tcx.mk_tup(&[ty, bool_ty]); | |
567 | let result_value = self.temp(result_tup, span); | |
3157f602 | 568 | |
353b0b11 FG |
569 | self.cfg.push_assign( |
570 | block, | |
571 | source_info, | |
572 | result_value, | |
573 | Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))), | |
574 | ); | |
575 | let val_fld = FieldIdx::new(0); | |
576 | let of_fld = FieldIdx::new(1); | |
3157f602 | 577 | |
353b0b11 FG |
578 | let tcx = self.tcx; |
579 | let val = tcx.mk_place_field(result_value, val_fld, ty); | |
580 | let of = tcx.mk_place_field(result_value, of_fld, bool_ty); | |
3157f602 | 581 | |
353b0b11 FG |
582 | let err = AssertKind::Overflow(op, lhs, rhs); |
583 | block = self.assert(block, Operand::Move(of), false, err, span); | |
3157f602 | 584 | |
353b0b11 FG |
585 | Rvalue::Use(Operand::Move(val)) |
586 | } | |
587 | BinOp::Shl | BinOp::Shr if self.check_overflow && ty.is_integral() => { | |
588 | // For an unsigned RHS, the shift is in-range for `rhs < bits`. | |
589 | // For a signed RHS, `IntToInt` cast to the equivalent unsigned | |
590 | // type and do that same comparison. Because the type is the | |
591 | // same size, there's no negative shift amount that ends up | |
592 | // overlapping with valid ones, thus it catches negatives too. | |
593 | let (lhs_size, _) = ty.int_size_and_signed(self.tcx); | |
594 | let rhs_ty = rhs.ty(&self.local_decls, self.tcx); | |
595 | let (rhs_size, _) = rhs_ty.int_size_and_signed(self.tcx); | |
596 | ||
597 | let (unsigned_rhs, unsigned_ty) = match rhs_ty.kind() { | |
598 | ty::Uint(_) => (rhs.to_copy(), rhs_ty), | |
599 | ty::Int(int_width) => { | |
600 | let uint_ty = self.tcx.mk_mach_uint(int_width.to_unsigned()); | |
601 | let rhs_temp = self.temp(uint_ty, span); | |
602 | self.cfg.push_assign( | |
603 | block, | |
604 | source_info, | |
605 | rhs_temp, | |
606 | Rvalue::Cast(CastKind::IntToInt, rhs.to_copy(), uint_ty), | |
607 | ); | |
608 | (Operand::Move(rhs_temp), uint_ty) | |
609 | } | |
610 | _ => unreachable!("only integers are shiftable"), | |
611 | }; | |
612 | ||
613 | // This can't overflow because the largest shiftable types are 128-bit, | |
614 | // which fits in `u8`, the smallest possible `unsigned_ty`. | |
615 | // (And `from_uint` will `bug!` if that's ever no longer true.) | |
616 | let lhs_bits = Operand::const_from_scalar( | |
617 | self.tcx, | |
618 | unsigned_ty, | |
619 | Scalar::from_uint(lhs_size.bits(), rhs_size), | |
620 | span, | |
621 | ); | |
622 | ||
623 | let inbounds = self.temp(bool_ty, span); | |
624 | self.cfg.push_assign( | |
625 | block, | |
626 | source_info, | |
627 | inbounds, | |
628 | Rvalue::BinaryOp(BinOp::Lt, Box::new((unsigned_rhs, lhs_bits))), | |
629 | ); | |
630 | ||
631 | let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy()); | |
632 | block = self.assert(block, Operand::Move(inbounds), true, overflow_err, span); | |
633 | Rvalue::BinaryOp(op, Box::new((lhs, rhs))) | |
634 | } | |
635 | BinOp::Div | BinOp::Rem if ty.is_integral() => { | |
3157f602 XL |
636 | // Checking division and remainder is more complex, since we 1. always check |
637 | // and 2. there are two possible failure cases, divide-by-zero and overflow. | |
638 | ||
416331ca | 639 | let zero_err = if op == BinOp::Div { |
f035d41b | 640 | AssertKind::DivisionByZero(lhs.to_copy()) |
3157f602 | 641 | } else { |
f035d41b | 642 | AssertKind::RemainderByZero(lhs.to_copy()) |
3157f602 | 643 | }; |
f035d41b | 644 | let overflow_err = AssertKind::Overflow(op, lhs.to_copy(), rhs.to_copy()); |
3157f602 XL |
645 | |
646 | // Check for / 0 | |
cc61c64b | 647 | let is_zero = self.temp(bool_ty, span); |
3157f602 | 648 | let zero = self.zero_literal(span, ty); |
b7449926 XL |
649 | self.cfg.push_assign( |
650 | block, | |
651 | source_info, | |
ba9703b0 | 652 | is_zero, |
94222f64 | 653 | Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), zero))), |
b7449926 | 654 | ); |
3157f602 | 655 | |
b7449926 | 656 | block = self.assert(block, Operand::Move(is_zero), false, zero_err, span); |
3157f602 XL |
657 | |
658 | // We only need to check for the overflow in one case: | |
659 | // MIN / -1, and only for signed values. | |
660 | if ty.is_signed() { | |
661 | let neg_1 = self.neg_1_literal(span, ty); | |
662 | let min = self.minval_literal(span, ty); | |
663 | ||
cc61c64b | 664 | let is_neg_1 = self.temp(bool_ty, span); |
b7449926 XL |
665 | let is_min = self.temp(bool_ty, span); |
666 | let of = self.temp(bool_ty, span); | |
3157f602 XL |
667 | |
668 | // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead | |
669 | ||
b7449926 XL |
670 | self.cfg.push_assign( |
671 | block, | |
672 | source_info, | |
ba9703b0 | 673 | is_neg_1, |
94222f64 | 674 | Rvalue::BinaryOp(BinOp::Eq, Box::new((rhs.to_copy(), neg_1))), |
b7449926 XL |
675 | ); |
676 | self.cfg.push_assign( | |
677 | block, | |
678 | source_info, | |
ba9703b0 | 679 | is_min, |
94222f64 | 680 | Rvalue::BinaryOp(BinOp::Eq, Box::new((lhs.to_copy(), min))), |
b7449926 | 681 | ); |
3157f602 | 682 | |
ff7c6d11 XL |
683 | let is_neg_1 = Operand::Move(is_neg_1); |
684 | let is_min = Operand::Move(is_min); | |
b7449926 XL |
685 | self.cfg.push_assign( |
686 | block, | |
687 | source_info, | |
ba9703b0 | 688 | of, |
94222f64 | 689 | Rvalue::BinaryOp(BinOp::BitAnd, Box::new((is_neg_1, is_min))), |
b7449926 | 690 | ); |
3157f602 | 691 | |
b7449926 | 692 | block = self.assert(block, Operand::Move(of), false, overflow_err, span); |
3157f602 | 693 | } |
3157f602 | 694 | |
353b0b11 FG |
695 | Rvalue::BinaryOp(op, Box::new((lhs, rhs))) |
696 | } | |
697 | _ => Rvalue::BinaryOp(op, Box::new((lhs, rhs))), | |
698 | }; | |
699 | block.and(rvalue) | |
3157f602 XL |
700 | } |
701 | ||
923072b8 FG |
702 | fn build_zero_repeat( |
703 | &mut self, | |
704 | mut block: BasicBlock, | |
705 | value: ExprId, | |
706 | scope: Option<region::Scope>, | |
707 | outer_source_info: SourceInfo, | |
708 | ) -> BlockAnd<Rvalue<'tcx>> { | |
709 | let this = self; | |
710 | let value = &this.thir[value]; | |
711 | let elem_ty = value.ty; | |
712 | if let Some(Category::Constant) = Category::of(&value.kind) { | |
713 | // Repeating a const does nothing | |
714 | } else { | |
715 | // For a non-const, we may need to generate an appropriate `Drop` | |
353b0b11 FG |
716 | let value_operand = unpack!( |
717 | block = this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No) | |
718 | ); | |
923072b8 FG |
719 | if let Operand::Move(to_drop) = value_operand { |
720 | let success = this.cfg.start_new_block(); | |
721 | this.cfg.terminate( | |
722 | block, | |
723 | outer_source_info, | |
353b0b11 FG |
724 | TerminatorKind::Drop { |
725 | place: to_drop, | |
726 | target: success, | |
727 | unwind: UnwindAction::Continue, | |
49aad941 | 728 | replace: false, |
353b0b11 | 729 | }, |
923072b8 FG |
730 | ); |
731 | this.diverge_from(block); | |
732 | block = success; | |
733 | } | |
734 | this.record_operands_moved(&[value_operand]); | |
735 | } | |
353b0b11 | 736 | block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), IndexVec::new())) |
923072b8 FG |
737 | } |
738 | ||
8faf50e0 XL |
739 | fn limit_capture_mutability( |
740 | &mut self, | |
741 | upvar_span: Span, | |
742 | upvar_ty: Ty<'tcx>, | |
743 | temp_lifetime: Option<region::Scope>, | |
744 | mut block: BasicBlock, | |
17df50a5 | 745 | arg: &Expr<'tcx>, |
8faf50e0 XL |
746 | ) -> BlockAnd<Operand<'tcx>> { |
747 | let this = self; | |
748 | ||
749 | let source_info = this.source_info(upvar_span); | |
f9f354fc | 750 | let temp = this.local_decls.push(LocalDecl::new(upvar_ty, upvar_span)); |
8faf50e0 | 751 | |
dfeec247 | 752 | this.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); |
8faf50e0 | 753 | |
fc512014 XL |
754 | let arg_place_builder = unpack!(block = this.as_place_builder(block, arg)); |
755 | ||
756 | let mutability = match arg_place_builder.base() { | |
757 | // We are capturing a path that starts off a local variable in the parent. | |
758 | // The mutability of the current capture is same as the mutability | |
759 | // of the local declaration in the parent. | |
5869c6ff | 760 | PlaceBase::Local(local) => this.local_decls[local].mutability, |
fc512014 XL |
761 | // Parent is a closure and we are capturing a path that is captured |
762 | // by the parent itself. The mutability of the current capture | |
763 | // is same as that of the capture in the parent closure. | |
764 | PlaceBase::Upvar { .. } => { | |
487cf647 | 765 | let enclosing_upvars_resolved = arg_place_builder.to_place(this); |
fc512014 XL |
766 | |
767 | match enclosing_upvars_resolved.as_ref() { | |
5869c6ff XL |
768 | PlaceRef { |
769 | local, | |
770 | projection: &[ProjectionElem::Field(upvar_index, _), ..], | |
771 | } | |
fc512014 XL |
772 | | PlaceRef { |
773 | local, | |
5869c6ff XL |
774 | projection: |
775 | &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..], | |
776 | } => { | |
777 | // Not in a closure | |
778 | debug_assert!( | |
17df50a5 | 779 | local == ty::CAPTURE_STRUCT_LOCAL, |
5869c6ff XL |
780 | "Expected local to be Local(1), found {:?}", |
781 | local | |
782 | ); | |
783 | // Not in a closure | |
784 | debug_assert!( | |
f2b60f7d FG |
785 | this.upvars.len() > upvar_index.index(), |
786 | "Unexpected capture place, upvars={:#?}, upvar_index={:?}", | |
787 | this.upvars, | |
5869c6ff XL |
788 | upvar_index |
789 | ); | |
f2b60f7d | 790 | this.upvars[upvar_index.index()].mutability |
5869c6ff | 791 | } |
fc512014 XL |
792 | _ => bug!("Unexpected capture place"), |
793 | } | |
8faf50e0 | 794 | } |
8faf50e0 XL |
795 | }; |
796 | ||
797 | let borrow_kind = match mutability { | |
798 | Mutability::Not => BorrowKind::Unique, | |
dfeec247 | 799 | Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, |
8faf50e0 XL |
800 | }; |
801 | ||
487cf647 | 802 | let arg_place = arg_place_builder.to_place(this); |
fc512014 | 803 | |
8faf50e0 XL |
804 | this.cfg.push_assign( |
805 | block, | |
806 | source_info, | |
ba9703b0 | 807 | Place::from(temp), |
6a06907d | 808 | Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place), |
8faf50e0 XL |
809 | ); |
810 | ||
fc512014 XL |
811 | // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why |
812 | // this can be `None`. | |
8faf50e0 | 813 | if let Some(temp_lifetime) = temp_lifetime { |
dfeec247 | 814 | this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp); |
8faf50e0 XL |
815 | } |
816 | ||
dc9dc135 | 817 | block.and(Operand::Move(Place::from(temp))) |
8faf50e0 XL |
818 | } |
819 | ||
3157f602 | 820 | // Helper to get a `-1` value of the appropriate type |
ea8adc8c | 821 | fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { |
dc9dc135 | 822 | let param_ty = ty::ParamEnv::empty().and(ty); |
c295e0f8 | 823 | let size = self.tcx.layout_of(param_ty).unwrap().size; |
04454e1e | 824 | let literal = ConstantKind::from_bits(self.tcx, size.unsigned_int_max(), param_ty); |
3157f602 | 825 | |
e1599b0c | 826 | self.literal_operand(span, literal) |
3157f602 XL |
827 | } |
828 | ||
829 | // Helper to get the minimum value of the appropriate type | |
ea8adc8c | 830 | fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { |
0531ce1d | 831 | assert!(ty.is_signed()); |
dc9dc135 | 832 | let param_ty = ty::ParamEnv::empty().and(ty); |
6a06907d | 833 | let bits = self.tcx.layout_of(param_ty).unwrap().size.bits(); |
0531ce1d | 834 | let n = 1 << (bits - 1); |
04454e1e | 835 | let literal = ConstantKind::from_bits(self.tcx, n, param_ty); |
3157f602 | 836 | |
e1599b0c | 837 | self.literal_operand(span, literal) |
3157f602 | 838 | } |
e9174d1e | 839 | } |