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