1//! This module generates a polymorphic MIR from a hir body23use std::{fmt::Write, iter, mem};45use base_db::Crate;6use hir_def::{7 AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule,8 ItemContainerId, LocalFieldId, Lookup, TraitId,9 expr_store::{Body, ExpressionStore, HygieneId, 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 span::{Edition, FileId};27use syntax::TextRange;2829use crate::{30 Adjust, Adjustment, AutoBorrow, CallableDefId, InferBodyId, ParamEnvAndCrate,31 consteval::ConstEvalError,32 db::{GeneralConstId, HirDatabase, InternedClosure, InternedClosureId},33 display::{DisplayTarget, HirDisplay, hir_display_with_store},34 generics::generics,35 infer::{36 CaptureSourceStack, CapturedPlace, UpvarCapture,37 cast::CastTy,38 closure::analysis::expr_use_visitor::{39 Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,40 },41 },42 inhabitedness::is_ty_uninhabited_from,43 layout::LayoutError,44 method_resolution::CandidateId,45 mir::{46 AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Expr,47 FieldIndex, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan,48 Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue,49 Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId,50 return_slot,51 },52 next_solver::{53 Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind,54 TypingMode, UnevaluatedConst,55 abi::Safety,56 infer::{DbInternerInferExt, InferCtxt},57 },58};5960use super::{OperandKind, PlaceRef};6162mod as_place;63mod pattern_matching;64#[cfg(test)]65mod tests;6667#[derive(Debug, Clone)]68struct LoopBlocks {69 begin: BasicBlockId,70 /// `None` for loops that are not terminating71 end: Option<BasicBlockId>,72 place: Place,73 drop_scope_index: usize,74}7576#[derive(Debug, Clone, Default)]77struct DropScope {78 /// locals, in order of definition (so we should run drop glues in reverse order)79 locals: Vec<LocalId>,80}8182struct MirLowerCtx<'a, 'db> {83 result: MirBody,84 owner: InferBodyId,85 store_owner: ExpressionStoreOwnerId,86 current_loop_blocks: Option<LoopBlocks>,87 labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,88 discr_temp: Option<Place>,89 db: &'db dyn HirDatabase,90 store: &'a ExpressionStore,91 infer: &'a InferenceResult,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}9899// FIXME: Make this smaller, its stored in database queries100#[derive(Debug, Clone, PartialEq, Eq)]101pub enum MirLowerError {102 ConstEvalError(Box<str>, Box<ConstEvalError>),103 LayoutError(LayoutError),104 IncompleteExpr,105 IncompletePattern,106 /// Trying to lower a trait function, instead of an implementation107 TraitFunctionDefinition(TraitId, Name),108 UnresolvedName(String),109 RecordLiteralWithoutPath,110 UnresolvedMethod(String),111 UnresolvedField,112 UnsizedTemporary(StoredTy),113 MissingFunctionDefinition(InferBodyId, ExprId),114 HasErrors,115 /// This should never happen. Type mismatch should catch everything.116 TypeError(&'static str),117 NotSupported(String),118 ContinueWithoutLoop,119 BreakWithoutLoop,120 Loop,121 /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened122 ImplementationError(String),123 LangItemNotFound,124 MutatingRvalue,125 UnresolvedLabel,126 UnresolvedUpvar(Place),127 InaccessibleLocal,128129 // monomorphization errors:130 GenericArgNotProvided(GenericParamId, StoredGenericArgs),131}132133/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.134struct 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 }145146 /// It is useful when we want a drop scope is syntactically closed, but we don't want to execute any drop147 /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled148 /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't149 /// do anything)150 fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_, '_>) {151 std::mem::forget(self);152 ctx.pop_drop_scope_assume_dropped_internal();153 }154}155156impl Drop for DropScopeToken {157 fn drop(&mut self) {}158}159160// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since161// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be162// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful163// stack trace.164//165// impl Drop for DropScopeToken {166// fn drop(&mut self) {167// never!("Drop scope doesn't popped");168// }169// }170171impl 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.edition203 )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::IncompleteExpr233 | MirLowerError::IncompletePattern234 | MirLowerError::InaccessibleLocal235 | MirLowerError::TraitFunctionDefinition(_, _)236 | MirLowerError::UnresolvedName(_)237 | MirLowerError::RecordLiteralWithoutPath238 | MirLowerError::UnresolvedMethod(_)239 | MirLowerError::UnresolvedField240 | MirLowerError::TypeError(_)241 | MirLowerError::NotSupported(_)242 | MirLowerError::ContinueWithoutLoop243 | MirLowerError::BreakWithoutLoop244 | MirLowerError::Loop245 | MirLowerError::ImplementationError(_)246 | MirLowerError::LangItemNotFound247 | MirLowerError::MutatingRvalue248 | MirLowerError::UnresolvedLabel249 | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{self:?}")?,250 }251 Ok(())252 }253}254255macro_rules! not_supported {256 ($it: expr) => {257 return Err(MirLowerError::NotSupported(format!($it)))258 };259}260261macro_rules! implementation_error {262 ($it: expr) => {{263 ::stdx::never!("MIR lower implementation bug: {}", format!($it));264 return Err(MirLowerError::ImplementationError(format!($it)));265 }};266}267268impl From<LayoutError> for MirLowerError {269 fn from(value: LayoutError) -> Self {270 MirLowerError::LayoutError(value)271 }272}273274impl 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}287288type Result<'db, T> = std::result::Result<T, MirLowerError>;289290impl<'a, 'db> MirLowerCtx<'a, 'db> {291 fn new(292 db: &'db dyn HirDatabase,293 owner: InferBodyId,294 store: &'a ExpressionStore,295 infer: &'a InferenceResult,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 // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body?320 let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());321322 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 }339340 #[inline]341 fn interner(&self) -> DbInterner<'db> {342 self.infcx.interner343 }344345 #[inline]346 fn lang_items(&self) -> &'db LangItems {347 self.infcx.interner.lang_items()348 }349350 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 }358359 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 }378379 fn lower_expr_to_place_with_adjust(380 &mut self,381 expr_id: ExprId,382 place: PlaceRef<'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 }438439 fn lower_expr_to_place_with_borrow_adjust(440 &mut self,441 expr_id: ExprId,442 place: PlaceRef<'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 }456457 fn lower_expr_to_place(458 &mut self,459 expr_id: ExprId,460 place: PlaceRef<'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 }468469 fn lower_expr_to_place_without_adjust(470 &mut self,471 expr_id: ExprId,472 place: PlaceRef<'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 // FnDefs are zero sized, no action is needed.502 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 = self516 .resolver517 .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 result529 };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 // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed569 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 // It's probably a unit struct or a zero sized function, so no action is needed.595 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::Unsafe { id: _, statements, tail } => {653 self.lower_block_to_place(statements, current, *tail, place, expr_id.into())654 }655 Expr::Block { id: _, statements, tail, label } => {656 if let Some(label) = label {657 self.lower_loop(current, place, Some(*label), expr_id.into(), |this, begin| {658 if let Some(current) = this.lower_block_to_place(659 statements,660 begin,661 *tail,662 place,663 expr_id.into(),664 )? {665 let end = this.current_loop_end()?;666 this.set_goto(current, end, expr_id.into());667 }668 Ok(())669 })670 } else {671 self.lower_block_to_place(statements, current, *tail, place, expr_id.into())672 }673 }674 Expr::Loop { body, label, source: _ } => {675 self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {676 let scope = this.push_drop_scope();677 if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {678 current = scope.pop_and_drop(this, current, body.into());679 this.set_goto(current, begin, expr_id.into());680 } else {681 scope.pop_assume_dropped(this);682 }683 Ok(())684 })685 }686 Expr::Call { callee, args, .. } => {687 if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {688 let ty = Ty::new_fn_def(689 self.interner(),690 CallableDefId::FunctionId(func_id).into(),691 generic_args,692 );693 let func = Operand::from_bytes(Box::default(), ty);694 return self.lower_call_and_args(695 func,696 iter::once(*callee).chain(args.iter().copied()),697 place,698 current,699 self.is_uninhabited(expr_id),700 expr_id.into(),701 );702 }703 let callee_ty = self.expr_ty_after_adjustments(*callee);704 match callee_ty.kind() {705 TyKind::FnDef(..) => {706 let func = Operand::from_bytes(Box::default(), callee_ty);707 self.lower_call_and_args(708 func,709 args.iter().copied(),710 place,711 current,712 self.is_uninhabited(expr_id),713 expr_id.into(),714 )715 }716 TyKind::FnPtr(..) => {717 let Some((func, current)) =718 self.lower_expr_to_some_operand(*callee, current)?719 else {720 return Ok(None);721 };722 self.lower_call_and_args(723 func,724 args.iter().copied(),725 place,726 current,727 self.is_uninhabited(expr_id),728 expr_id.into(),729 )730 }731 TyKind::Closure(_, _) => {732 not_supported!(733 "method resolution not emitted for closure (Are Fn traits available?)"734 );735 }736 TyKind::Error(_) => {737 Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id))738 }739 _ => Err(MirLowerError::TypeError("function call on bad type")),740 }741 }742 Expr::MethodCall { receiver, args, method_name, .. } => {743 let (func_id, generic_args) =744 self.infer.method_resolution(expr_id).ok_or_else(|| {745 MirLowerError::UnresolvedMethod(746 method_name.display(self.db, self.edition()).to_string(),747 )748 })?;749 let func = Operand::from_fn(self.db, func_id, generic_args);750 self.lower_call_and_args(751 func,752 iter::once(*receiver).chain(args.iter().copied()),753 place,754 current,755 self.is_uninhabited(expr_id),756 expr_id.into(),757 )758 }759 Expr::Match { expr, arms } => {760 let Some((cond_place, mut current)) =761 self.lower_expr_as_place(current, *expr, true)?762 else {763 return Ok(None);764 };765 self.push_fake_read(current, cond_place, expr_id.into());766 let mut end = None;767 let resolver_guard =768 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);769 for MatchArm { pat, guard, expr } in arms.iter() {770 let (then, mut otherwise) =771 self.pattern_match(current, None, cond_place, *pat)?;772 let then = if let &Some(guard) = guard {773 let next = self.new_basic_block();774 let o = otherwise.get_or_insert_with(|| self.new_basic_block());775 if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {776 self.set_terminator(777 c,778 TerminatorKind::SwitchInt {779 discr,780 targets: SwitchTargets::static_if(1, next, *o),781 },782 expr_id.into(),783 );784 }785 next786 } else {787 then788 };789 if let Some(block) = self.lower_expr_to_place(*expr, place, then)? {790 let r = end.get_or_insert_with(|| self.new_basic_block());791 self.set_goto(block, *r, expr_id.into());792 }793 match otherwise {794 Some(o) => current = o,795 None => {796 // The current pattern was irrefutable, so there is no need to generate code797 // for the rest of patterns798 break;799 }800 }801 }802 self.resolver.reset_to_guard(resolver_guard);803 if self.is_unterminated(current) {804 self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());805 }806 Ok(end)807 }808 Expr::Continue { label } => {809 let loop_data = match label {810 Some(l) => {811 self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?812 }813 None => self814 .current_loop_blocks815 .as_ref()816 .ok_or(MirLowerError::ContinueWithoutLoop)?,817 };818 let begin = loop_data.begin;819 current =820 self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());821 self.set_goto(current, begin, expr_id.into());822 Ok(None)823 }824 &Expr::Break { expr, label } => {825 if let Some(expr) = expr {826 let loop_data = match label {827 Some(l) => self828 .labeled_loop_blocks829 .get(&l)830 .ok_or(MirLowerError::UnresolvedLabel)?,831 None => self832 .current_loop_blocks833 .as_ref()834 .ok_or(MirLowerError::BreakWithoutLoop)?,835 };836 let Some(c) =837 self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)?838 else {839 return Ok(None);840 };841 current = c;842 }843 let (end, drop_scope) = match label {844 Some(l) => {845 let loop_blocks = self846 .labeled_loop_blocks847 .get(&l)848 .ok_or(MirLowerError::UnresolvedLabel)?;849 (850 loop_blocks.end.expect("We always generate end for labeled loops"),851 loop_blocks.drop_scope_index,852 )853 }854 None => (855 self.current_loop_end()?,856 self.current_loop_blocks.as_ref().unwrap().drop_scope_index,857 ),858 };859 current = self.drop_until_scope(drop_scope, current, expr_id.into());860 self.set_goto(current, end, expr_id.into());861 Ok(None)862 }863 Expr::Return { expr } => {864 if let Some(expr) = expr {865 if let Some(c) =866 self.lower_expr_to_place(*expr, return_slot().into(), current)?867 {868 current = c;869 } else {870 return Ok(None);871 }872 }873 current = self.drop_until_scope(0, current, expr_id.into());874 self.set_terminator(current, TerminatorKind::Return, expr_id.into());875 Ok(None)876 }877 Expr::Become { .. } => not_supported!("tail-calls"),878 Expr::Yield { .. } => not_supported!("yield"),879 Expr::RecordLit { fields, path, spread, .. } => {880 let spread_place = match *spread {881 RecordSpread::Expr(it) => {882 let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {883 return Ok(None);884 };885 current = c;886 Some(p)887 }888 RecordSpread::None => None,889 RecordSpread::FieldDefaults => not_supported!("empty record spread"),890 };891 let variant_id =892 self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| {893 MirLowerError::unresolved_path(894 self.db,895 path,896 self.display_target(),897 self.owner.expression_store_owner(self.db),898 self.store,899 )900 })?;901 let subst = match self.expr_ty_without_adjust(expr_id).kind() {902 TyKind::Adt(_, s) => s,903 _ => not_supported!("Non ADT record literal"),904 };905 let variant_fields = variant_id.fields(self.db);906 match variant_id {907 VariantId::EnumVariantId(_) | VariantId::StructId(_) => {908 let mut operands = vec![None; variant_fields.fields().len()];909 for RecordLitField { name, expr } in fields.iter() {910 let field_id =911 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;912 let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)?913 else {914 return Ok(None);915 };916 current = c;917 operands[u32::from(field_id.into_raw()) as usize] = Some(op);918 }919 let rvalue = Rvalue::Aggregate(920 AggregateKind::Adt(variant_id, subst.store()),921 match spread_place {922 Some(sp) if let VariantId::StructId(_) = variant_id => operands923 .into_iter()924 .enumerate()925 .map(|(i, it)| match it {926 Some(it) => it,927 None => {928 let p = sp.project(ProjectionElem::Field(FieldIndex(929 i as u32,930 )));931 Operand {932 kind: OperandKind::Copy(p.store()),933 span: None,934 }935 }936 })937 .collect(),938 Some(_) => {939 return Err(MirLowerError::TypeError(940 "functional record update syntax requires a struct",941 ));942 }943 None => operands.into_iter().collect::<Option<_>>().ok_or(944 MirLowerError::TypeError("missing field in record literal"),945 )?,946 },947 );948 self.push_assignment(current, place, rvalue, expr_id.into());949 Ok(Some(current))950 }951 VariantId::UnionId(_union_id) => {952 let [RecordLitField { name, expr }] = fields.as_ref() else {953 not_supported!("Union record literal with more than one field");954 };955 let local_id =956 variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?;957 let place = place.project(PlaceElem::Field(local_id.into()));958 self.lower_expr_to_place(*expr, place, current)959 }960 }961 }962 Expr::Await { .. } => not_supported!("await"),963 Expr::Yeet { .. } => not_supported!("yeet"),964 &Expr::Const(_) => {965 // let subst = self.placeholder_subst();966 // self.lower_const(967 // id.into(),968 // current,969 // place,970 // subst,971 // expr_id.into(),972 // self.expr_ty_without_adjust(expr_id),973 // )?;974 // Ok(Some(current))975 not_supported!("const block")976 }977 Expr::Cast { expr, type_ref: _ } => {978 let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else {979 return Ok(None);980 };981 // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178)982 // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313)983 let rvalue = if self.infer.coercion_casts.contains(expr) {984 Rvalue::Use(it)985 } else {986 let source_ty = self.infer.expr_ty(*expr);987 let target_ty = self.infer.expr_ty(expr_id);988 let cast_kind = if source_ty.as_reference().is_some() {989 CastKind::PointerCoercion(PointerCast::ArrayToPointer)990 } else {991 cast_kind(self.db, source_ty, target_ty)?992 };993994 Rvalue::Cast(cast_kind, it, target_ty.store())995 };996 self.push_assignment(current, place, rvalue, expr_id.into());997 Ok(Some(current))998 }999 Expr::Ref { expr, rawness: _, mutability } => {1000 let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {1001 return Ok(None);1002 };1003 let bk = BorrowKind::from_hir_mutability(*mutability);1004 self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into());1005 Ok(Some(current))1006 }1007 Expr::Box { expr } => {1008 let ty = self.expr_ty_after_adjustments(*expr);1009 self.push_assignment(1010 current,1011 place,1012 Rvalue::ShallowInitBoxWithAlloc(ty.store()),1013 expr_id.into(),1014 );1015 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)?1016 else {1017 return Ok(None);1018 };1019 let p = place.project(ProjectionElem::Deref);1020 self.push_assignment(current, p, operand.into(), expr_id.into());1021 Ok(Some(current))1022 }1023 Expr::Field { .. }1024 | Expr::Index { .. }1025 | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {1026 let Some((p, current)) =1027 self.lower_expr_as_place_without_adjust(current, expr_id, true)?1028 else {1029 return Ok(None);1030 };1031 self.push_assignment(1032 current,1033 place,1034 Operand { kind: OperandKind::Copy(p.store()), span: None }.into(),1035 expr_id.into(),1036 );1037 Ok(Some(current))1038 }1039 Expr::UnaryOp {1040 expr,1041 op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg),1042 } => {1043 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)?1044 else {1045 return Ok(None);1046 };1047 let operation = match op {1048 hir_def::hir::UnaryOp::Not => UnOp::Not,1049 hir_def::hir::UnaryOp::Neg => UnOp::Neg,1050 _ => unreachable!(),1051 };1052 self.push_assignment(1053 current,1054 place,1055 Rvalue::UnaryOp(operation, operand),1056 expr_id.into(),1057 );1058 Ok(Some(current))1059 }1060 Expr::BinaryOp { lhs, rhs, op } => {1061 let op: BinaryOp = op.ok_or(MirLowerError::IncompleteExpr)?;1062 let is_builtin = 'b: {1063 // Without adjust here is a hack. We assume that we know every possible adjustment1064 // for binary operator, and use without adjust to simplify our conditions.1065 let lhs_ty = self.expr_ty_without_adjust(*lhs);1066 let rhs_ty = self.expr_ty_without_adjust(*rhs);1067 if matches!(op, BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. }))1068 && matches!(lhs_ty.kind(), TyKind::RawPtr(..))1069 && matches!(rhs_ty.kind(), TyKind::RawPtr(..))1070 {1071 break 'b true;1072 }1073 let builtin_inequal_impls = matches!(1074 op,1075 BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr)1076 | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }1077 );1078 matches!(1079 lhs_ty.kind(),1080 TyKind::Bool1081 | TyKind::Char1082 | TyKind::Int(_)1083 | TyKind::Uint(_)1084 | TyKind::Float(_)1085 ) && matches!(1086 rhs_ty.kind(),1087 TyKind::Bool1088 | TyKind::Char1089 | TyKind::Int(_)1090 | TyKind::Uint(_)1091 | TyKind::Float(_)1092 ) && (lhs_ty == rhs_ty || builtin_inequal_impls)1093 };1094 if !is_builtin1095 && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id)1096 {1097 let func = Operand::from_fn(self.db, func_id, generic_args);1098 return self.lower_call_and_args(1099 func,1100 [*lhs, *rhs].into_iter(),1101 place,1102 current,1103 self.is_uninhabited(expr_id),1104 expr_id.into(),1105 );1106 }1107 if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {1108 // last adjustment is `&mut` which we don't want it.1109 let adjusts = self1110 .infer1111 .expr_adjustments1112 .get(lhs)1113 .and_then(|it| it.split_last())1114 .map(|it| it.1)1115 .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;1116 let Some((lhs_place, current)) =1117 self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?1118 else {1119 return Ok(None);1120 };1121 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?1122 else {1123 return Ok(None);1124 };1125 let r_value = Rvalue::CheckedBinaryOp(1126 op.into(),1127 Operand { kind: OperandKind::Copy(lhs_place.store()), span: None },1128 rhs_op,1129 );1130 self.push_assignment(current, lhs_place, r_value, expr_id.into());1131 return Ok(Some(current));1132 }1133 let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?1134 else {1135 return Ok(None);1136 };1137 if let hir_def::hir::BinaryOp::LogicOp(op) = op {1138 let value_to_short = match op {1139 syntax::ast::LogicOp::And => 0,1140 syntax::ast::LogicOp::Or => 1,1141 };1142 let start_of_then = self.new_basic_block();1143 self.push_assignment(1144 start_of_then,1145 place,1146 lhs_op.clone().into(),1147 expr_id.into(),1148 );1149 let end_of_then = Some(start_of_then);1150 let start_of_else = self.new_basic_block();1151 let end_of_else = self.lower_expr_to_place(*rhs, place, start_of_else)?;1152 self.set_terminator(1153 current,1154 TerminatorKind::SwitchInt {1155 discr: lhs_op,1156 targets: SwitchTargets::static_if(1157 value_to_short,1158 start_of_then,1159 start_of_else,1160 ),1161 },1162 expr_id.into(),1163 );1164 return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));1165 }1166 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?1167 else {1168 return Ok(None);1169 };1170 self.push_assignment(1171 current,1172 place,1173 Rvalue::CheckedBinaryOp(1174 match op {1175 hir_def::hir::BinaryOp::LogicOp(op) => match op {1176 hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit1177 hir_def::hir::LogicOp::Or => BinOp::BitOr,1178 },1179 hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),1180 hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),1181 hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above1182 },1183 lhs_op,1184 rhs_op,1185 ),1186 expr_id.into(),1187 );1188 Ok(Some(current))1189 }1190 &Expr::Assignment { target, value } => {1191 let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?1192 else {1193 return Ok(None);1194 };1195 self.push_fake_read(current, value, expr_id.into());1196 let resolver_guard =1197 self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);1198 current = self.pattern_match_assignment(current, value, target)?;1199 self.resolver.reset_to_guard(resolver_guard);1200 Ok(Some(current))1201 }1202 &Expr::Range { lhs, rhs, range_type: _ } => {1203 let ty = self.expr_ty_without_adjust(expr_id);1204 let Some((adt, subst)) = ty.as_adt() else {1205 return Err(MirLowerError::TypeError("Range type is not adt"));1206 };1207 let AdtId::StructId(st) = adt else {1208 return Err(MirLowerError::TypeError("Range type is not struct"));1209 };1210 let mut lp = None;1211 let mut rp = None;1212 if let Some(it) = lhs {1213 let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else {1214 return Ok(None);1215 };1216 lp = Some(o);1217 current = c;1218 }1219 if let Some(it) = rhs {1220 let Some((o, c)) = self.lower_expr_to_some_operand(it, current)? else {1221 return Ok(None);1222 };1223 rp = Some(o);1224 current = c;1225 }1226 self.push_assignment(1227 current,1228 place,1229 Rvalue::Aggregate(1230 AggregateKind::Adt(st.into(), subst.store()),1231 st.fields(self.db)1232 .fields()1233 .iter()1234 .map(|it| {1235 let o = match it.1.name.as_str() {1236 "start" => lp.take(),1237 "end" => rp.take(),1238 "exhausted" => Some(Operand::from_bytes(1239 Box::new([0]),1240 Ty::new_bool(self.interner()),1241 )),1242 _ => None,1243 };1244 o.ok_or(MirLowerError::UnresolvedField)1245 })1246 .collect::<Result<'_, _>>()?,1247 ),1248 expr_id.into(),1249 );1250 Ok(Some(current))1251 }1252 Expr::Closure { closure_kind: ClosureKind::Closure, .. } => {1253 let ty = self.expr_ty_without_adjust(expr_id);1254 let TyKind::Closure(id, _) = ty.kind() else {1255 not_supported!("closure with non closure type");1256 };1257 self.result.closures.push(id.0);1258 let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr];12591260 let span = |sources: &[CaptureSourceStack]| match sources1261 .first()1262 .map(|it| it.final_source())1263 {1264 Some(ExprOrPatId::ExprId(it)) => it.into(),1265 Some(ExprOrPatId::PatId(it)) => it.into(),1266 None => MirSpan::Unknown,1267 };1268 let convert_place = |this: &mut Self, place: &HirPlace| {1269 let (HirPlaceBase::Local(local) | HirPlaceBase::Upvar { var_id: local, .. }) =1270 place.base1271 else {1272 not_supported!("non-local capture");1273 };1274 Ok(Place {1275 local: this.binding_local(local)?,1276 projection: Projection::new_from_iter(convert_closure_capture_projections(1277 self.db, place,1278 ))1279 .store(),1280 })1281 };12821283 for (place, _, sources) in &closure_data.fake_reads {1284 let p = convert_place(self, place)?;1285 self.push_fake_read(current, p.as_ref(), span(sources));1286 }12871288 let captures = closure_data.min_captures.values().flatten();1289 let mut operands = vec![];1290 for capture in captures {1291 let p = convert_place(self, &capture.place)?;1292 match capture.info.capture_kind {1293 UpvarCapture::ByRef(bk) => {1294 let tmp_ty = capture.captured_ty(self.db);1295 // FIXME: Handle more than one span.1296 let capture_span = span(&capture.info.sources);1297 let tmp = self.temp(tmp_ty, current, capture_span)?.into();1298 self.push_assignment(1299 current,1300 tmp,1301 Rvalue::Ref(BorrowKind::from_hir(bk), p),1302 capture_span,1303 );1304 operands1305 .push(Operand { kind: OperandKind::Move(tmp.store()), span: None });1306 }1307 UpvarCapture::ByValue => {1308 operands.push(Operand { kind: OperandKind::Move(p), span: None })1309 }1310 UpvarCapture::ByUse => not_supported!("capture by use"),1311 }1312 }1313 self.push_assignment(1314 current,1315 place,1316 Rvalue::Aggregate(AggregateKind::Closure(ty.store()), operands.into()),1317 expr_id.into(),1318 );1319 Ok(Some(current))1320 }1321 Expr::Closure { closure_kind, .. } => not_supported!("{closure_kind:?} closure"),1322 Expr::Tuple { exprs } => {1323 let Some(values) = exprs1324 .iter()1325 .map(|it| {1326 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else {1327 return Ok(None);1328 };1329 current = c;1330 Ok(Some(o))1331 })1332 .collect::<Result<'_, Option<_>>>()?1333 else {1334 return Ok(None);1335 };1336 let r = Rvalue::Aggregate(1337 AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id).store()),1338 values,1339 );1340 self.push_assignment(current, place, r, expr_id.into());1341 Ok(Some(current))1342 }1343 Expr::Array(l) => match l {1344 Array::ElementList { elements, .. } => {1345 let elem_ty = match self.expr_ty_without_adjust(expr_id).kind() {1346 TyKind::Array(ty, _) => ty,1347 _ => {1348 return Err(MirLowerError::TypeError(1349 "Array expression with non array type",1350 ));1351 }1352 };1353 let Some(values) = elements1354 .iter()1355 .map(|it| {1356 let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)?1357 else {1358 return Ok(None);1359 };1360 current = c;1361 Ok(Some(o))1362 })1363 .collect::<Result<'_, Option<_>>>()?1364 else {1365 return Ok(None);1366 };1367 let r = Rvalue::Aggregate(AggregateKind::Array(elem_ty.store()), values);1368 self.push_assignment(current, place, r, expr_id.into());1369 Ok(Some(current))1370 }1371 Array::Repeat { initializer, .. } => {1372 let Some((init, current)) =1373 self.lower_expr_to_some_operand(*initializer, current)?1374 else {1375 return Ok(None);1376 };1377 let len = match self.expr_ty_without_adjust(expr_id).kind() {1378 TyKind::Array(_, len) => len,1379 _ => {1380 return Err(MirLowerError::TypeError(1381 "Array repeat expression with non array type",1382 ));1383 }1384 };1385 let r = Rvalue::Repeat(init, len.store());1386 self.push_assignment(current, place, r, expr_id.into());1387 Ok(Some(current))1388 }1389 },1390 Expr::Literal(l) => {1391 let ty = self.expr_ty_without_adjust(expr_id);1392 let op = self.lower_literal_to_operand(ty, l)?;1393 self.push_assignment(current, place, op.into(), expr_id.into());1394 Ok(Some(current))1395 }1396 Expr::Underscore => Ok(Some(current)),1397 Expr::IncludeBytes => not_supported!("include_bytes!()"),1398 }1399 }14001401 fn push_field_projection(1402 &mut self,1403 place: &mut PlaceRef<'db>,1404 expr_id: ExprId,1405 ) -> Result<'db, ()> {1406 if let Expr::Field { expr, name } = &self.store[expr_id] {1407 if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() {1408 let index =1409 name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))?1410 as u32;1411 *place = place.project(ProjectionElem::Field(FieldIndex(index)))1412 } else {1413 let field = self1414 .infer1415 .field_resolution(expr_id)1416 .ok_or(MirLowerError::UnresolvedField)?1417 .either(|f| f.local_id.into(), |t| FieldIndex(t.index));1418 *place = place.project(ProjectionElem::Field(field));1419 }1420 } else {1421 not_supported!("")1422 }1423 Ok(())1424 }14251426 fn lower_literal_or_const_to_operand(1427 &mut self,1428 ty: Ty<'db>,1429 loc: &ExprId,1430 ) -> Result<'db, Operand> {1431 match &self.store[*loc] {1432 Expr::Literal(l) => self.lower_literal_to_operand(ty, l),1433 Expr::Path(c) => {1434 let owner = self.owner;1435 let db = self.db;1436 let unresolved_name = || {1437 MirLowerError::unresolved_path(1438 self.db,1439 c,1440 DisplayTarget::from_crate(db, owner.krate(db)),1441 self.owner.expression_store_owner(self.db),1442 self.store,1443 )1444 };1445 let pr = self1446 .resolver1447 .resolve_path_in_value_ns(self.db, c, HygieneId::ROOT)1448 .ok_or_else(unresolved_name)?;1449 match pr {1450 ResolveValueResult::ValueNs(v) => {1451 if let ValueNs::ConstId(c) = v {1452 self.lower_const_to_operand(1453 GenericArgs::empty(self.interner()),1454 c.into(),1455 )1456 } else {1457 not_supported!("bad path in range pattern");1458 }1459 }1460 ResolveValueResult::Partial(_, _) => {1461 not_supported!("associated constants in range pattern")1462 }1463 }1464 }1465 _ => {1466 not_supported!("only `char` and numeric types are allowed in range patterns");1467 }1468 }1469 }14701471 fn lower_literal_to_operand(&mut self, ty: Ty<'db>, l: &Literal) -> Result<'db, Operand> {1472 let size = || {1473 self.db1474 .layout_of_ty(1475 ty.store(),1476 ParamEnvAndCrate { param_env: self.env, krate: self.krate() }.store(),1477 )1478 .map(|it| it.size.bytes_usize())1479 };1480 const USIZE_SIZE: usize = size_of::<usize>();1481 let bytes: Box<[_]> = match l {1482 hir_def::hir::Literal::String(b) => {1483 let b = b.as_str();1484 let mut data = [0; { 2 * USIZE_SIZE }];1485 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());1486 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());1487 let mm = MemoryMap::simple(b.as_bytes().into());1488 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));1489 }1490 hir_def::hir::Literal::CString(b) => {1491 let bytes = b.iter().copied().chain(iter::once(0)).collect::<Box<_>>();14921493 let mut data = [0; { 2 * USIZE_SIZE }];1494 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());1495 data[USIZE_SIZE..].copy_from_slice(&bytes.len().to_le_bytes());1496 let mm = MemoryMap::simple(bytes);1497 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));1498 }1499 hir_def::hir::Literal::ByteString(b) => {1500 let mut data = [0; { 2 * USIZE_SIZE }];1501 data[..USIZE_SIZE].copy_from_slice(&0usize.to_le_bytes());1502 data[USIZE_SIZE..].copy_from_slice(&b.len().to_le_bytes());1503 let mm = MemoryMap::simple(b.clone());1504 return Ok(Operand::from_concrete_const(Box::new(data), mm, ty));1505 }1506 hir_def::hir::Literal::Char(c) => Box::new(u32::from(*c).to_le_bytes()),1507 hir_def::hir::Literal::Bool(b) => Box::new([*b as u8]),1508 hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),1509 hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),1510 hir_def::hir::Literal::Float(f, _) => match size()? {1511 16 => Box::new(f.to_f128().to_bits().to_le_bytes()),1512 8 => Box::new(f.to_f64().to_bits().to_le_bytes()),1513 4 => Box::new(f.to_f32().to_bits().to_le_bytes()),1514 2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),1515 _ => {1516 return Err(MirLowerError::TypeError(1517 "float with size other than 2, 4, 8 or 16 bytes",1518 ));1519 }1520 },1521 };1522 Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))1523 }15241525 fn new_basic_block(&mut self) -> BasicBlockId {1526 self.result.basic_blocks.alloc(BasicBlock::default())1527 }15281529 fn lower_const(1530 &mut self,1531 const_id: GeneralConstId,1532 prev_block: BasicBlockId,1533 place: PlaceRef<'db>,1534 subst: GenericArgs<'db>,1535 span: MirSpan,1536 ) -> Result<'db, ()> {1537 let c = self.lower_const_to_operand(subst, const_id)?;1538 self.push_assignment(prev_block, place, c.into(), span);1539 Ok(())1540 }15411542 fn lower_const_to_operand(1543 &mut self,1544 subst: GenericArgs<'db>,1545 const_id: GeneralConstId,1546 ) -> Result<'db, Operand> {1547 let konst = Const::new_unevaluated(1548 self.interner(),1549 UnevaluatedConst { def: const_id.into(), args: subst },1550 );1551 let ty = match const_id {1552 GeneralConstId::ConstId(id) => self.db.value_ty(id.into()).unwrap(),1553 GeneralConstId::StaticId(id) => self.db.value_ty(id.into()).unwrap(),1554 GeneralConstId::AnonConstId(id) => id.loc(self.db).ty.get(),1555 };1556 let ty = ty.instantiate(self.interner(), subst).skip_norm_wip();1557 Ok(Operand {1558 kind: OperandKind::Constant { konst: konst.store(), ty: ty.store() },1559 span: None,1560 })1561 }15621563 fn write_bytes_to_place(1564 &mut self,1565 prev_block: BasicBlockId,1566 place: PlaceRef<'db>,1567 cv: Box<[u8]>,1568 ty: Ty<'db>,1569 span: MirSpan,1570 ) -> Result<'db, ()> {1571 self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span);1572 Ok(())1573 }15741575 fn lower_enum_variant(1576 &mut self,1577 variant_id: EnumVariantId,1578 prev_block: BasicBlockId,1579 place: PlaceRef<'db>,1580 ty: Ty<'db>,1581 fields: Box<[Operand]>,1582 span: MirSpan,1583 ) -> Result<'db, BasicBlockId> {1584 let subst = match ty.kind() {1585 TyKind::Adt(_, subst) => subst,1586 _ => implementation_error!("Non ADT enum"),1587 };1588 self.push_assignment(1589 prev_block,1590 place,1591 Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst.store()), fields),1592 span,1593 );1594 Ok(prev_block)1595 }15961597 fn lower_call_and_args(1598 &mut self,1599 func: Operand,1600 args: impl Iterator<Item = ExprId>,1601 place: PlaceRef<'db>,1602 mut current: BasicBlockId,1603 is_uninhabited: bool,1604 span: MirSpan,1605 ) -> Result<'db, Option<BasicBlockId>> {1606 let Some(args) = args1607 .map(|arg| {1608 if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {1609 current = c;1610 Ok(Some(temp))1611 } else {1612 Ok(None)1613 }1614 })1615 .collect::<Result<'_, Option<Vec<_>>>>()?1616 else {1617 return Ok(None);1618 };1619 self.lower_call(func, args.into(), place, current, is_uninhabited, span)1620 }16211622 fn lower_call(1623 &mut self,1624 func: Operand,1625 args: Box<[Operand]>,1626 place: PlaceRef<'db>,1627 current: BasicBlockId,1628 is_uninhabited: bool,1629 span: MirSpan,1630 ) -> Result<'db, Option<BasicBlockId>> {1631 let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };1632 self.set_terminator(1633 current,1634 TerminatorKind::Call {1635 func,1636 args,1637 destination: place.store(),1638 target: b,1639 cleanup: None,1640 from_hir_call: true,1641 },1642 span,1643 );1644 Ok(b)1645 }16461647 fn is_unterminated(&mut self, source: BasicBlockId) -> bool {1648 self.result.basic_blocks[source].terminator.is_none()1649 }16501651 fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {1652 self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });1653 }16541655 fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {1656 self.set_terminator(source, TerminatorKind::Goto { target }, span);1657 }16581659 fn expr_ty_without_adjust(&self, e: ExprId) -> Ty<'db> {1660 self.infer.expr_ty(e)1661 }16621663 fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {1664 let mut ty = None;1665 if let Some(it) = self.infer.expr_adjustments.get(&e)1666 && let Some(it) = it.last()1667 {1668 ty = Some(it.target.as_ref());1669 }1670 ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))1671 }16721673 fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {1674 self.result.basic_blocks[block].statements.push(statement);1675 }16761677 fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) {1678 self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span));1679 }16801681 fn push_assignment(1682 &mut self,1683 block: BasicBlockId,1684 place: PlaceRef<'db>,1685 rvalue: Rvalue,1686 span: MirSpan,1687 ) {1688 self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span));1689 }16901691 fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> {1692 match &self.discr_temp {1693 Some(it) => it.as_ref(),1694 None => {1695 // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well1696 let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128);1697 let tmp: PlaceRef<'_> = self1698 .temp(discr_ty, current, MirSpan::Unknown)1699 .expect("discr_ty is never unsized")1700 .into();1701 self.discr_temp = Some(tmp.store());1702 tmp1703 }1704 }1705 }17061707 fn lower_loop(1708 &mut self,1709 prev_block: BasicBlockId,1710 place: PlaceRef<'db>,1711 label: Option<LabelId>,1712 span: MirSpan,1713 f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>,1714 ) -> Result<'db, Option<BasicBlockId>> {1715 let begin = self.new_basic_block();1716 let prev = self.current_loop_blocks.replace(LoopBlocks {1717 begin,1718 end: None,1719 place: place.store(),1720 drop_scope_index: self.drop_scopes.len(),1721 });1722 let prev_label = if let Some(label) = label {1723 // We should generate the end now, to make sure that it wouldn't change later. It is1724 // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but1725 // it should not affect correctness.1726 self.current_loop_end()?;1727 self.labeled_loop_blocks1728 .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())1729 } else {1730 None1731 };1732 self.set_goto(prev_block, begin, span);1733 f(self, begin)?;1734 let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(1735 MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_owned()),1736 )?;1737 if let Some(prev) = prev_label {1738 self.labeled_loop_blocks.insert(label.unwrap(), prev);1739 }1740 Ok(my.end)1741 }17421743 fn has_adjustments(&self, expr_id: ExprId) -> bool {1744 !self.infer.expr_adjustments.get(&expr_id).map(|it| it.is_empty()).unwrap_or(true)1745 }17461747 fn merge_blocks(1748 &mut self,1749 b1: Option<BasicBlockId>,1750 b2: Option<BasicBlockId>,1751 span: MirSpan,1752 ) -> Option<BasicBlockId> {1753 match (b1, b2) {1754 (None, None) => None,1755 (None, Some(b)) | (Some(b), None) => Some(b),1756 (Some(b1), Some(b2)) => {1757 let bm = self.new_basic_block();1758 self.set_goto(b1, bm, span);1759 self.set_goto(b2, bm, span);1760 Some(bm)1761 }1762 }1763 }17641765 fn current_loop_end(&mut self) -> Result<'db, BasicBlockId> {1766 let r = match self1767 .current_loop_blocks1768 .as_mut()1769 .ok_or(MirLowerError::ImplementationError(1770 "Current loop access out of loop".to_owned(),1771 ))?1772 .end1773 {1774 Some(it) => it,1775 None => {1776 let s = self.new_basic_block();1777 self.current_loop_blocks1778 .as_mut()1779 .ok_or(MirLowerError::ImplementationError(1780 "Current loop access out of loop".to_owned(),1781 ))?1782 .end = Some(s);1783 s1784 }1785 };1786 Ok(r)1787 }17881789 fn is_uninhabited(&self, expr_id: ExprId) -> bool {1790 is_ty_uninhabited_from(1791 &self.infcx,1792 self.infer.expr_ty(expr_id),1793 self.owner.module(self.db),1794 self.env,1795 )1796 }17971798 /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and1799 /// `Drop` in the appropriated places.1800 fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<'db, ()> {1801 let l = self.binding_local(b)?;1802 self.push_storage_live_for_local(l, current, MirSpan::BindingId(b))1803 }18041805 fn push_storage_live_for_local(1806 &mut self,1807 l: LocalId,1808 current: BasicBlockId,1809 span: MirSpan,1810 ) -> Result<'db, ()> {1811 self.drop_scopes.last_mut().unwrap().locals.push(l);1812 self.push_statement(current, StatementKind::StorageLive(l).with_span(span));1813 Ok(())1814 }18151816 fn lower_block_to_place(1817 &mut self,1818 statements: &[hir_def::hir::Statement],1819 mut current: BasicBlockId,1820 tail: Option<ExprId>,1821 place: PlaceRef<'db>,1822 span: MirSpan,1823 ) -> Result<'db, Option<Idx<BasicBlock>>> {1824 let scope = self.push_drop_scope();1825 for statement in statements.iter() {1826 match statement {1827 hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {1828 if let Some(expr_id) = initializer {1829 let else_block;1830 let Some((init_place, c)) =1831 self.lower_expr_as_place(current, *expr_id, true)?1832 else {1833 scope.pop_assume_dropped(self);1834 return Ok(None);1835 };1836 current = c;1837 self.push_fake_read(current, init_place, span);1838 // Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations1839 // and has all declarations of the `let`.1840 let resolver_guard = self.resolver.update_to_inner_scope(1841 self.db,1842 self.store_owner,1843 *expr_id,1844 );1845 (current, else_block) =1846 self.pattern_match(current, None, init_place, *pat)?;1847 self.resolver.reset_to_guard(resolver_guard);1848 match (else_block, else_branch) {1849 (None, _) => (),1850 (Some(else_block), None) => {1851 self.set_terminator(else_block, TerminatorKind::Unreachable, span);1852 }1853 (Some(else_block), Some(else_branch)) => {1854 if let Some((_, b)) =1855 self.lower_expr_as_place(else_block, *else_branch, true)?1856 {1857 self.set_terminator(b, TerminatorKind::Unreachable, span);1858 }1859 }1860 }1861 } else {1862 let mut err = None;1863 self.store.walk_bindings_in_pat(*pat, |b| {1864 if let Err(e) = self.push_storage_live(b, current) {1865 err = Some(e);1866 }1867 });1868 if let Some(e) = err {1869 return Err(e);1870 }1871 }1872 }1873 &hir_def::hir::Statement::Expr { expr, has_semi: _ } => {1874 let scope2 = self.push_drop_scope();1875 let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else {1876 scope2.pop_assume_dropped(self);1877 scope.pop_assume_dropped(self);1878 return Ok(None);1879 };1880 self.push_fake_read(c, p, expr.into());1881 current = scope2.pop_and_drop(self, c, expr.into());1882 }1883 hir_def::hir::Statement::Item(_) => (),1884 }1885 }1886 if let Some(tail) = tail {1887 let Some(c) = self.lower_expr_to_place(tail, place, current)? else {1888 scope.pop_assume_dropped(self);1889 return Ok(None);1890 };1891 current = c;1892 }1893 current = scope.pop_and_drop(self, current, span);1894 Ok(Some(current))1895 }18961897 fn lower_params_and_bindings(1898 &mut self,1899 params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,1900 self_binding: Option<(BindingId, Ty<'db>)>,1901 pick_binding: impl Fn(BindingId) -> bool,1902 ) -> Result<'db, BasicBlockId> {1903 let base_param_count = self.result.param_locals.len();1904 let self_binding = match self_binding {1905 Some((self_binding, ty)) => {1906 let local_id = self.result.locals.alloc(Local { ty: ty.store() });1907 self.drop_scopes.last_mut().unwrap().locals.push(local_id);1908 self.result.binding_locals.insert(self_binding, local_id);1909 self.result.param_locals.push(local_id);1910 Some(self_binding)1911 }1912 None => None,1913 };1914 self.result.param_locals.extend(params.clone().map(|(it, ty)| {1915 let local_id = self.result.locals.alloc(Local { ty: ty.store() });1916 self.drop_scopes.last_mut().unwrap().locals.push(local_id);1917 if let Pat::Bind { id, subpat: None } = self.store[it]1918 && matches!(1919 self.store[id].mode,1920 BindingAnnotation::Unannotated | BindingAnnotation::Mutable1921 )1922 {1923 self.result.binding_locals.insert(id, local_id);1924 }1925 local_id1926 }));1927 // and then rest of bindings1928 for (id, _) in self.store.bindings() {1929 if !pick_binding(id) {1930 continue;1931 }1932 if !self.result.binding_locals.contains_idx(id) {1933 self.result.binding_locals.insert(1934 id,1935 self.result.locals.alloc(Local { ty: self.infer.binding_ty(id).store() }),1936 );1937 }1938 }1939 let mut current = self.result.start_block;1940 if let Some(self_binding) = self_binding {1941 let local = self.result.param_locals.clone()[base_param_count];1942 if local != self.binding_local(self_binding)? {1943 let r = self.match_self_param(self_binding, current, local)?;1944 if let Some(b) = r.1 {1945 self.set_terminator(b, TerminatorKind::Unreachable, MirSpan::SelfParam);1946 }1947 current = r.0;1948 }1949 }1950 let local_params = self1951 .result1952 .param_locals1953 .clone()1954 .into_iter()1955 .skip(base_param_count + self_binding.is_some() as usize);1956 for ((param, _), local) in params.zip(local_params) {1957 if let Pat::Bind { id, .. } = self.store[param]1958 && local == self.binding_local(id)?1959 {1960 continue;1961 }1962 let r = self.pattern_match(current, None, local.into(), param)?;1963 if let Some(b) = r.1 {1964 self.set_terminator(b, TerminatorKind::Unreachable, param.into());1965 }1966 current = r.0;1967 }1968 Ok(current)1969 }19701971 fn binding_local(&self, b: BindingId) -> Result<'db, LocalId> {1972 match self.result.binding_locals.get(b) {1973 Some(it) => Ok(*it),1974 None => {1975 // FIXME: It should never happens, but currently it will happen in some cases, not sure when exactly.1976 // never!("Using inaccessible local for binding is always a bug");1977 Err(MirLowerError::InaccessibleLocal)1978 }1979 }1980 }19811982 fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<'db, i128> {1983 let r = self.db.const_eval_discriminant(variant);1984 match r {1985 Ok(r) => Ok(r),1986 Err(e) => {1987 let edition = self.edition();1988 let db = self.db;1989 let loc = variant.lookup(db);1990 let name = format!(1991 "{}::{}",1992 EnumSignature::of(db, loc.parent).name.display(db, edition),1993 loc.parent1994 .enum_variants(self.db)1995 .variant_name_by_id(variant)1996 .unwrap()1997 .display(db, edition),1998 );1999 Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))2000 }
Findings
✓ No findings reported for this file.