1use std::{fmt::Write, iter, mem};
4
5use base_db::Crate;
6use hir_def::{
7 AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule,
8 ItemContainerId, LocalFieldId, Lookup, TraitId,
9 expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path},
10 hir::{
11 ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId,
12 LabelId, Literal, MatchArm, Pat, PatId, RecordLitField, RecordSpread,
13 generics::GenericParams,
14 },
15 item_tree::FieldsShape,
16 lang_item::LangItems,
17 resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
18 signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
19};
20use hir_expand::name::Name;
21use itertools::{EitherOrBoth, Itertools};
22use la_arena::{ArenaMap, RawIdx};
23use rustc_apfloat::Float;
24use rustc_hash::FxHashMap;
25use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
26use salsa::Update;
27use span::{Edition, FileId};
28use syntax::TextRange;
29
30use crate::{
31 Adjust, Adjustment, AutoBorrow, CallableDefId, InferBodyId, ParamEnvAndCrate,
32 consteval::ConstEvalError,
33 db::{GeneralConstId, HirDatabase, InternedClosure, InternedClosureId},
34 display::{DisplayTarget, HirDisplay, hir_display_with_store},
35 generics::generics,
36 infer::{
37 CaptureSourceStack, CapturedPlace, UpvarCapture,
38 cast::CastTy,
39 closure::analysis::expr_use_visitor::{
40 Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
41 },
42 },
43 inhabitedness::is_ty_uninhabited_from,
44 layout::LayoutError,
45 method_resolution::CandidateId,
46 mir::{
47 AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Expr,
48 FieldIndex, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan,
49 Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue,
50 Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId,
51 return_slot,
52 },
53 next_solver::{
54 Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind,
55 TypingMode, UnevaluatedConst,
56 abi::Safety,
57 infer::{DbInternerInferExt, InferCtxt},
58 },
59};
60
61use super::{OperandKind, PlaceRef};
62
63mod as_place;
64mod pattern_matching;
65#[cfg(test)]
66mod tests;
67
68#[derive(Debug, Clone)]
69struct LoopBlocks {
70 begin: BasicBlockId,
71 end: Option<BasicBlockId>,
73 place: Place,
74 drop_scope_index: usize,
75}
76
77#[derive(Debug, Clone, Default)]
78struct DropScope {
79 locals: Vec<LocalId>,
81}
82
83struct MirLowerCtx<'a, 'db> {
84 result: MirBody<'db>,
85 owner: InferBodyId<'db>,
86 store_owner: ExpressionStoreOwnerId,
87 current_loop_blocks: Option<LoopBlocks>,
88 labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
89 discr_temp: Option<Place>,
90 db: &'db dyn HirDatabase,
91 store: &'a ExpressionStore,
92 infer: &'a InferenceResult<'db>,
93 types: &'db crate::next_solver::DefaultAny<'db>,
94 resolver: Resolver<'db>,
95 drop_scopes: Vec<DropScope>,
96 env: ParamEnv<'db>,
97 infcx: InferCtxt<'db>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Update)]
102pub enum MirLowerError<'db> {
103 ConstEvalError(Box<str>, Box<ConstEvalError<'db>>),
104 LayoutError(LayoutError),
105 IncompleteExpr,
106 IncompletePattern,
107 TraitFunctionDefinition(TraitId, Name),
109 UnresolvedName(String),
110 RecordLiteralWithoutPath,
111 UnresolvedMethod(String),
112 UnresolvedField,
113 UnsizedTemporary(StoredTy),
114 MissingFunctionDefinition(InferBodyId<'db>, ExprId),
115 HasErrors,
116 TypeError(&'static str),
118 NotSupported(String),
119 ContinueWithoutLoop,
120 BreakWithoutLoop,
121 Loop,
122 ImplementationError(String),
124 LangItemNotFound,
125 MutatingRvalue,
126 UnresolvedLabel,
127 UnresolvedUpvar(Place),
128 InaccessibleLocal,
129
130 GenericArgNotProvided(GenericParamId, StoredGenericArgs),
132}
133
134struct DropScopeToken;
136impl DropScopeToken {
137 fn pop_and_drop<'db>(
138 self,
139 ctx: &mut MirLowerCtx<'_, 'db>,
140 current: BasicBlockId,
141 span: MirSpan,
142 ) -> BasicBlockId {
143 std::mem::forget(self);
144 ctx.pop_drop_scope_internal(current, span)
145 }
146
147 fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_, '_>) {
152 std::mem::forget(self);
153 ctx.pop_drop_scope_assume_dropped_internal();
154 }
155}
156
157impl Drop for DropScopeToken {
158 fn drop(&mut self) {}
159}
160
161impl MirLowerError<'_> {
173 pub fn pretty_print(
174 &self,
175 f: &mut String,
176 db: &dyn HirDatabase,
177 span_formatter: impl Fn(FileId, TextRange) -> String,
178 display_target: DisplayTarget,
179 ) -> std::result::Result<(), std::fmt::Error> {
180 match self {
181 MirLowerError::ConstEvalError(name, e) => {
182 writeln!(f, "In evaluating constant {name}")?;
183 match &**e {
184 ConstEvalError::MirLowerError(e) => {
185 e.pretty_print(f, db, span_formatter, display_target)?
186 }
187 ConstEvalError::MirEvalError(e) => {
188 e.pretty_print(f, db, span_formatter, display_target)?
189 }
190 }
191 }
192 MirLowerError::MissingFunctionDefinition(owner, it) => {
193 let owner = owner.expression_store_owner(db);
194 let store = ExpressionStore::of(db, owner);
195 writeln!(
196 f,
197 "Missing function definition for {}",
198 hir_def::expr_store::pretty::print_expr_hir(
199 db,
200 store,
201 owner,
202 *it,
203 display_target.edition
204 )
205 )?;
206 }
207 MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?,
208 MirLowerError::GenericArgNotProvided(id, subst) => {
209 let param_name = match *id {
210 GenericParamId::TypeParamId(id) => {
211 GenericParams::of(db, id.parent())[id.local_id()].name().cloned()
212 }
213 GenericParamId::ConstParamId(id) => {
214 GenericParams::of(db, id.parent())[id.local_id()].name().cloned()
215 }
216 GenericParamId::LifetimeParamId(id) => {
217 Some(GenericParams::of(db, id.parent)[id.local_id].name.clone())
218 }
219 };
220 writeln!(
221 f,
222 "Generic arg not provided for {}",
223 param_name.unwrap_or(Name::missing()).display(db, display_target.edition)
224 )?;
225 writeln!(f, "Provided args: [")?;
226 for g in subst.as_ref() {
227 write!(f, " {},", g.display(db, display_target))?;
228 }
229 writeln!(f, "]")?;
230 }
231 MirLowerError::LayoutError(_)
232 | MirLowerError::UnsizedTemporary(_)
233 | MirLowerError::IncompleteExpr
234 | MirLowerError::IncompletePattern
235 | MirLowerError::InaccessibleLocal
236 | MirLowerError::TraitFunctionDefinition(_, _)
237 | MirLowerError::UnresolvedName(_)
238 | MirLowerError::RecordLiteralWithoutPath
239 | MirLowerError::UnresolvedMethod(_)
240 | MirLowerError::UnresolvedField
241 | MirLowerError::TypeError(_)
242 | MirLowerError::NotSupported(_)
243 | MirLowerError::ContinueWithoutLoop
244 | MirLowerError::BreakWithoutLoop
245 | MirLowerError::Loop
246 | MirLowerError::ImplementationError(_)
247 | MirLowerError::LangItemNotFound
248 | MirLowerError::MutatingRvalue
249 | MirLowerError::UnresolvedLabel
250 | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{self:?}")?,
251 }
252 Ok(())
253 }
254}
255
256macro_rules! not_supported {
257 ($it: expr) => {
258 return Err(MirLowerError::NotSupported(format!($it)))
259 };
260}
261
262macro_rules! implementation_error {
263 ($it: expr) => {{
264 ::stdx::never!("MIR lower implementation bug: {}", format!($it));
265 return Err(MirLowerError::ImplementationError(format!($it)));
266 }};
267}
268
269impl From<LayoutError> for MirLowerError<'_> {
270 fn from(value: LayoutError) -> Self {
271 MirLowerError::LayoutError(value)
272 }
273}
274
275impl MirLowerError<'_> {
276 fn unresolved_path(
277 db: &dyn HirDatabase,
278 p: &Path,
279 display_target: DisplayTarget,
280 owner: ExpressionStoreOwnerId,
281 store: &ExpressionStore,
282 ) -> Self {
283 Self::UnresolvedName(
284 hir_display_with_store(p, owner, store).display(db, display_target).to_string(),
285 )
286 }
287}
288
289type Result<'db, T> = std::result::Result<T, MirLowerError<'db>>;
290
291impl<'a, 'db> MirLowerCtx<'a, 'db> {
292 fn new(
293 db: &'db dyn HirDatabase,
294 owner: InferBodyId<'db>,
295 store: &'a ExpressionStore,
296 infer: &'a InferenceResult<'db>,
297 ) -> Self {
298 let mut basic_blocks = Arena::new();
299 let start_block = basic_blocks.alloc(BasicBlock {
300 statements: vec![],
301 terminator: None,
302 is_cleanup: false,
303 });
304 let locals = Arena::new();
305 let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
306 let mir = MirBody {
307 basic_blocks,
308 locals,
309 start_block,
310 binding_locals,
311 upvar_locals: FxHashMap::default(),
312 param_locals: vec![],
313 owner,
314 closures: vec![],
315 };
316 let store_owner = owner.expression_store_owner(db);
317 let resolver = owner.resolver(db);
318 let env = db.trait_environment(owner.generic_def(db));
319 let interner = DbInterner::new_with(db, resolver.krate());
320 let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
322
323 MirLowerCtx {
324 result: mir,
325 db,
326 infer,
327 store,
328 types: crate::next_solver::default_types(db),
329 owner,
330 store_owner,
331 resolver,
332 current_loop_blocks: None,
333 labeled_loop_blocks: Default::default(),
334 discr_temp: None,
335 drop_scopes: vec![DropScope::default()],
336 env,
337 infcx,
338 }
339 }
340
341 #[inline]
342 fn interner(&self) -> DbInterner<'db> {
343 self.infcx.interner
344 }
345
346 #[inline]
347 fn lang_items(&self) -> &'db LangItems {
348 self.infcx.interner.lang_items()
349 }
350
351 fn temp(&mut self, ty: Ty<'db>, current: BasicBlockId, span: MirSpan) -> Result<'db, LocalId> {
352 if matches!(ty.kind(), TyKind::Slice(_) | TyKind::Dynamic(..)) {
353 return Err(MirLowerError::UnsizedTemporary(ty.store()));
354 }
355 let l = self.result.locals.alloc(Local { ty: ty.store() });
356 self.push_storage_live_for_local(l, current, span)?;
357 Ok(l)
358 }
359
360 fn lower_expr_to_some_operand(
361 &mut self,
362 expr_id: ExprId,
363 current: BasicBlockId,
364 ) -> Result<'db, Option<(Operand, BasicBlockId)>> {
365 if !self.has_adjustments(expr_id)
366 && let Expr::Literal(l) = &self.store[expr_id]
367 {
368 let ty = self.expr_ty_without_adjust(expr_id);
369 return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
370 }
371 let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
372 return Ok(None);
373 };
374 Ok(Some((
375 Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) },
376 current,
377 )))
378 }
379
380 fn lower_expr_to_place_with_adjust(
381 &mut self,
382 expr_id: ExprId,
383 place: PlaceRef<'db>,
384 current: BasicBlockId,
385 adjustments: &[Adjustment],
386 ) -> Result<'db, Option<BasicBlockId>> {
387 match adjustments.split_last() {
388 Some((last, rest)) => match &last.kind {
389 Adjust::NeverToAny => {
390 let temp = self.temp(self.types.types.never, current, MirSpan::Unknown)?;
391 self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
392 }
393 Adjust::Deref(_) => {
394 let Some((p, current)) =
395 self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)?
396 else {
397 return Ok(None);
398 };
399 self.push_assignment(
400 current,
401 place,
402 Operand { kind: OperandKind::Copy(p.store()), span: None }.into(),
403 expr_id.into(),
404 );
405 Ok(Some(current))
406 }
407 Adjust::Borrow(AutoBorrow::Ref(m)) => self.lower_expr_to_place_with_borrow_adjust(
408 expr_id,
409 place,
410 current,
411 rest,
412 (*m).into(),
413 ),
414 Adjust::Borrow(AutoBorrow::RawPtr(m)) => {
415 self.lower_expr_to_place_with_borrow_adjust(expr_id, place, current, rest, *m)
416 }
417 Adjust::Pointer(cast) => {
418 let Some((p, current)) =
419 self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
420 else {
421 return Ok(None);
422 };
423 self.push_assignment(
424 current,
425 place,
426 Rvalue::Cast(
427 CastKind::PointerCoercion(*cast),
428 Operand { kind: OperandKind::Copy(p.store()), span: None },
429 last.target.clone(),
430 ),
431 expr_id.into(),
432 );
433 Ok(Some(current))
434 }
435 },
436 None => self.lower_expr_to_place_without_adjust(expr_id, place, current),
437 }
438 }
439
440 fn lower_expr_to_place_with_borrow_adjust(
441 &mut self,
442 expr_id: ExprId,
443 place: PlaceRef<'db>,
444 current: BasicBlockId,
445 rest: &[Adjustment],
446 m: Mutability,
447 ) -> Result<'db, Option<BasicBlockId>> {
448 let Some((p, current)) =
449 self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
450 else {
451 return Ok(None);
452 };
453 let bk = BorrowKind::from_rustc_mutability(m);
454 self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into());
455 Ok(Some(current))
456 }
457
458 fn lower_expr_to_place(
459 &mut self,
460 expr_id: ExprId,
461 place: PlaceRef<'db>,
462 prev_block: BasicBlockId,
463 ) -> Result<'db, Option<BasicBlockId>> {
464 if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) {
465 return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments);
466 }
467 self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
468 }
469
470 fn lower_expr_to_place_without_adjust(
471 &mut self,
472 expr_id: ExprId,
473 place: PlaceRef<'db>,
474 mut current: BasicBlockId,
475 ) -> Result<'db, Option<BasicBlockId>> {
476 match &self.store[expr_id] {
477 Expr::OffsetOf(_) => {
478 not_supported!("builtin#offset_of")
479 }
480 Expr::InlineAsm(_) => {
481 not_supported!("builtin#asm")
482 }
483 Expr::Missing => {
484 if let Some(f) = self.owner.as_function() {
485 let assoc = f.lookup(self.db);
486 if let ItemContainerId::TraitId(t) = assoc.container {
487 let name = &FunctionSignature::of(self.db, f).name;
488 return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
489 }
490 }
491 Err(MirLowerError::IncompleteExpr)
492 }
493 Expr::Path(p) => {
494 let pr =
495 if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
496 match assoc {
497 CandidateId::ConstId(c) => {
498 self.lower_const(c.into(), current, place, subst, expr_id.into())?;
499 return Ok(Some(current));
500 }
501 CandidateId::FunctionId(_) => {
502 return Ok(Some(current));
504 }
505 }
506 } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
507 match variant {
508 VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
509 VariantId::StructId(s) => ValueNs::StructId(s),
510 VariantId::UnionId(_) => implementation_error!("Union variant as path"),
511 }
512 } else {
513 let resolver_guard =
514 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
515 let hygiene = self.store.expr_path_hygiene(expr_id);
516 let result = self
517 .resolver
518 .resolve_path_in_value_ns_fully(self.db, p, hygiene)
519 .ok_or_else(|| {
520 MirLowerError::unresolved_path(
521 self.db,
522 p,
523 DisplayTarget::from_crate(self.db, self.krate()),
524 self.owner.expression_store_owner(self.db),
525 self.store,
526 )
527 })?;
528 self.resolver.reset_to_guard(resolver_guard);
529 result
530 };
531 match pr {
532 ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
533 let Some((temp, current)) =
534 self.lower_expr_as_place_without_adjust(current, expr_id, false)?
535 else {
536 return Ok(None);
537 };
538 self.push_assignment(
539 current,
540 place,
541 Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(),
542 expr_id.into(),
543 );
544 Ok(Some(current))
545 }
546 ValueNs::ConstId(const_id) => {
547 self.lower_const(
548 const_id.into(),
549 current,
550 place,
551 GenericArgs::empty(self.interner()),
552 expr_id.into(),
553 )?;
554 Ok(Some(current))
555 }
556 ValueNs::EnumVariantId(variant_id) => {
557 let variant_fields = variant_id.fields(self.db);
558 if variant_fields.shape == FieldsShape::Unit {
559 let ty = self.infer.expr_ty(expr_id);
560 current = self.lower_enum_variant(
561 variant_id,
562 current,
563 place,
564 ty,
565 Box::new([]),
566 expr_id.into(),
567 )?;
568 }
569 Ok(Some(current))
571 }
572 ValueNs::GenericParam(p) => {
573 let def = self.owner.generic_def(self.db);
574 let generics = generics(self.db, def);
575 let index = generics.type_or_const_param_idx(p.into());
576 self.push_assignment(
577 current,
578 place,
579 Rvalue::from(Operand {
580 kind: OperandKind::Constant {
581 konst: Const::new_param(
582 self.interner(),
583 ParamConst { id: p, index },
584 )
585 .store(),
586 ty: self.db.const_param_ty(p).store(),
587 },
588 span: None,
589 }),
590 expr_id.into(),
591 );
592 Ok(Some(current))
593 }
594 ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::ImplSelf(_) => {
595 Ok(Some(current))
597 }
598 }
599 }
600 Expr::If { condition, then_branch, else_branch } => {
601 let Some((discr, current)) =
602 self.lower_expr_to_some_operand(*condition, current)?
603 else {
604 return Ok(None);
605 };
606 let start_of_then = self.new_basic_block();
607 let end_of_then = self.lower_expr_to_place(*then_branch, place, start_of_then)?;
608 let start_of_else = self.new_basic_block();
609 let end_of_else = if let Some(else_branch) = else_branch {
610 self.lower_expr_to_place(*else_branch, place, start_of_else)?
611 } else {
612 Some(start_of_else)
613 };
614 self.set_terminator(
615 current,
616 TerminatorKind::SwitchInt {
617 discr,
618 targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
619 },
620 expr_id.into(),
621 );
622 Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
623 }
624 Expr::Let { pat, expr } => {
625 let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)?
626 else {
627 return Ok(None);
628 };
629 self.push_fake_read(current, cond_place, expr_id.into());
630 let resolver_guard =
631 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
632 let (then_target, else_target) =
633 self.pattern_match(current, None, cond_place, *pat)?;
634 self.resolver.reset_to_guard(resolver_guard);
635 self.write_bytes_to_place(
636 then_target,
637 place,
638 Box::new([1]),
639 Ty::new_bool(self.interner()),
640 MirSpan::Unknown,
641 )?;
642 if let Some(else_target) = else_target {
643 self.write_bytes_to_place(
644 else_target,
645 place,
646 Box::new([0]),
647 Ty::new_bool(self.interner()),
648 MirSpan::Unknown,
649 )?;
650 }
651 Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
652 }
653 Expr::Unsafe { id: _, statements, tail } => {
654 self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
655 }
656 Expr::Block { id: _, statements, tail, label } => {
657 if let Some(label) = label {
658 self.lower_loop(current, place, Some(*label), expr_id.into(), |this, begin| {
659 if let Some(current) = this.lower_block_to_place(
660 statements,
661 begin,
662 *tail,
663 place,
664 expr_id.into(),
665 )? {
666 let end = this.current_loop_end()?;
667 this.set_goto(current, end, expr_id.into());
668 }
669 Ok(())
670 })
671 } else {
672 self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
673 }
674 }
675 Expr::Loop { body, label, source: _ } => {
676 self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
677 let scope = this.push_drop_scope();
678 if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
679 current = scope.pop_and_drop(this, current, body.into());
680 this.set_goto(current, begin, expr_id.into());
681 } else {
682 scope.pop_assume_dropped(this);
683 }
684 Ok(())
685 })
686 }
687 Expr::Call { callee, args, .. } => {
688 if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
689 let ty = Ty::new_fn_def(
690 self.interner(),
691 CallableDefId::FunctionId(func_id).into(),
692 generic_args,
693 );
694 let func = Operand::from_bytes(Box::default(), ty);
695 return self.lower_call_and_args(
696 func,
697 iter::once(*callee).chain(args.iter().copied()),
698 place,
699 current,
700 self.is_uninhabited(expr_id),
701 expr_id.into(),
702 );
703 }
704 let callee_ty = self.expr_ty_after_adjustments(*callee);
705 match callee_ty.kind() {
706 TyKind::FnDef(..) => {
707 let func = Operand::from_bytes(Box::default(), callee_ty);
708 self.lower_call_and_args(
709 func,
710 args.iter().copied(),
711 place,
712 current,
713 self.is_uninhabited(expr_id),
714 expr_id.into(),
715 )
716 }
717 TyKind::FnPtr(..) => {
718 let Some((func, current)) =
719 self.lower_expr_to_some_operand(*callee, current)?
720 else {
721 return Ok(None);
722 };
723 self.lower_call_and_args(
724 func,
725 args.iter().copied(),
726 place,
727 current,
728 self.is_uninhabited(expr_id),
729 expr_id.into(),
730 )
731 }
732 TyKind::Closure(_, _) => {
733 not_supported!(
734 "method resolution not emitted for closure (Are Fn traits available?)"
735 );
736 }
737 TyKind::Error(_) => {
738 Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id))
739 }
740 _ => Err(MirLowerError::TypeError("function call on bad type")),
741 }
742 }
743 Expr::MethodCall { receiver, args, method_name, .. } => {
744 let (func_id, generic_args) =
745 self.infer.method_resolution(expr_id).ok_or_else(|| {
746 MirLowerError::UnresolvedMethod(
747 method_name.display(self.db, self.edition()).to_string(),
748 )
749 })?;
750 let func = Operand::from_fn(self.db, func_id, generic_args);
751 self.lower_call_and_args(
752 func,
753 iter::once(*receiver).chain(args.iter().copied()),
754 place,
755 current,
756 self.is_uninhabited(expr_id),
757 expr_id.into(),
758 )
759 }
760 Expr::Match { expr, arms } => {
761 let Some((cond_place, mut current)) =
762 self.lower_expr_as_place(current, *expr, true)?
763 else {
764 return Ok(None);
765 };
766 self.push_fake_read(current, cond_place, expr_id.into());
767 let mut end = None;
768 let resolver_guard =
769 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
770 for MatchArm { pat, guard, expr } in arms.iter() {
771 let (then, mut otherwise) =
772 self.pattern_match(current, None, cond_place, *pat)?;
773 let then = if let &Some(guard) = guard {
774 let next = self.new_basic_block();
775 let o = otherwise.get_or_insert_with(|| self.new_basic_block());
776 if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
777 self.set_terminator(
778 c,
779 TerminatorKind::SwitchInt {
780 discr,
781 targets: SwitchTargets::static_if(1, next, *o),
782 },
783 expr_id.into(),
784 );
785 }
786 next
787 } else {
788 then
789 };
790 if let Some(block) = self.lower_expr_to_place(*expr, place, then)? {
791 let r = end.get_or_insert_with(|| self.new_basic_block());
792 self.set_goto(block, *r, expr_id.into());
793 }
794 match otherwise {
795 Some(o) => current = o,
796 None => {
797 break;
800 }
801 }
802 }
803 self.resolver.reset_to_guard(resolver_guard);
804 if self.is_unterminated(current) {
805 self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
806 }
807 Ok(end)
808 }
809 Expr::Continue { label } => {
810 let loop_data = match label {
811 Some(l) => {
812 self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?
813 }
814 None => self
815 .current_loop_blocks
816 .as_ref()
817 .ok_or(MirLowerError::ContinueWithoutLoop)?,
818 };
819 let begin = loop_data.begin;
820 current =
821 self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
822 self.set_goto(current, begin, expr_id.into());
823 Ok(None)
824 }
825 &Expr::Break { expr, label } => {
826 if let Some(expr) = expr {
827 let loop_data = match label {
828 Some(l) => self
829 .labeled_loop_blocks
830 .get(&l)
831 .ok_or(MirLowerError::UnresolvedLabel)?,
832 None => self
833 .current_loop_blocks
834 .as_ref()
835 .ok_or(MirLowerError::BreakWithoutLoop)?,
836 };
837 let Some(c) =
838 self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)?
839 else {
840 return Ok(None);
841 };
842 current = c;
843 }
844 let (end, drop_scope) = match label {
845 Some(l) => {
846 let loop_blocks = self
847 .labeled_loop_blocks
848 .get(&l)
849 .ok_or(MirLowerError::UnresolvedLabel)?;
850 (
851 loop_blocks.end.expect("We always generate end for labeled loops"),
852 loop_blocks.drop_scope_index,
853 )
854 }
855 None => (
856 self.current_loop_end()?,
857 self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
858 ),
859 };
860 current = self.drop_until_scope(drop_scope, current, expr_id.into());
861 self.set_goto(current, end, expr_id.into());
862 Ok(None)
863 }
864 Expr::Return { expr } => {
865 if let Some(expr) = expr {
866 if let Some(c) =
867 self.lower_expr_to_place(*expr, return_slot().into(), current)?
868 {
869 current = c;
870 } else {
871 return Ok(None);
872 }
873 }
874 current = self.drop_until_scope(0, current, expr_id.into());
875 self.set_terminator(current, TerminatorKind::Return, expr_id.into());
876 Ok(None)
877 }
878 Expr::Become { .. } => not_supported!("tail-calls"),
879 Expr::Yield { .. } => not_supported!("yield"),
880 Expr::RecordLit { fields, path, spread, .. } => {
881 let spread_place = match *spread {
882 RecordSpread::Expr(it) => {
883 let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
884 return Ok(None);
885 };
886 current = c;
887 Some(p)
888 }
889 RecordSpread::None => None,
890 RecordSpread::FieldDefaults => not_supported!("empty record spread"),
891 };
892 let variant_id =
893 self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| {
894 MirLowerError::unresolved_path(
895 self.db,
896 path,
897 self.display_target(),
898 self.owner.expression_store_owner(self.db),
899 self.store,
900 )
901 })?;
902 let subst = match self.expr_ty_without_adjust(expr_id).kind() {
903 TyKind::Adt(_, s) => s,
904 _ => not_supported!("Non ADT record literal"),
905 };
906 let variant_fields = variant_id.fields(self.db);
907 match variant_id {
908 VariantId::EnumVariantId(_) | VariantId::StructId(_) => {
909 let mut operands = vec![None; variant_fields.fields().len()];
910 for RecordLitField { name, expr } in fields.iter() {
911 let field_id =
912 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;
913 let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)?
914 else {
915 return Ok(None);
916 };
917 current = c;
918 operands[u32::from(field_id.into_raw()) as usize] = Some(op);
919 }
920 let rvalue = Rvalue::Aggregate(
921 AggregateKind::Adt(variant_id, subst.store()),
922 match spread_place {
923 Some(sp) if let VariantId::StructId(_) = variant_id => operands
924 .into_iter()
925 .enumerate()
926 .map(|(i, it)| match it {
927 Some(it) => it,
928 None => {
929 let p = sp.project(ProjectionElem::Field(FieldIndex(
930 i as u32,
931 )));
932 Operand {
933 kind: OperandKind::Copy(p.store()),
934 span: None,
935 }
936 }
937 })
938 .collect(),
939 Some(_) => {
940 return Err(MirLowerError::TypeError(
941 "functional record update syntax requires a struct",
942 ));
943 }
944 None => operands.into_iter().collect::<Option<_>>().ok_or(
945 MirLowerError::TypeError("missing field in record literal"),
946 )?,
947 },
948 );
949 self.push_assignment(current, place, rvalue, expr_id.into());
950 Ok(Some(current))
951 }
952 VariantId::UnionId(_union_id) => {
953 let [RecordLitField { name, expr }] = fields.as_ref() else {
954 not_supported!("Union record literal with more than one field");
955 };
956 let local_id =
957 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;
958 let place = place.project(PlaceElem::Field(local_id.into()));
959 self.lower_expr_to_place(*expr, place, current)
960 }
961 }
962 }
963 Expr::Await { .. } => not_supported!("await"),
964 Expr::Yeet { .. } => not_supported!("yeet"),
965 &Expr::Const(id) => {
966 self.lower_expr_to_place(id, place, current)
971 }
972 Expr::Cast { expr, type_ref: _ } => {
973 let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
974 return Ok(None);
975 };
976 let rvalue = if self.infer.coercion_casts.contains(expr) {
979 Rvalue::Use(it)
980 } else {
981 let source_ty = self.infer.expr_ty(*expr);
982 let target_ty = self.infer.expr_ty(expr_id);
983 let cast_kind = if source_ty.as_reference().is_some() {
984 CastKind::PointerCoercion(PointerCast::ArrayToPointer)
985 } else {
986 cast_kind(self.db, source_ty, target_ty)?
987 };
988
989 Rvalue::Cast(cast_kind, it, target_ty.store())
990 };
991 self.push_assignment(current, place, rvalue, expr_id.into());
992 Ok(Some(current))
993 }
994 Expr::Ref { expr, rawness: _, mutability } => {
995 let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
996 return Ok(None);
997 };
998 let bk = BorrowKind::from_hir_mutability(*mutability);
999 self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into());
1000 Ok(Some(current))
1001 }
1002 Expr::Box { expr } => {
1003 let ty = self.expr_ty_after_adjustments(*expr);
1004 self.push_assignment(
1005 current,
1006 place,
1007 Rvalue::ShallowInitBoxWithAlloc(ty.store()),
1008 expr_id.into(),
1009 );
1010 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)?
1011 else {
1012 return Ok(None);
1013 };
1014 let p = place.project(ProjectionElem::Deref);
1015 self.push_assignment(current, p, operand.into(), expr_id.into());
1016 Ok(Some(current))
1017 }
1018 Expr::Field { .. }
1019 | Expr::Index { .. }
1020 | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
1021 let Some((p, current)) =
1022 self.lower_expr_as_place_without_adjust(current, expr_id, true)?
1023 else {
1024 return Ok(None);
1025 };
1026 self.push_assignment(
1027 current,
1028 place,
1029 Operand { kind: OperandKind::Copy(p.store()), span: None }.into(),
1030 expr_id.into(),
1031 );
1032 Ok(Some(current))
1033 }
1034 Expr::UnaryOp {
1035 expr,
1036 op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg),
1037 } => {
1038 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)?
1039 else {
1040 return Ok(None);
1041 };
1042 let operation = match op {
1043 hir_def::hir::UnaryOp::Not => UnOp::Not,
1044 hir_def::hir::UnaryOp::Neg => UnOp::Neg,
1045 _ => unreachable!(),
1046 };
1047 self.push_assignment(
1048 current,
1049 place,
1050 Rvalue::UnaryOp(operation, operand),
1051 expr_id.into(),
1052 );
1053 Ok(Some(current))
1054 }
1055 Expr::BinaryOp { lhs, rhs, op } => {
1056 let op: BinaryOp = op.ok_or(MirLowerError::IncompleteExpr)?;
1057 let is_builtin = 'b: {
1058 let lhs_ty = self.expr_ty_without_adjust(*lhs);
1061 let rhs_ty = self.expr_ty_without_adjust(*rhs);
1062 if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. }))
1063 && matches!(lhs_ty.kind(), TyKind::RawPtr(..))
1064 && matches!(rhs_ty.kind(), TyKind::RawPtr(..))
1065 {
1066 break 'b true;
1067 }
1068 let builtin_inequal_impls = matches!(
1069 op,
1070 BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr)
1071 | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
1072 );
1073 matches!(
1074 lhs_ty.kind(),
1075 TyKind::Bool
1076 | TyKind::Char
1077 | TyKind::Int(_)
1078 | TyKind::Uint(_)
1079 | TyKind::Float(_)
1080 ) && matches!(
1081 rhs_ty.kind(),
1082 TyKind::Bool
1083 | TyKind::Char
1084 | TyKind::Int(_)
1085 | TyKind::Uint(_)
1086 | TyKind::Float(_)
1087 ) && (lhs_ty == rhs_ty || builtin_inequal_impls)
1088 };
1089 if !is_builtin
1090 && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id)
1091 {
1092 let func = Operand::from_fn(self.db, func_id, generic_args);
1093 return self.lower_call_and_args(
1094 func,
1095 [*lhs, *rhs].into_iter(),
1096 place,
1097 current,
1098 self.is_uninhabited(expr_id),
1099 expr_id.into(),
1100 );
1101 }
1102 if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {
1103 let adjusts = self
1105 .infer
1106 .expr_adjustments
1107 .get(lhs)
1108 .and_then(|it| it.split_last())
1109 .map(|it| it.1)
1110 .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
1111 let Some((lhs_place, current)) =
1112 self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
1113 else {
1114 return Ok(None);
1115 };
1116 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
1117 else {
1118 return Ok(None);
1119 };
1120 let r_value = Rvalue::CheckedBinaryOp(
1121 op.into(),
1122 Operand { kind: OperandKind::Copy(lhs_place.store()), span: None },
1123 rhs_op,
1124 );
1125 self.push_assignment(current, lhs_place, r_value, expr_id.into());
1126 return Ok(Some(current));
1127 }
1128 let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?
1129 else {
1130 return Ok(None);
1131 };
1132 if let hir_def::hir::BinaryOp::LogicOp(op) = op {
1133 let value_to_short = match op {
1134 syntax::ast::LogicOp::And => 0,
1135 syntax::ast::LogicOp::Or => 1,
1136 };
1137 let start_of_then = self.new_basic_block();
1138 self.push_assignment(
1139 start_of_then,
1140 place,
1141 lhs_op.clone().into(),
1142 expr_id.into(),
1143 );
1144 let end_of_then = Some(start_of_then);
1145 let start_of_else = self.new_basic_block();
1146 let end_of_else = self.lower_expr_to_place(*rhs, place, start_of_else)?;
1147 self.set_terminator(
1148 current,
1149 TerminatorKind::SwitchInt {
1150 discr: lhs_op,
1151 targets: SwitchTargets::static_if(
1152 value_to_short,
1153 start_of_then,
1154 start_of_else,
1155 ),
1156 },
1157 expr_id.into(),
1158 );
1159 return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
1160 }
1161 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
1162 else {
1163 return Ok(None);
1164 };
1165 self.push_assignment(
1166 current,
1167 place,
1168 Rvalue::CheckedBinaryOp(
1169 match op {
1170 hir_def::hir::BinaryOp::LogicOp(op) => match op {
1171 hir_def::hir::LogicOp::And => BinOp::BitAnd, hir_def::hir::LogicOp::Or => BinOp::BitOr,
1173 },
1174 hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
1175 hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
1176 hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), },
1178 lhs_op,
1179 rhs_op,
1180 ),
1181 expr_id.into(),
1182 );
1183 Ok(Some(current))
1184 }
1185 &Expr::Assignment { target, value } => {
1186 let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?
1187 else {
1188 return Ok(None);
1189 };
1190 self.push_fake_read(current, value, expr_id.into());
1191 let resolver_guard =
1192 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
1193 current = self.pattern_match_assignment(current, value, target)?;
1194 self.resolver.reset_to_guard(resolver_guard);
1195 Ok(Some(current))
1196 }
1197 &Expr::Range { lhs, rhs, range_type: _ } => {
1198 let ty = self.expr_ty_without_adjust(expr_id);
1199 let Some((adt, subst)) = ty.as_adt() else {
1200 return Err(MirLowerError::TypeError("Range type is not adt"));
1201 };
1202 let AdtId::StructId(st) = adt else {
1203 return Err(MirLowerError::TypeError("Range type is not struct"));
1204 };
1205 let mut lp = None;
1206 let mut rp = None;
1207 if let Some(it) = lhs {
1208 let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else {
1209 return Ok(None);
1210 };
1211 lp = Some(o);
1212 current = c;
1213 }
1214 if let Some(it) = rhs {
1215 let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else {
1216 return Ok(None);
1217 };
1218 rp = Some(o);
1219 current = c;
1220 }
1221 self.push_assignment(
1222 current,
1223 place,
1224 Rvalue::Aggregate(
1225 AggregateKind::Adt(st.into(), subst.store()),
1226 st.fields(self.db)
1227 .fields()
1228 .iter()
1229 .map(|it| {
1230 let o = match it.1.name.as_str() {
1231 "start" => lp.take(),
1232 "end" => rp.take(),
1233 "exhausted" => Some(Operand::from_bytes(
1234 Box::new([0]),
1235 Ty::new_bool(self.interner()),
1236 )),
1237 _ => None,
1238 };
1239 o.ok_or(MirLowerError::UnresolvedField)
1240 })
1241 .collect::<Result<'_, _>>()?,
1242 ),
1243 expr_id.into(),
1244 );
1245 Ok(Some(current))
1246 }
1247 Expr::Closure { closure_kind: ClosureKind::Closure, .. } => {
1248 let ty = self.expr_ty_without_adjust(expr_id);
1249 let TyKind::Closure(id, _) = ty.kind() else {
1250 not_supported!("closure with non closure type");
1251 };
1252 self.result.closures.push(id.0);
1253 let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr];
1254
1255 let span = |sources: &[CaptureSourceStack]| match sources
1256 .first()
1257 .map(|it| it.final_source().unpack())
1258 {
1259 Some(ExprOrPatId::ExprId(it)) => it.into(),
1260 Some(ExprOrPatId::PatId(it)) => it.into(),
1261 None => MirSpan::Unknown,
1262 };
1263 let convert_place = |this: &mut Self, place: &HirPlace| {
1264 let (HirPlaceBase::Local(local) | HirPlaceBase::Upvar { var_id: local, .. }) =
1265 place.base
1266 else {
1267 not_supported!("non-local capture");
1268 };
1269 Ok(Place {
1270 local: this.binding_local(local)?,
1271 projection: Projection::new_from_iter(convert_closure_capture_projections(
1272 self.db, place,
1273 ))
1274 .store(),
1275 })
1276 };
1277
1278 for (place, _, sources) in &closure_data.fake_reads {
1279 let p = convert_place(self, place)?;
1280 self.push_fake_read(current, p.as_ref(), span(sources));
1281 }
1282
1283 let captures = closure_data.min_captures.values().flatten();
1284 let mut operands = vec![];
1285 for capture in captures {
1286 let p = convert_place(self, &capture.place)?;
1287 match capture.info.capture_kind {
1288 UpvarCapture::ByRef(bk) => {
1289 let tmp_ty = capture.captured_ty(self.db);
1290 let capture_span = span(&capture.info.sources);
1292 let tmp = self.temp(tmp_ty, current, capture_span)?.into();
1293 self.push_assignment(
1294 current,
1295 tmp,
1296 Rvalue::Ref(BorrowKind::from_hir(bk), p),
1297 capture_span,
1298 );
1299 operands
1300 .push(Operand { kind: OperandKind::Move(tmp.store()), span: None });
1301 }
1302 UpvarCapture::ByValue => {
1303 operands.push(Operand { kind: OperandKind::Move(p), span: None })
1304 }
1305 UpvarCapture::ByUse => not_supported!("capture by use"),
1306 }
1307 }
1308 self.push_assignment(
1309 current,
1310 place,
1311 Rvalue::Aggregate(AggregateKind::Closure(ty.store()), operands.into()),
1312 expr_id.into(),
1313 );
1314 Ok(Some(current))
1315 }
1316 Expr::Closure { closure_kind, .. } => not_supported!("{closure_kind:?} closure"),
1317 Expr::Tuple { exprs } => {
1318 let Some(values) = exprs
1319 .iter()
1320 .map(|it| {
1321 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else {
1322 return Ok(None);
1323 };
1324 current = c;
1325 Ok(Some(o))
1326 })
1327 .collect::<Result<'_, Option<_>>>()?
1328 else {
1329 return Ok(None);
1330 };
1331 let r = Rvalue::Aggregate(
1332 AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id).store()),
1333 values,
1334 );
1335 self.push_assignment(current, place, r, expr_id.into());
1336 Ok(Some(current))
1337 }
1338 Expr::Array(l) => match l {
1339 Array::ElementList { elements, .. } => {
1340 let elem_ty = match self.expr_ty_without_adjust(expr_id).kind() {
1341 TyKind::Array(ty, _) => ty,
1342 _ => {
1343 return Err(MirLowerError::TypeError(
1344 "Array expression with non array type",
1345 ));
1346 }
1347 };
1348 let Some(values) = elements
1349 .iter()
1350 .map(|it| {
1351 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)?
1352 else {
1353 return Ok(None);
1354 };
1355 current = c;
1356 Ok(Some(o))
1357 })
1358 .collect::<Result<'_, Option<_>>>()?
1359 else {
1360 return Ok(None);
1361 };
1362 let r = Rvalue::Aggregate(AggregateKind::Array(elem_ty.store()), values);
1363 self.push_assignment(current, place, r, expr_id.into());
1364 Ok(Some(current))
1365 }
1366 Array::Repeat { initializer, .. } => {
1367 let Some((init, current)) =
1368 self.lower_expr_to_some_operand(*initializer, current)?
1369 else {
1370 return Ok(None);
1371 };
1372 let len = match self.expr_ty_without_adjust(expr_id).kind() {
1373 TyKind::Array(_, len) => len,
1374 _ => {
1375 return Err(MirLowerError::TypeError(
1376 "Array repeat expression with non array type",
1377 ));
1378 }
1379 };
1380 let r = Rvalue::Repeat(init, len.store());
1381 self.push_assignment(current, place, r, expr_id.into());
1382 Ok(Some(current))
1383 }
1384 },
1385 Expr::Literal(l) => {
1386 let ty = self.expr_ty_without_adjust(expr_id);
1387 let op = self.lower_literal_to_operand(ty, l)?;
1388 self.push_assignment(current, place, op.into(), expr_id.into());
1389 Ok(Some(current))
1390 }
1391 Expr::Underscore => Ok(Some(current)),
1392 Expr::IncludeBytes => not_supported!("include_bytes!()"),
1393 }
1394 }
1395
1396 fn push_field_projection(
1397 &mut self,
1398 place: &mut PlaceRef<'db>,
1399 expr_id: ExprId,
1400 ) -> Result<'db, ()> {
1401 if let Expr::Field { expr, name } = &self.store[expr_id] {
1402 if let TyKind::Tuple(tys) = self.expr_ty_after_adjustments(*expr).kind() {
1403 let index =
1404 name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))?
1405 as u32;
1406 if tys.get(index as usize).is_none() {
1407 return Err(MirLowerError::TypeError("tuple field index out of range"));
1408 }
1409 *place = place.project(ProjectionElem::Field(FieldIndex(index)));
1410 } else {
1411 let field = self
1412 .infer
1413 .field_resolution(expr_id)
1414 .ok_or(MirLowerError::UnresolvedField)?
1415 .either(|f| f.local_id.into(), |t| FieldIndex(t.index));
1416 *place = place.project(ProjectionElem::Field(field));
1417 }
1418 } else {
1419 not_supported!("")
1420 }
1421 Ok(())
1422 }
1423
1424 fn lower_literal_or_const_to_operand(
1425 &mut self,
1426 ty: Ty<'db>,
1427 loc: &ExprId,
1428 ) -> Result<'db, Operand> {
1429 match &self.store[*loc] {
1430 Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
1431 Expr::Path(c) => {
1432 let owner = self.owner;
1433 let db = self.db;
1434 let unresolved_name = || {
1435 MirLowerError::unresolved_path(
1436 self.db,
1437 c,
1438 DisplayTarget::from_crate(db, owner.krate(db)),
1439 self.owner.expression_store_owner(self.db),
1440 self.store,
1441 )
1442 };
1443 let pr = self
1444 .resolver
1445 .resolve_path_in_value_ns(self.db, c, HygieneId::ROOT)
1446 .ok_or_else(unresolved_name)?;
1447 match pr {
1448 ResolveValueResult::ValueNs(v) => {
1449 if let ValueNs::ConstId(c) = v {
1450 self.lower_const_to_operand(
1451 GenericArgs::empty(self.interner()),
1452 c.into(),
1453 )
1454 } else {
1455 not_supported!("bad path in range pattern");
1456 }
1457 }
1458 ResolveValueResult::Partial(_, _) => {
1459 not_supported!("associated constants in range pattern")
1460 }
1461 }
1462 }
1463 _ => {
1464 not_supported!("only `char` and numeric types are allowed in range patterns");
1465 }
1466 }
1467 }
1468
1469 fn lower_literal_to_operand(&mut self, ty: Ty<'db>, l: &Literal) -> Result<'db, Operand> {
1470 let size = || {
1471 self.db
1472 .layout_of_ty(
1473 ty.store(),
1474 ParamEnvAndCrate { param_env: self.env, krate: self.krate() }.store(),
1475 )
1476 .map(|it| it.size.bytes_usize())
1477 };
1478 const USIZE_SIZE: usize = size_of::<usize>();
1479 let bytes: Box<[_]> = match l {
1480 hir_def::hir::Literal::String(b) => {
1481 let b = b.as_str();
1482 let mut data = [0; { 2 * USIZE_SIZE }];
1483 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1484 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());
1485 let mm = MemoryMap::simple(b.as_bytes().into());
1486 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1487 }
1488 hir_def::hir::Literal::CString(b) => {
1489 let bytes = b.iter().copied().chain(iter::once(0)).collect::<Box<_>>();
1490
1491 let mut data = [0; { 2 * USIZE_SIZE }];
1492 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1493 data[USIZE_SIZE..].copy_from_slice(&bytes.len().to_le_bytes());
1494 let mm = MemoryMap::simple(bytes);
1495 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1496 }
1497 hir_def::hir::Literal::ByteString(b) => {
1498 let mut data = [0; { 2 * USIZE_SIZE }];
1499 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());
1500 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());
1501 let mm = MemoryMap::simple(b.clone());
1502 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));
1503 }
1504 hir_def::hir::Literal::Char(c) => Box::new(u32::from(*c).to_le_bytes()),
1505 hir_def::hir::Literal::Bool(b) => Box::new([*b as u8]),
1506 hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
1507 hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
1508 hir_def::hir::Literal::Float(f, _) => match size()? {
1509 16 => Box::new(f.to_f128().to_bits().to_le_bytes()),
1510 8 => Box::new(f.to_f64().to_bits().to_le_bytes()),
1511 4 => Box::new(f.to_f32().to_bits().to_le_bytes()),
1512 2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),
1513 _ => {
1514 return Err(MirLowerError::TypeError(
1515 "float with size other than 2, 4, 8 or 16 bytes",
1516 ));
1517 }
1518 },
1519 };
1520 Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))
1521 }
1522
1523 fn new_basic_block(&mut self) -> BasicBlockId {
1524 self.result.basic_blocks.alloc(BasicBlock::default())
1525 }
1526
1527 fn lower_const(
1528 &mut self,
1529 const_id: GeneralConstId<'db>,
1530 prev_block: BasicBlockId,
1531 place: PlaceRef<'db>,
1532 subst: GenericArgs<'db>,
1533 span: MirSpan,
1534 ) -> Result<'db, ()> {
1535 let c = self.lower_const_to_operand(subst, const_id)?;
1536 self.push_assignment(prev_block, place, c.into(), span);
1537 Ok(())
1538 }
1539
1540 fn lower_const_to_operand(
1541 &mut self,
1542 subst: GenericArgs<'db>,
1543 const_id: GeneralConstId<'db>,
1544 ) -> Result<'db, Operand> {
1545 let konst = Const::new_unevaluated(
1546 self.interner(),
1547 UnevaluatedConst { def: const_id.into(), args: subst },
1548 );
1549 let ty = match const_id {
1550 GeneralConstId::ConstId(id) => self.db.value_ty(id.into()).unwrap(),
1551 GeneralConstId::StaticId(id) => self.db.value_ty(id.into()).unwrap(),
1552 GeneralConstId::AnonConstId(id) => id.loc(self.db).ty.get(),
1553 };
1554 let ty = ty.instantiate(self.interner(), subst).skip_norm_wip();
1555 Ok(Operand {
1556 kind: OperandKind::Constant { konst: konst.store(), ty: ty.store() },
1557 span: None,
1558 })
1559 }
1560
1561 fn write_bytes_to_place(
1562 &mut self,
1563 prev_block: BasicBlockId,
1564 place: PlaceRef<'db>,
1565 cv: Box<[u8]>,
1566 ty: Ty<'db>,
1567 span: MirSpan,
1568 ) -> Result<'db, ()> {
1569 self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span);
1570 Ok(())
1571 }
1572
1573 fn lower_enum_variant(
1574 &mut self,
1575 variant_id: EnumVariantId,
1576 prev_block: BasicBlockId,
1577 place: PlaceRef<'db>,
1578 ty: Ty<'db>,
1579 fields: Box<[Operand]>,
1580 span: MirSpan,
1581 ) -> Result<'db, BasicBlockId> {
1582 let subst = match ty.kind() {
1583 TyKind::Adt(_, subst) => subst,
1584 _ => implementation_error!("Non ADT enum"),
1585 };
1586 self.push_assignment(
1587 prev_block,
1588 place,
1589 Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst.store()), fields),
1590 span,
1591 );
1592 Ok(prev_block)
1593 }
1594
1595 fn lower_call_and_args(
1596 &mut self,
1597 func: Operand,
1598 args: impl Iterator<Item = ExprId>,
1599 place: PlaceRef<'db>,
1600 mut current: BasicBlockId,
1601 is_uninhabited: bool,
1602 span: MirSpan,
1603 ) -> Result<'db, Option<BasicBlockId>> {
1604 let Some(args) = args
1605 .map(|arg| {
1606 if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {
1607 current = c;
1608 Ok(Some(temp))
1609 } else {
1610 Ok(None)
1611 }
1612 })
1613 .collect::<Result<'_, Option<Vec<_>>>>()?
1614 else {
1615 return Ok(None);
1616 };
1617 self.lower_call(func, args.into(), place, current, is_uninhabited, span)
1618 }
1619
1620 fn lower_call(
1621 &mut self,
1622 func: Operand,
1623 args: Box<[Operand]>,
1624 place: PlaceRef<'db>,
1625 current: BasicBlockId,
1626 is_uninhabited: bool,
1627 span: MirSpan,
1628 ) -> Result<'db, Option<BasicBlockId>> {
1629 let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
1630 self.set_terminator(
1631 current,
1632 TerminatorKind::Call {
1633 func,
1634 args,
1635 destination: place.store(),
1636 target: b,
1637 cleanup: None,
1638 from_hir_call: true,
1639 },
1640 span,
1641 );
1642 Ok(b)
1643 }
1644
1645 fn is_unterminated(&mut self, source: BasicBlockId) -> bool {
1646 self.result.basic_blocks[source].terminator.is_none()
1647 }
1648
1649 fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
1650 self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
1651 }
1652
1653 fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
1654 self.set_terminator(source, TerminatorKind::Goto { target }, span);
1655 }
1656
1657 fn expr_ty_without_adjust(&self, e: ExprId) -> Ty<'db> {
1658 self.infer.expr_ty(e)
1659 }
1660
1661 fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {
1662 let mut ty = None;
1663 if let Some(it) = self.infer.expr_adjustments.get(&e)
1664 && let Some(it) = it.last()
1665 {
1666 ty = Some(it.target.as_ref());
1667 }
1668 ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
1669 }
1670
1671 fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
1672 self.result.basic_blocks[block].statements.push(statement);
1673 }
1674
1675 fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) {
1676 self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span));
1677 }
1678
1679 fn push_assignment(
1680 &mut self,
1681 block: BasicBlockId,
1682 place: PlaceRef<'db>,
1683 rvalue: Rvalue,
1684 span: MirSpan,
1685 ) {
1686 self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span));
1687 }
1688
1689 fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> {
1690 match &self.discr_temp {
1691 Some(it) => it.as_ref(),
1692 None => {
1693 let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128);
1695 let tmp: PlaceRef<'_> = self
1696 .temp(discr_ty, current, MirSpan::Unknown)
1697 .expect("discr_ty is never unsized")
1698 .into();
1699 self.discr_temp = Some(tmp.store());
1700 tmp
1701 }
1702 }
1703 }
1704
1705 fn lower_loop(
1706 &mut self,
1707 prev_block: BasicBlockId,
1708 place: PlaceRef<'db>,
1709 label: Option<LabelId>,
1710 span: MirSpan,
1711 f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>,
1712 ) -> Result<'db, Option<BasicBlockId>> {
1713 let begin = self.new_basic_block();
1714 let prev = self.current_loop_blocks.replace(LoopBlocks {
1715 begin,
1716 end: None,
1717 place: place.store(),
1718 drop_scope_index: self.drop_scopes.len(),
1719 });
1720 let prev_label = if let Some(label) = label {
1721 self.current_loop_end()?;
1725 self.labeled_loop_blocks
1726 .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
1727 } else {
1728 None
1729 };
1730 self.set_goto(prev_block, begin, span);
1731 f(self, begin)?;
1732 let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
1733 MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_owned()),
1734 )?;
1735 if let Some(prev) = prev_label {
1736 self.labeled_loop_blocks.insert(label.unwrap(), prev);
1737 }
1738 Ok(my.end)
1739 }
1740
1741 fn has_adjustments(&self, expr_id: ExprId) -> bool {
1742 !self.infer.expr_adjustments.get(&expr_id).map(|it| it.is_empty()).unwrap_or(true)
1743 }
1744
1745 fn merge_blocks(
1746 &mut self,
1747 b1: Option<BasicBlockId>,
1748 b2: Option<BasicBlockId>,
1749 span: MirSpan,
1750 ) -> Option<BasicBlockId> {
1751 match (b1, b2) {
1752 (None, None) => None,
1753 (None, Some(b)) | (Some(b), None) => Some(b),
1754 (Some(b1), Some(b2)) => {
1755 let bm = self.new_basic_block();
1756 self.set_goto(b1, bm, span);
1757 self.set_goto(b2, bm, span);
1758 Some(bm)
1759 }
1760 }
1761 }
1762
1763 fn current_loop_end(&mut self) -> Result<'db, BasicBlockId> {
1764 let r = match self
1765 .current_loop_blocks
1766 .as_mut()
1767 .ok_or(MirLowerError::ImplementationError(
1768 "Current loop access out of loop".to_owned(),
1769 ))?
1770 .end
1771 {
1772 Some(it) => it,
1773 None => {
1774 let s = self.new_basic_block();
1775 self.current_loop_blocks
1776 .as_mut()
1777 .ok_or(MirLowerError::ImplementationError(
1778 "Current loop access out of loop".to_owned(),
1779 ))?
1780 .end = Some(s);
1781 s
1782 }
1783 };
1784 Ok(r)
1785 }
1786
1787 fn is_uninhabited(&self, expr_id: ExprId) -> bool {
1788 is_ty_uninhabited_from(
1789 &self.infcx,
1790 self.infer.expr_ty(expr_id),
1791 self.owner.module(self.db),
1792 self.env,
1793 )
1794 }
1795
1796 fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<'db, ()> {
1799 let l = self.binding_local(b)?;
1800 self.push_storage_live_for_local(l, current, MirSpan::BindingId(b))
1801 }
1802
1803 fn push_storage_live_for_local(
1804 &mut self,
1805 l: LocalId,
1806 current: BasicBlockId,
1807 span: MirSpan,
1808 ) -> Result<'db, ()> {
1809 self.drop_scopes.last_mut().unwrap().locals.push(l);
1810 self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
1811 Ok(())
1812 }
1813
1814 fn lower_block_to_place(
1815 &mut self,
1816 statements: &[hir_def::hir::Statement],
1817 mut current: BasicBlockId,
1818 tail: Option<ExprId>,
1819 place: PlaceRef<'db>,
1820 span: MirSpan,
1821 ) -> Result<'db, Option<Idx<BasicBlock>>> {
1822 let scope = self.push_drop_scope();
1823 for statement in statements.iter() {
1824 match statement {
1825 hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
1826 if let Some(expr_id) = initializer {
1827 let else_block;
1828 let Some((init_place, c)) =
1829 self.lower_expr_as_place(current, *expr_id, true)?
1830 else {
1831 scope.pop_assume_dropped(self);
1832 return Ok(None);
1833 };
1834 current = c;
1835 self.push_fake_read(current, init_place, span);
1836 let resolver_guard = self.resolver.update_to_inner_scope(
1839 self.db,
1840 self.store_owner,
1841 *expr_id,
1842 );
1843 (current, else_block) =
1844 self.pattern_match(current, None, init_place, *pat)?;
1845 self.resolver.reset_to_guard(resolver_guard);
1846 match (else_block, else_branch) {
1847 (None, _) => (),
1848 (Some(else_block), None) => {
1849 self.set_terminator(else_block, TerminatorKind::Unreachable, span);
1850 }
1851 (Some(else_block), Some(else_branch)) => {
1852 if let Some((_, b)) =
1853 self.lower_expr_as_place(else_block, *else_branch, true)?
1854 {
1855 self.set_terminator(b, TerminatorKind::Unreachable, span);
1856 }
1857 }
1858 }
1859 } else {
1860 let mut err = None;
1861 self.store.walk_bindings_in_pat(*pat, |b| {
1862 if let Err(e) = self.push_storage_live(b, current) {
1863 err = Some(e);
1864 }
1865 });
1866 if let Some(e) = err {
1867 return Err(e);
1868 }
1869 }
1870 }
1871 &hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
1872 let scope2 = self.push_drop_scope();
1873 let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else {
1874 scope2.pop_assume_dropped(self);
1875 scope.pop_assume_dropped(self);
1876 return Ok(None);
1877 };
1878 self.push_fake_read(c, p, expr.into());
1879 current = scope2.pop_and_drop(self, c, expr.into());
1880 }
1881 hir_def::hir::Statement::Item(_) => (),
1882 }
1883 }
1884 if let Some(tail) = tail {
1885 let Some(c) = self.lower_expr_to_place(tail, place, current)? else {
1886 scope.pop_assume_dropped(self);
1887 return Ok(None);
1888 };
1889 current = c;
1890 }
1891 current = scope.pop_and_drop(self, current, span);
1892 Ok(Some(current))
1893 }
1894
1895 fn lower_params_and_bindings(
1896 &mut self,
1897 params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,
1898 self_binding: Option<(BindingId, Ty<'db>)>,
1899 pick_binding: impl Fn(BindingId) -> bool,
1900 ) -> Result<'db, BasicBlockId> {
1901 let base_param_count = self.result.param_locals.len();
1902 let self_binding = match self_binding {
1903 Some((self_binding, ty)) => {
1904 let local_id = self.result.locals.alloc(Local { ty: ty.store() });
1905 self.drop_scopes.last_mut().unwrap().locals.push(local_id);
1906 self.result.binding_locals.insert(self_binding, local_id);
1907 self.result.param_locals.push(local_id);
1908 Some(self_binding)
1909 }
1910 None => None,
1911 };
1912 self.result.param_locals.extend(params.clone().map(|(it, ty)| {
1913 let local_id = self.result.locals.alloc(Local { ty: ty.store() });
1914 self.drop_scopes.last_mut().unwrap().locals.push(local_id);
1915 if let Pat::Bind { id, subpat: None } = self.store[it]
1916 && matches!(
1917 self.store[id].mode,
1918 BindingAnnotation::Unannotated | BindingAnnotation::Mutable
1919 )
1920 {
1921 self.result.binding_locals.insert(id, local_id);
1922 }
1923 local_id
1924 }));
1925 for (id, _) in self.store.bindings() {
1927 if !pick_binding(id) {
1928 continue;
1929 }
1930 if !self.result.binding_locals.contains_idx(id) {
1931 self.result.binding_locals.insert(
1932 id,
1933 self.result.locals.alloc(Local { ty: self.infer.binding_ty(id).store() }),
1934 );
1935 }
1936 }
1937 let mut current = self.result.start_block;
1938 if let Some(self_binding) = self_binding {
1939 let local = self.result.param_locals.clone()[base_param_count];
1940 if local != self.binding_local(self_binding)? {
1941 let r = self.match_self_param(self_binding, current, local)?;
1942 if let Some(b) = r.1 {
1943 self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam);
1944 }
1945 current = r.0;
1946 }
1947 }
1948 let local_params = self
1949 .result
1950 .param_locals
1951 .clone()
1952 .into_iter()
1953 .skip(base_param_count + self_binding.is_some() as usize);
1954 for ((param, _), local) in params.zip(local_params) {
1955 if let Pat::Bind { id, .. } = self.store[param]
1956 && local == self.binding_local(id)?
1957 {
1958 continue;
1959 }
1960 let r = self.pattern_match(current, None, local.into(), param)?;
1961 if let Some(b) = r.1 {
1962 self.set_terminator(b, TerminatorKind::Unreachable, param.into());
1963 }
1964 current = r.0;
1965 }
1966 Ok(current)
1967 }
1968
1969 fn binding_local(&self, b: BindingId) -> Result<'db, LocalId> {
1970 match self.result.binding_locals.get(b) {
1971 Some(it) => Ok(*it),
1972 None => {
1973 Err(MirLowerError::InaccessibleLocal)
1976 }
1977 }
1978 }
1979
1980 fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> {
1981 let r = self.db.const_eval_discriminant(variant);
1982 match r {
1983 Ok(r) => Ok(r),
1984 Err(e) => {
1985 let edition = self.edition();
1986 let db = self.db;
1987 let loc = variant.lookup(db);
1988 let name = format!(
1989 "{}::{}",
1990 EnumSignature::of(db, loc.parent).name.display(db, edition),
1991 loc.parent
1992 .enum_variants(self.db)
1993 .variant_name_by_id(variant)
1994 .unwrap()
1995 .display(db, edition),
1996 );
1997 Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
1998 }
1999 }
2000 }
2001
2002 fn edition(&self) -> Edition {
2003 self.krate().data(self.db).edition
2004 }
2005
2006 fn krate(&self) -> Crate {
2007 self.owner.krate(self.db)
2008 }
2009
2010 fn display_target(&self) -> DisplayTarget {
2011 DisplayTarget::from_crate(self.db, self.krate())
2012 }
2013
2014 fn drop_until_scope(
2015 &mut self,
2016 scope_index: usize,
2017 mut current: BasicBlockId,
2018 span: MirSpan,
2019 ) -> BasicBlockId {
2020 for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
2021 self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
2022 }
2023 current
2024 }
2025
2026 fn push_drop_scope(&mut self) -> DropScopeToken {
2027 self.drop_scopes.push(DropScope::default());
2028 DropScopeToken
2029 }
2030
2031 fn pop_drop_scope_assume_dropped_internal(&mut self) {
2033 self.drop_scopes.pop();
2034 }
2035
2036 fn pop_drop_scope_internal(
2038 &mut self,
2039 mut current: BasicBlockId,
2040 span: MirSpan,
2041 ) -> BasicBlockId {
2042 let scope = self.drop_scopes.pop().unwrap();
2043 self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
2044 current
2045 }
2046
2047 fn pop_drop_scope_assert_finished(
2048 &mut self,
2049 mut current: BasicBlockId,
2050 span: MirSpan,
2051 ) -> Result<'db, BasicBlockId> {
2052 current = self.pop_drop_scope_internal(current, span);
2053 if !self.drop_scopes.is_empty() {
2054 implementation_error!("Mismatched count between drop scope push and pops");
2055 }
2056 Ok(current)
2057 }
2058
2059 fn emit_drop_and_storage_dead_for_scope(
2060 &mut self,
2061 scope: &DropScope,
2062 current: &mut Idx<BasicBlock>,
2063 span: MirSpan,
2064 ) {
2065 for &l in scope.locals.iter().rev() {
2066 if !self.infcx.type_is_copy_modulo_regions(self.env, self.result.locals[l].ty.as_ref())
2067 {
2068 let prev = std::mem::replace(current, self.new_basic_block());
2069 self.set_terminator(
2070 prev,
2071 TerminatorKind::Drop {
2072 place: PlaceRef::from(l).store(),
2073 target: *current,
2074 unwind: None,
2075 },
2076 span,
2077 );
2078 }
2079 self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
2080 }
2081 }
2082}
2083
2084fn convert_closure_capture_projections(
2085 _db: &dyn HirDatabase,
2086 place: &HirPlace,
2087) -> impl Iterator<Item = PlaceElem> {
2088 place.projections.iter().enumerate().map(|(i, proj)| match proj.kind {
2089 HirProjectionKind::Deref => ProjectionElem::Deref,
2090 HirProjectionKind::Field { field_idx, variant_idx: _ } => {
2091 let ty = place.ty_before_projection(i);
2092 match ty.kind() {
2093 TyKind::Tuple(_) => ProjectionElem::Field(FieldIndex(field_idx)),
2094 TyKind::Adt(_, _) => {
2095 let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx));
2096 ProjectionElem::Field(local_field_id.into())
2097 }
2098 _ => panic!("unexpected type"),
2099 }
2100 }
2101 _ => panic!("unexpected projection"),
2102 })
2103}
2104
2105fn cast_kind<'db>(
2106 db: &'db dyn HirDatabase,
2107 source_ty: Ty<'db>,
2108 target_ty: Ty<'db>,
2109) -> Result<'db, CastKind> {
2110 let from = CastTy::from_ty(db, source_ty);
2111 let cast = CastTy::from_ty(db, target_ty);
2112 Ok(match (from, cast) {
2113 (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
2114 CastKind::PointerExposeAddress
2115 }
2116 (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress,
2117 (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt,
2118 (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr,
2119 (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt,
2120 (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat,
2121 (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat,
2122 (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr,
2123 _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"),
2124 })
2125}
2126
2127#[salsa::tracked(returns(as_ref), cycle_result = mir_body_for_closure_cycle_result)]
2128pub fn mir_body_for_closure_query<'db>(
2129 db: &'db dyn HirDatabase,
2130 closure: InternedClosureId<'db>,
2131) -> Result<'db, MirBody<'db>> {
2132 let InternedClosure { owner: body_owner, expr, .. } = closure.loc(db);
2133 let store = ExpressionStore::of(db, body_owner.expression_store_owner(db));
2134 let infer = InferenceResult::of(db, body_owner);
2135 let Expr::Closure { args, body: root, .. } = &store[expr] else {
2136 implementation_error!("closure expression is not closure");
2137 };
2138 let crate::next_solver::TyKind::Closure(_, substs) = infer.expr_ty(expr).kind() else {
2139 implementation_error!("closure expression is not closure");
2140 };
2141 let kind = substs.as_closure().kind();
2142 let captures = infer.closures_data[&expr].min_captures.values().flatten();
2143 let mut ctx = MirLowerCtx::new(db, body_owner, store, infer);
2144
2145 ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() });
2147 let closure_local = ctx.result.locals.alloc(Local {
2148 ty: match kind {
2149 rustc_type_ir::ClosureKind::FnOnce => infer.expr_ty(expr),
2150 rustc_type_ir::ClosureKind::FnMut => Ty::new_ref(
2151 ctx.interner(),
2152 Region::error(ctx.interner()),
2153 infer.expr_ty(expr),
2154 Mutability::Mut,
2155 ),
2156 rustc_type_ir::ClosureKind::Fn => Ty::new_ref(
2157 ctx.interner(),
2158 Region::error(ctx.interner()),
2159 infer.expr_ty(expr),
2160 Mutability::Not,
2161 ),
2162 }
2163 .store(),
2164 });
2165 ctx.result.param_locals.push(closure_local);
2166
2167 let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe);
2168 let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr);
2169 let current = ctx.lower_params_and_bindings(
2170 args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)),
2171 None,
2172 |_| true,
2173 )?;
2174
2175 let is_by_ref_closure = match kind {
2178 rustc_type_ir::ClosureKind::Fn | rustc_type_ir::ClosureKind::FnMut => true,
2179 rustc_type_ir::ClosureKind::FnOnce => false,
2180 };
2181 let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedPlace, LocalId)>> = FxHashMap::default();
2182 for (capture_idx, capture) in captures.enumerate() {
2183 let capture_local = ctx.result.locals.alloc(Local { ty: capture.captured_ty(db).store() });
2184 ctx.push_storage_live_for_local(capture_local, current, MirSpan::Unknown)?;
2185 let mut projections = Vec::with_capacity(usize::from(is_by_ref_closure) + 1);
2186 if is_by_ref_closure {
2187 projections.push(ProjectionElem::Deref);
2188 }
2189 projections.push(ProjectionElem::Field(FieldIndex(capture_idx as u32)));
2190 let capture_param_place = Place {
2191 local: closure_local,
2192 projection: Projection::new_from_slice(&projections).store(),
2193 };
2194 let capture_local_place =
2195 Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() };
2196 let capture_local_rvalue =
2197 Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None });
2198 ctx.push_assignment(
2199 current,
2200 capture_local_place.as_ref(),
2201 capture_local_rvalue,
2202 MirSpan::Unknown,
2203 );
2204
2205 let local = capture.captured_local();
2206 let local = ctx.binding_local(local)?;
2207 upvar_map.entry(local).or_default().push((capture, capture_local));
2208
2209 ctx.result
2210 .upvar_locals
2211 .entry(capture.captured_local())
2212 .or_default()
2213 .push((capture_local, capture.place.clone()));
2214 }
2215
2216 ctx.resolver.reset_to_guard(resolver_guard);
2217 if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
2218 let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
2219 ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
2220 }
2221 let mut err = None;
2222 ctx.result.walk_places(|mir_place| {
2223 let mir_projections = mir_place.projection.lookup();
2224 if let Some(hir_places) = upvar_map.get(&mir_place.local) {
2225 let projections = hir_places.iter().find_map(|hir_place| {
2226 let iter = mir_projections
2227 .iter()
2228 .cloned()
2229 .zip_longest(convert_closure_capture_projections(db, &hir_place.0.place))
2230 .enumerate();
2231
2232 for (idx, item) in iter {
2233 match item {
2234 EitherOrBoth::Both(mir, hir) => {
2235 if mir != hir {
2236 return None;
2238 }
2239 }
2240 EitherOrBoth::Right(_) => {
2241 return Some((mir_projections.len(), hir_place));
2248 }
2249 EitherOrBoth::Left(_) => return Some((idx, hir_place)),
2251 }
2252 }
2253 Some((hir_place.0.place.projections.len(), hir_place))
2255 });
2256 match projections {
2257 Some((skip_projections_up_to, (hir_place, upvar_local))) => {
2258 mir_place.local = *upvar_local;
2259 let maybe_deref: &[PlaceElem] =
2260 if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] };
2261 mir_place.projection = Projection::new_from_iter(
2262 maybe_deref
2263 .iter()
2264 .copied()
2265 .chain(mir_projections[skip_projections_up_to..].iter().copied()),
2266 )
2267 .store();
2268 }
2269 None => err = Some(mir_place.clone()),
2270 }
2271 }
2272 });
2273 ctx.result.binding_locals = ctx
2274 .result
2275 .binding_locals
2276 .into_iter()
2277 .filter(|it| ctx.store.binding_owner(it.0) == Some(expr))
2278 .collect();
2279 if let Some(err) = err {
2280 return Err(MirLowerError::UnresolvedUpvar(err));
2281 }
2282 ctx.result.shrink_to_fit();
2283 Ok(ctx.result)
2284}
2285
2286#[salsa::tracked(returns(as_ref), cycle_result = mir_body_cycle_result)]
2287pub fn mir_body_query<'db>(
2288 db: &'db dyn HirDatabase,
2289 def: InferBodyId<'db>,
2290) -> Result<'db, MirBody<'db>> {
2291 let krate = def.krate(db);
2292 let edition = krate.data(db).edition;
2293 let detail = match def {
2294 InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => {
2295 FunctionSignature::of(db, it).name.display(db, edition).to_string()
2296 }
2297 InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(it)) => {
2298 StaticSignature::of(db, it).name.display(db, edition).to_string()
2299 }
2300 InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(it)) => ConstSignature::of(db, it)
2301 .name
2302 .clone()
2303 .unwrap_or_else(Name::missing)
2304 .display(db, edition)
2305 .to_string(),
2306 InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => {
2307 let loc = it.lookup(db);
2308 loc.name.display(db, edition).to_string()
2309 }
2310 InferBodyId::AnonConstId(_) => "{const}".to_owned(),
2311 };
2312 let _p = tracing::info_span!("mir_body_query", ?detail).entered();
2313 let (store, root_expr, self_param, params) = match def {
2314 InferBodyId::DefWithBodyId(def) => {
2315 let body = Body::of(db, def);
2316 (&**body, body.root_expr(), body.self_param.map(|param| param.formal), &*body.params)
2317 }
2318 InferBodyId::AnonConstId(def) => {
2319 let loc = def.loc(db);
2320 let store = ExpressionStore::of(db, loc.owner);
2321 (store, loc.expr, None, &[][..])
2322 }
2323 };
2324 let infer = InferenceResult::of(db, def);
2325 let mut result = lower_body_to_mir(db, def, store, infer, root_expr, self_param, params)?;
2326 result.shrink_to_fit();
2327 Ok(result)
2328}
2329
2330fn mir_body_cycle_result<'db>(
2331 _db: &'db dyn HirDatabase,
2332 _: salsa::Id,
2333 _def: InferBodyId<'db>,
2334) -> Result<'db, MirBody<'db>> {
2335 Err(MirLowerError::Loop)
2336}
2337
2338fn mir_body_for_closure_cycle_result<'db>(
2339 _db: &'db dyn HirDatabase,
2340 _: salsa::Id,
2341 _def: InternedClosureId<'db>,
2342) -> Result<'db, MirBody<'db>> {
2343 Err(MirLowerError::Loop)
2344}
2345
2346pub fn lower_body_to_mir<'db>(
2349 db: &'db dyn HirDatabase,
2350 owner: InferBodyId<'db>,
2351 store: &ExpressionStore,
2352 infer: &InferenceResult<'db>,
2353 root_expr: ExprId,
2354 self_param: Option<BindingId>,
2355 params: &[Param<PatId>],
2356) -> Result<'db, MirBody<'db>> {
2357 if let Some(fid) = owner.as_function() {
2359 let callable_sig = {
2360 let resolver = owner.resolver(db);
2361 let interner = DbInterner::new_with(db, resolver.krate());
2362 interner.liberate_late_bound_regions(
2363 fid.into(),
2364 db.callable_item_signature(fid.into()).instantiate_identity().skip_norm_wip(),
2365 )
2366 };
2367 let mut param_tys = callable_sig.inputs().iter().copied();
2368 let self_param = self_param.and_then(|id| Some((id, param_tys.next()?)));
2369
2370 lower_to_mir_with_store(
2371 db,
2372 owner,
2373 store,
2374 infer,
2375 root_expr,
2376 params.iter().map(|param| param.formal).zip(param_tys),
2377 self_param,
2378 )
2379 } else {
2380 lower_to_mir_with_store(db, owner, store, infer, root_expr, iter::empty(), None)
2381 }
2382}
2383
2384pub fn lower_to_mir_with_store<'db>(
2389 db: &'db dyn HirDatabase,
2390 owner: InferBodyId<'db>,
2391 store: &ExpressionStore,
2392 infer: &InferenceResult<'db>,
2393 root_expr: ExprId,
2394 params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,
2395 self_param: Option<(BindingId, Ty<'db>)>,
2396) -> Result<'db, MirBody<'db>> {
2397 if infer.has_type_mismatches() || infer.is_erroneous() {
2398 return Err(MirLowerError::HasErrors);
2399 }
2400 let mut ctx = MirLowerCtx::new(db, owner, store, infer);
2401 ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr).store() });
2403 let expected_binding_owner =
2404 if matches!(owner, InferBodyId::DefWithBodyId(_)) { None } else { Some(root_expr) };
2405 let binding_picker = |b: BindingId| ctx.store.binding_owner(b) == expected_binding_owner;
2406 let current = ctx.lower_params_and_bindings(params, self_param, binding_picker)?;
2407 if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
2408 let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
2409 ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
2410 }
2411 Ok(ctx.result)
2412}