Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/lower.rs')
-rw-r--r--crates/hir-ty/src/mir/lower.rs1793
1 files changed, 1077 insertions, 716 deletions
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index c4dd7c0ace..aad1a82f29 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1,60 +1,88 @@
//! This module generates a polymorphic MIR from a hir body
-use std::{iter, mem, sync::Arc};
+use std::{fmt::Write, iter, mem};
+use base_db::FileId;
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
body::Body,
- expr::{
- Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
- RecordLitField,
+ data::adt::{StructKind, VariantData},
+ hir::{
+ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
+ LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
},
lang_item::{LangItem, LangItemTarget},
- layout::LayoutError,
path::Path,
- resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
- DefWithBodyId, EnumVariantId, HasModule,
+ resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
+ AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
+ TraitId, TypeOrConstParamId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
+use rustc_hash::FxHashMap;
+use syntax::TextRange;
+use triomphe::Arc;
use crate::{
- consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
- inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
- utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+ consteval::ConstEvalError,
+ db::HirDatabase,
+ display::HirDisplay,
+ infer::{CaptureKind, CapturedItem, TypeMismatch},
+ inhabitedness::is_ty_uninhabited_from,
+ layout::LayoutError,
+ mapping::ToChalk,
+ static_lifetime,
+ traits::FnTrait,
+ utils::{generics, ClosureSubst},
+ Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
};
use super::*;
mod as_place;
+mod pattern_matching;
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
struct LoopBlocks {
begin: BasicBlockId,
/// `None` for loops that are not terminating
end: Option<BasicBlockId>,
+ place: Place,
+ drop_scope_index: usize,
+}
+
+#[derive(Debug, Clone, Default)]
+struct DropScope {
+ /// locals, in order of definition (so we should run drop glues in reverse order)
+ locals: Vec<LocalId>,
}
struct MirLowerCtx<'a> {
result: MirBody,
owner: DefWithBodyId,
current_loop_blocks: Option<LoopBlocks>,
+ labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
db: &'a dyn HirDatabase,
body: &'a Body,
infer: &'a InferenceResult,
+ drop_scopes: Vec<DropScope>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError {
- ConstEvalError(Box<ConstEvalError>),
+ ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
+ IncompletePattern,
+ /// Trying to lower a trait function, instead of an implementation
+ TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String),
RecordLiteralWithoutPath,
- UnresolvedMethod,
+ UnresolvedMethod(String),
UnresolvedField,
- MissingFunctionDefinition,
+ UnsizedTemporary(Ty),
+ MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch),
/// This should be never happen. Type mismatch should catch everything.
TypeError(&'static str),
@@ -63,9 +91,114 @@ pub enum MirLowerError {
BreakWithoutLoop,
Loop,
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
- ImplementationError(&'static str),
+ ImplementationError(String),
LangItemNotFound(LangItem),
MutatingRvalue,
+ UnresolvedLabel,
+ UnresolvedUpvar(Place),
+ UnaccessableLocal,
+
+ // monomorphization errors:
+ GenericArgNotProvided(TypeOrConstParamId, Substitution),
+}
+
+/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
+struct DropScopeToken;
+impl DropScopeToken {
+ fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_internal(current)
+ }
+
+ /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
+ /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled
+ /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't
+ /// do anything)
+ fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_assume_dropped_internal();
+ }
+}
+
+// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
+// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
+// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
+// stack trace.
+//
+// impl Drop for DropScopeToken {
+// fn drop(&mut self) {
+// never!("Drop scope doesn't popped");
+// }
+// }
+
+impl MirLowerError {
+ pub fn pretty_print(
+ &self,
+ f: &mut String,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ match self {
+ MirLowerError::ConstEvalError(name, e) => {
+ writeln!(f, "In evaluating constant {name}")?;
+ match &**e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
+ ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
+ }
+ }
+ MirLowerError::MissingFunctionDefinition(owner, x) => {
+ let body = db.body(*owner);
+ writeln!(
+ f,
+ "Missing function definition for {}",
+ body.pretty_print_expr(db.upcast(), *owner, *x)
+ )?;
+ }
+ MirLowerError::TypeMismatch(e) => {
+ writeln!(
+ f,
+ "Type mismatch: Expected {}, found {}",
+ e.expected.display(db),
+ e.actual.display(db),
+ )?;
+ }
+ MirLowerError::GenericArgNotProvided(id, subst) => {
+ let parent = id.parent;
+ let param = &db.generic_params(parent).type_or_consts[id.local_id];
+ writeln!(
+ f,
+ "Generic arg not provided for {}",
+ param.name().unwrap_or(&Name::missing()).display(db.upcast())
+ )?;
+ writeln!(f, "Provided args: [")?;
+ for g in subst.iter(Interner) {
+ write!(f, " {},", g.display(db).to_string())?;
+ }
+ writeln!(f, "]")?;
+ }
+ MirLowerError::LayoutError(_)
+ | MirLowerError::UnsizedTemporary(_)
+ | MirLowerError::IncompleteExpr
+ | MirLowerError::IncompletePattern
+ | MirLowerError::UnaccessableLocal
+ | MirLowerError::TraitFunctionDefinition(_, _)
+ | MirLowerError::UnresolvedName(_)
+ | MirLowerError::RecordLiteralWithoutPath
+ | MirLowerError::UnresolvedMethod(_)
+ | MirLowerError::UnresolvedField
+ | MirLowerError::TypeError(_)
+ | MirLowerError::NotSupported(_)
+ | MirLowerError::ContinueWithoutLoop
+ | MirLowerError::BreakWithoutLoop
+ | MirLowerError::Loop
+ | MirLowerError::ImplementationError(_)
+ | MirLowerError::LangItemNotFound(_)
+ | MirLowerError::MutatingRvalue
+ | MirLowerError::UnresolvedLabel
+ | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
+ }
+ Ok(())
+ }
}
macro_rules! not_supported {
@@ -76,20 +209,11 @@ macro_rules! not_supported {
macro_rules! implementation_error {
($x: expr) => {{
- ::stdx::never!("MIR lower implementation bug: {}", $x);
- return Err(MirLowerError::ImplementationError($x));
+ ::stdx::never!("MIR lower implementation bug: {}", format!($x));
+ return Err(MirLowerError::ImplementationError(format!($x)));
}};
}
-impl From<ConstEvalError> for MirLowerError {
- fn from(value: ConstEvalError) -> Self {
- match value {
- ConstEvalError::MirLowerError(e) => e,
- _ => MirLowerError::ConstEvalError(Box::new(value)),
- }
- }
-}
-
impl From<LayoutError> for MirLowerError {
fn from(value: LayoutError) -> Self {
MirLowerError::LayoutError(value)
@@ -104,12 +228,51 @@ impl MirLowerError {
type Result<T> = std::result::Result<T, MirLowerError>;
-impl MirLowerCtx<'_> {
- fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+impl<'ctx> MirLowerCtx<'ctx> {
+ fn new(
+ db: &'ctx dyn HirDatabase,
+ owner: DefWithBodyId,
+ body: &'ctx Body,
+ infer: &'ctx InferenceResult,
+ ) -> Self {
+ let mut basic_blocks = Arena::new();
+ let start_block = basic_blocks.alloc(BasicBlock {
+ statements: vec![],
+ terminator: None,
+ is_cleanup: false,
+ });
+ let locals = Arena::new();
+ let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+ let mir = MirBody {
+ basic_blocks,
+ locals,
+ start_block,
+ binding_locals,
+ param_locals: vec![],
+ owner,
+ closures: vec![],
+ };
+ let ctx = MirLowerCtx {
+ result: mir,
+ db,
+ infer,
+ body,
+ owner,
+ current_loop_blocks: None,
+ labeled_loop_blocks: Default::default(),
+ discr_temp: None,
+ drop_scopes: vec![DropScope::default()],
+ };
+ ctx
+ }
+
+ fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
- implementation_error!("unsized temporaries");
+ return Err(MirLowerError::UnsizedTemporary(ty));
}
- Ok(self.result.locals.alloc(Local { ty }))
+ let l = self.result.locals.alloc(Local { ty });
+ self.push_storage_live_for_local(l, current, span)?;
+ Ok(l)
}
fn lower_expr_to_some_operand(
@@ -120,7 +283,7 @@ impl MirLowerCtx<'_> {
if !self.has_adjustments(expr_id) {
match &self.body.exprs[expr_id] {
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
}
_ => (),
@@ -142,7 +305,8 @@ impl MirLowerCtx<'_> {
match adjustments.split_last() {
Some((last, rest)) => match &last.kind {
Adjust::NeverToAny => {
- let temp = self.temp(TyKind::Never.intern(Interner))?;
+ let temp =
+ self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?;
self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
}
Adjust::Deref(_) => {
@@ -200,65 +364,82 @@ impl MirLowerCtx<'_> {
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] {
- Expr::Missing => Err(MirLowerError::IncompleteExpr),
+ Expr::Missing => {
+ if let DefWithBodyId::FunctionId(f) = self.owner {
+ let assoc = self.db.lookup_intern_function(f);
+ if let ItemContainerId::TraitId(t) = assoc.container {
+ let name = &self.db.function_data(f).name;
+ return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
+ }
+ }
+ Err(MirLowerError::IncompleteExpr)
+ },
Expr::Path(p) => {
- let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let pr = resolver
- .resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
- .ok_or_else(unresolved_name)?;
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => {
- if let Some(assoc) = self
- .infer
- .assoc_resolutions_for_expr(expr_id)
- {
- match assoc.0 {
- hir_def::AssocItemId::ConstId(c) => {
- self.lower_const(c, current, place, expr_id.into())?;
- return Ok(Some(current))
- },
- _ => not_supported!("associated functions and types"),
- }
- } else if let Some(variant) = self
- .infer
- .variant_resolution_for_expr(expr_id)
- {
- match variant {
- VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
- VariantId::StructId(s) => ValueNs::StructId(s),
- VariantId::UnionId(_) => implementation_error!("Union variant as path"),
- }
- } else {
- return Err(unresolved_name());
+ let pr = if let Some((assoc, subst)) = self
+ .infer
+ .assoc_resolutions_for_expr(expr_id)
+ {
+ match assoc {
+ hir_def::AssocItemId::ConstId(c) => {
+ self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ return Ok(Some(current))
+ },
+ hir_def::AssocItemId::FunctionId(_) => {
+ // FnDefs are zero sized, no action is needed.
+ return Ok(Some(current))
}
+ hir_def::AssocItemId::TypeAliasId(_) => {
+ // FIXME: If it is unreachable, use proper error instead of `not_supported`.
+ not_supported!("associated functions and types")
+ },
}
+ } else if let Some(variant) = self
+ .infer
+ .variant_resolution_for_expr(expr_id)
+ {
+ match variant {
+ VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+ VariantId::StructId(s) => ValueNs::StructId(s),
+ VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+ }
+ } else {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+ resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?
};
match pr {
- ValueNs::LocalBinding(pat_id) => {
+ ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
+ let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else {
+ return Ok(None);
+ };
self.push_assignment(
current,
place,
- Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+ Operand::Copy(temp).into(),
expr_id.into(),
);
Ok(Some(current))
}
ValueNs::ConstId(const_id) => {
- self.lower_const(const_id, current, place, expr_id.into())?;
+ self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
Ok(Some(current))
}
ValueNs::EnumVariantId(variant_id) => {
- let ty = self.infer.type_of_expr[expr_id].clone();
- let current = self.lower_enum_variant(
- variant_id,
- current,
- place,
- ty,
- vec![],
- expr_id.into(),
- )?;
+ let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
+ if variant_data.variant_data.kind() == StructKind::Unit {
+ let ty = self.infer.type_of_expr[expr_id].clone();
+ current = self.lower_enum_variant(
+ variant_id,
+ current,
+ place,
+ ty,
+ Box::new([]),
+ expr_id.into(),
+ )?;
+ }
+ // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
Ok(Some(current))
}
ValueNs::GenericParam(p) => {
@@ -266,7 +447,7 @@ impl MirLowerCtx<'_> {
not_supported!("owner without generic def id");
};
let gen = generics(self.db.upcast(), def);
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
self.push_assignment(
current,
place,
@@ -287,7 +468,7 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- ValueNs::StructId(_) => {
+ ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
// It's probably a unit struct or a zero sized function, so no action is needed.
Ok(Some(current))
}
@@ -311,12 +492,13 @@ impl MirLowerCtx<'_> {
};
self.set_terminator(
current,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
},
+ expr_id.into(),
);
- Ok(self.merge_blocks(end_of_then, end_of_else))
+ Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
}
Expr::Let { pat, expr } => {
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
@@ -326,9 +508,7 @@ impl MirLowerCtx<'_> {
current,
None,
cond_place,
- self.expr_ty_after_adjustments(*expr),
*pat,
- BindingAnnotation::Unannotated,
)?;
self.write_bytes_to_place(
then_target,
@@ -346,141 +526,107 @@ impl MirLowerCtx<'_> {
MirSpan::Unknown,
)?;
}
- Ok(self.merge_blocks(Some(then_target), else_target))
+ Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
}
Expr::Unsafe { id: _, statements, tail } => {
- self.lower_block_to_place(None, statements, current, *tail, place)
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
}
Expr::Block { id: _, statements, tail, label } => {
- self.lower_block_to_place(*label, statements, current, *tail, place)
+ if let Some(label) = label {
+ self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
+ if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
+ let end = this.current_loop_end()?;
+ this.set_goto(current, end, expr_id.into());
+ }
+ Ok(())
+ })
+ } else {
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
+ }
}
- Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
- if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
- this.set_goto(block, begin);
+ Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
+ let scope = this.push_drop_scope();
+ if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
+ current = scope.pop_and_drop(this, current);
+ this.set_goto(current, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
}),
Expr::While { condition, body, label } => {
- self.lower_loop(current, *label, |this, begin| {
+ self.lower_loop(current, place, *label, expr_id.into(),|this, begin| {
+ let scope = this.push_drop_scope();
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
return Ok(());
};
- let end = this.current_loop_end()?;
+ let fail_cond = this.new_basic_block();
let after_cond = this.new_basic_block();
this.set_terminator(
to_switch,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
- targets: SwitchTargets::static_if(1, after_cond, end),
+ targets: SwitchTargets::static_if(1, after_cond, fail_cond),
},
+ expr_id.into(),
);
+ let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond);
+ let end = this.current_loop_end()?;
+ this.set_goto(fail_cond, end, expr_id.into());
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
- this.set_goto(block, begin);
+ let block = scope.pop_and_drop(this, block);
+ this.set_goto(block, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
})
}
- &Expr::For { iterable, pat, body, label } => {
- let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
- let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
- let option_some = self.resolve_lang_item(LangItem::OptionSome)?
- .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
- let option = option_some.parent;
- let into_iter_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let iter_next_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
- return Err(MirLowerError::TypeError("unknown for loop iterator type"));
- };
- let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
- let item_ty = &self.infer.type_of_pat[pat];
- let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
- let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
- let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
- let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
- let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
- else {
- return Ok(None);
- };
- self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
- self.lower_loop(current, label, |this, begin| {
- let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
- else {
- return Ok(());
- };
- let end = this.current_loop_end()?;
- let (current, _) = this.pattern_matching_variant(
- option_item_ty.clone(),
- BindingAnnotation::Unannotated,
- option_item_place.into(),
- option_some.into(),
- current,
- pat.into(),
- Some(end),
- &[pat], &None)?;
- if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
- this.set_goto(block, begin);
- }
- Ok(())
- })
- },
Expr::Call { callee, args, .. } => {
+ if let Some((func_id, generic_args)) =
+ self.infer.method_resolution(expr_id) {
+ let ty = chalk_ir::TyKind::FnDef(
+ CallableDefId::FunctionId(func_id).to_chalk(self.db),
+ generic_args,
+ )
+ .intern(Interner);
+ let func = Operand::from_bytes(vec![], ty);
+ return self.lower_call_and_args(
+ func,
+ iter::once(*callee).chain(args.iter().copied()),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
let callee_ty = self.expr_ty_after_adjustments(*callee);
match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => {
let func = Operand::from_bytes(vec![], callee_ty.clone());
- self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Scalar(_)
- | TyKind::Tuple(_, _)
- | TyKind::Array(_, _)
- | TyKind::Adt(_, _)
- | TyKind::Str
- | TyKind::Foreign(_)
- | TyKind::Slice(_) => {
- return Err(MirLowerError::TypeError("function call on data type"))
+ chalk_ir::TyKind::Function(_) => {
+ let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
+ return Ok(None);
+ };
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
- TyKind::AssociatedType(_, _)
- | TyKind::Raw(_, _)
- | TyKind::Ref(_, _, _)
- | TyKind::OpaqueType(_, _)
- | TyKind::Never
- | TyKind::Closure(_, _)
- | TyKind::Generator(_, _)
- | TyKind::GeneratorWitness(_, _)
- | TyKind::Placeholder(_)
- | TyKind::Dyn(_)
- | TyKind::Alias(_)
- | TyKind::Function(_)
- | TyKind::BoundVar(_)
- | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+ TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)),
+ _ => return Err(MirLowerError::TypeError("function call on bad type")),
}
}
- Expr::MethodCall { receiver, args, .. } => {
+ Expr::MethodCall { receiver, args, method_name, .. } => {
let (func_id, generic_args) =
- self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
- let ty = chalk_ir::TyKind::FnDef(
- CallableDefId::FunctionId(func_id).to_chalk(self.db),
- generic_args,
- )
- .intern(Interner);
- let func = Operand::from_bytes(vec![], ty);
+ self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?;
+ let func = Operand::from_fn(self.db, func_id, generic_args);
self.lower_call_and_args(
func,
iter::once(*receiver).chain(args.iter().copied()),
place,
current,
self.is_uninhabited(expr_id),
+ expr_id.into(),
)
}
Expr::Match { expr, arms } => {
@@ -488,23 +634,27 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- let cond_ty = self.expr_ty_after_adjustments(*expr);
let mut end = None;
for MatchArm { pat, guard, expr } in arms.iter() {
- if guard.is_some() {
- not_supported!("pattern matching with guard");
- }
- let (then, otherwise) = self.pattern_match(
+ let (then, mut otherwise) = self.pattern_match(
current,
None,
cond_place.clone(),
- cond_ty.clone(),
*pat,
- BindingAnnotation::Unannotated,
)?;
+ let then = if let &Some(guard) = guard {
+ let next = self.new_basic_block();
+ let o = otherwise.get_or_insert_with(|| self.new_basic_block());
+ if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
+ self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into());
+ }
+ next
+ } else {
+ then
+ };
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
let r = end.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(block, *r);
+ self.set_goto(block, *r, expr_id.into());
}
match otherwise {
Some(o) => current = o,
@@ -516,32 +666,43 @@ impl MirLowerCtx<'_> {
}
}
if self.is_unterminated(current) {
- self.set_terminator(current, Terminator::Unreachable);
+ self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
}
Ok(end)
}
- Expr::Continue { label } => match label {
- Some(_) => not_supported!("continue with label"),
- None => {
- let loop_data =
- self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
- self.set_goto(current, loop_data.begin);
- Ok(None)
- }
+ Expr::Continue { label } => {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
+ };
+ let begin = loop_data.begin;
+ current = self.drop_until_scope(loop_data.drop_scope_index, current);
+ self.set_goto(current, begin, expr_id.into());
+ Ok(None)
},
- Expr::Break { expr, label } => {
- if expr.is_some() {
- not_supported!("break with value");
+ &Expr::Break { expr, label } => {
+ if let Some(expr) = expr {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
+ };
+ let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else {
+ return Ok(None);
+ };
+ current = c;
}
- match label {
- Some(_) => not_supported!("break with label"),
+ let (end, drop_scope) = match label {
+ Some(l) => {
+ let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?;
+ (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index)
+ },
None => {
- let end =
- self.current_loop_end()?;
- self.set_goto(current, end);
- Ok(None)
- }
- }
+ (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index)
+ },
+ };
+ current = self.drop_until_scope(drop_scope, current);
+ self.set_goto(current, end, expr_id.into());
+ Ok(None)
}
Expr::Return { expr } => {
if let Some(expr) = expr {
@@ -551,11 +712,22 @@ impl MirLowerCtx<'_> {
return Ok(None);
}
}
- self.set_terminator(current, Terminator::Return);
+ current = self.drop_until_scope(0, current);
+ self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None)
}
Expr::Yield { .. } => not_supported!("yield"),
- Expr::RecordLit { fields, path, .. } => {
+ Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
+ let spread_place = match spread {
+ &Some(x) => {
+ let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else {
+ return Ok(None);
+ };
+ current = c;
+ Some(p)
+ },
+ None => None,
+ };
let variant_id = self
.infer
.variant_resolution_for_expr(expr_id)
@@ -563,7 +735,7 @@ impl MirLowerCtx<'_> {
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
None => MirLowerError::RecordLiteralWithoutPath,
})?;
- let subst = match self.expr_ty(expr_id).kind(Interner) {
+ let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
TyKind::Adt(_, s) => s.clone(),
_ => not_supported!("Non ADT record literal"),
};
@@ -585,9 +757,23 @@ impl MirLowerCtx<'_> {
place,
Rvalue::Aggregate(
AggregateKind::Adt(variant_id, subst),
- operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
+ match spread_place {
+ Some(sp) => operands.into_iter().enumerate().map(|(i, x)| {
+ match x {
+ Some(x) => x,
+ None => {
+ let p = sp.project(ProjectionElem::Field(FieldId {
+ parent: variant_id,
+ local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)),
+ }));
+ Operand::Copy(p)
+ },
+ }
+ }).collect(),
+ None => operands.into_iter().collect::<Option<_>>().ok_or(
+ MirLowerError::TypeError("missing field in record literal"),
+ )?,
+ },
),
expr_id.into(),
);
@@ -599,20 +785,19 @@ impl MirLowerCtx<'_> {
};
let local_id =
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
- let mut place = place;
- place
- .projection
- .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
+ let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
self.lower_expr_to_place(*expr, place, current)
}
}
}
Expr::Await { .. } => not_supported!("await"),
- Expr::Try { .. } => not_supported!("? operator"),
Expr::Yeet { .. } => not_supported!("yeet"),
- Expr::TryBlock { .. } => not_supported!("try block"),
Expr::Async { .. } => not_supported!("async block"),
- Expr::Const { .. } => not_supported!("anonymous const block"),
+ &Expr::Const(id) => {
+ let subst = self.placeholder_subst();
+ self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ Ok(Some(current))
+ },
Expr::Cast { expr, type_ref: _ } => {
let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
@@ -635,21 +820,30 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current))
}
- Expr::Box { .. } => not_supported!("box expression"),
- Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+ Expr::Box { expr } => {
+ let ty = self.expr_ty_after_adjustments(*expr);
+ self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into());
+ let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+ return Ok(None);
+ };
+ let p = place.project(ProjectionElem::Deref);
+ self.push_assignment(current, p, operand.into(), expr_id.into());
+ Ok(Some(current))
+ },
+ Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None);
};
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current))
}
- Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+ Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
};
let operation = match op {
- hir_def::expr::UnaryOp::Not => UnOp::Not,
- hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+ hir_def::hir::UnaryOp::Not => UnOp::Not,
+ hir_def::hir::UnaryOp::Neg => UnOp::Neg,
_ => unreachable!(),
};
self.push_assignment(
@@ -662,24 +856,93 @@ impl MirLowerCtx<'_> {
},
Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
- if let hir_def::expr::BinaryOp::Assignment { op } = op {
- if op.is_some() {
- not_supported!("assignment with arith op (like +=)");
+ let is_builtin = 'b: {
+ // Without adjust here is a hack. We assume that we know every possible adjustment
+ // for binary operator, and use without adjust to simplify our conditions.
+ let lhs_ty = self.expr_ty_without_adjust(*lhs);
+ let rhs_ty = self.expr_ty_without_adjust(*rhs);
+ if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) {
+ if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() {
+ break 'b true;
+ }
}
- let Some((lhs_place, current)) =
+ let builtin_inequal_impls = matches!(
+ op,
+ BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
+ );
+ lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls)
+ };
+ if !is_builtin {
+ if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
+ let func = Operand::from_fn(self.db, func_id, generic_args);
+ return self.lower_call_and_args(
+ func,
+ [*lhs, *rhs].into_iter(),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
+ }
+ if let hir_def::hir::BinaryOp::Assignment { op } = op {
+ if let Some(op) = op {
+ // last adjustment is `&mut` which we don't want it.
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(lhs)
+ .and_then(|x| x.split_last())
+ .map(|x| x.1)
+ .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
+ let Some((lhs_place, current)) =
+ self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op);
+ self.push_assignment(current, lhs_place, r_value, expr_id.into());
+ return Ok(Some(current));
+ } else {
+ let Some((lhs_place, current)) =
self.lower_expr_as_place(current, *lhs, false)?
- else {
- return Ok(None);
- };
- let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
- return Ok(None);
- };
- self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
- return Ok(Some(current));
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
+ return Ok(Some(current));
+ }
}
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
return Ok(None);
};
+ if let hir_def::hir::BinaryOp::LogicOp(op) = op {
+ let value_to_short = match op {
+ syntax::ast::LogicOp::And => 0,
+ syntax::ast::LogicOp::Or => 1,
+ };
+ let start_of_then = self.new_basic_block();
+ self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into());
+ let end_of_then = Some(start_of_then);
+ let start_of_else = self.new_basic_block();
+ let end_of_else =
+ self.lower_expr_to_place(*rhs, place, start_of_else)?;
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: lhs_op,
+ targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else),
+ },
+ expr_id.into(),
+ );
+ return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
+ }
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
return Ok(None);
};
@@ -688,13 +951,13 @@ impl MirLowerCtx<'_> {
place,
Rvalue::CheckedBinaryOp(
match op {
- hir_def::expr::BinaryOp::LogicOp(op) => match op {
- hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
- hir_def::expr::LogicOp::Or => BinOp::BitOr,
+ hir_def::hir::BinaryOp::LogicOp(op) => match op {
+ hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+ hir_def::hir::LogicOp::Or => BinOp::BitOr,
},
- hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+ hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
},
lhs_op,
rhs_op,
@@ -703,8 +966,96 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- Expr::Range { .. } => not_supported!("range"),
- Expr::Closure { .. } => not_supported!("closure"),
+ &Expr::Range { lhs, rhs, range_type: _ } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let Some((adt, subst)) = ty.as_adt() else {
+ return Err(MirLowerError::TypeError("Range type is not adt"));
+ };
+ let AdtId::StructId(st) = adt else {
+ return Err(MirLowerError::TypeError("Range type is not struct"));
+ };
+ let mut lp = None;
+ let mut rp = None;
+ if let Some(x) = lhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ lp = Some(o);
+ current = c;
+ }
+ if let Some(x) = rhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ rp = Some(o);
+ current = c;
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(
+ AggregateKind::Adt(st.into(), subst.clone()),
+ self.db.struct_data(st).variant_data.fields().iter().map(|x| {
+ let o = match x.1.name.as_str() {
+ Some("start") => lp.take(),
+ Some("end") => rp.take(),
+ Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())),
+ _ => None,
+ };
+ o.ok_or(MirLowerError::UnresolvedField)
+ }).collect::<Result<_>>()?,
+ ),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
+ Expr::Closure { .. } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let TyKind::Closure(id, _) = ty.kind(Interner) else {
+ not_supported!("closure with non closure type");
+ };
+ self.result.closures.push(*id);
+ let (captures, _) = self.infer.closure_info(id);
+ let mut operands = vec![];
+ for capture in captures.iter() {
+ let p = Place {
+ local: self.binding_local(capture.place.local)?,
+ projection: capture.place.projections.clone().into_iter().map(|x| {
+ match x {
+ ProjectionElem::Deref => ProjectionElem::Deref,
+ ProjectionElem::Field(x) => ProjectionElem::Field(x),
+ ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end },
+ ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
+ ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
+ ProjectionElem::Index(x) => match x { },
+ }
+ }).collect(),
+ };
+ match &capture.kind {
+ CaptureKind::ByRef(bk) => {
+ let placeholder_subst = self.placeholder_subst();
+ let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
+ let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into();
+ self.push_assignment(
+ current,
+ tmp.clone(),
+ Rvalue::Ref(bk.clone(), p),
+ capture.span,
+ );
+ operands.push(Operand::Move(tmp));
+ },
+ CaptureKind::ByValue => operands.push(Operand::Move(p)),
+ }
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs
.iter()
@@ -720,7 +1071,7 @@ impl MirLowerCtx<'_> {
return Ok(None);
};
let r = Rvalue::Aggregate(
- AggregateKind::Tuple(self.expr_ty(expr_id)),
+ AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
values,
);
self.push_assignment(current, place, r, expr_id.into());
@@ -728,7 +1079,7 @@ impl MirLowerCtx<'_> {
}
Expr::Array(l) => match l {
Array::ElementList { elements, .. } => {
- let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
+ let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
TyKind::Array(ty, _) => ty.clone(),
_ => {
return Err(MirLowerError::TypeError(
@@ -756,10 +1107,25 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, r, expr_id.into());
Ok(Some(current))
}
- Array::Repeat { .. } => not_supported!("array repeat"),
+ Array::Repeat { initializer, .. } => {
+ let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
+ return Ok(None);
+ };
+ let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
+ TyKind::Array(_, len) => len.clone(),
+ _ => {
+ return Err(MirLowerError::TypeError(
+ "Array repeat expression with non array type",
+ ))
+ }
+ };
+ let r = Rvalue::Repeat(init, len);
+ self.push_assignment(current, place, r, expr_id.into());
+ Ok(Some(current))
+ },
},
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
let op = self.lower_literal_to_operand(ty, l)?;
self.push_assignment(current, place, op.into(), expr_id.into());
Ok(Some(current))
@@ -768,17 +1134,25 @@ impl MirLowerCtx<'_> {
}
}
+ fn placeholder_subst(&mut self) -> Substitution {
+ let placeholder_subst = match self.owner.as_generic_def_id() {
+ Some(x) => TyBuilder::placeholder_subst(self.db, x),
+ None => Substitution::empty(Interner),
+ };
+ placeholder_subst
+ }
+
fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
if let Expr::Field { expr, name } = &self.body[expr_id] {
if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
- place.projection.push(ProjectionElem::TupleField(index))
+ *place = place.project(ProjectionElem::TupleOrClosureField(index))
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
- place.projection.push(ProjectionElem::Field(field));
+ *place = place.project(ProjectionElem::Field(field));
}
} else {
not_supported!("")
@@ -786,33 +1160,75 @@ impl MirLowerCtx<'_> {
Ok(())
}
+ fn lower_literal_or_const_to_operand(
+ &mut self,
+ ty: Ty,
+ loc: &LiteralOrConst,
+ ) -> Result<Operand> {
+ match loc {
+ LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
+ LiteralOrConst::Const(c) => {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, c);
+ let resolver = self.owner.resolver(self.db.upcast());
+ let pr = resolver
+ .resolve_path_in_value_ns(self.db.upcast(), c)
+ .ok_or_else(unresolved_name)?;
+ match pr {
+ ResolveValueResult::ValueNs(v) => {
+ if let ValueNs::ConstId(c) = v {
+ self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
+ } else {
+ not_supported!("bad path in range pattern");
+ }
+ }
+ ResolveValueResult::Partial(_, _) => {
+ not_supported!("associated constants in range pattern")
+ }
+ }
+ }
+ }
+ }
+
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
- let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+ let size = self
+ .db
+ .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())?
.size
.bytes_usize();
let bytes = match l {
- hir_def::expr::Literal::String(b) => {
+ hir_def::hir::Literal::String(b) => {
let b = b.as_bytes();
- let mut data = vec![];
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::ByteString(b) => {
- let mut data = vec![];
+ hir_def::hir::Literal::CString(b) => {
+ let b = b.as_bytes();
+ let bytes = b.iter().copied().chain(iter::once(0)).collect::<Vec<_>>();
+
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
+ data.extend(0usize.to_le_bytes());
+ data.extend(bytes.len().to_le_bytes());
+ let mut mm = MemoryMap::default();
+ mm.insert(0, bytes);
+ return Ok(Operand::from_concrete_const(data, mm, ty));
+ }
+ hir_def::hir::Literal::ByteString(b) => {
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
- hir_def::expr::Literal::Bool(b) => vec![*b as u8],
- hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Float(f, _) => match size {
+ hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+ hir_def::hir::Literal::Bool(b) => vec![*b as u8],
+ hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Float(f, _) => match size {
8 => f.into_f64().to_le_bytes().into(),
4 => f.into_f32().to_le_bytes().into(),
_ => {
@@ -829,24 +1245,34 @@ impl MirLowerCtx<'_> {
fn lower_const(
&mut self,
- const_id: hir_def::ConstId,
+ const_id: GeneralConstId,
prev_block: BasicBlockId,
place: Place,
+ subst: Substitution,
span: MirSpan,
+ ty: Ty,
) -> Result<()> {
- let c = self.db.const_eval(const_id)?;
- self.write_const_to_place(c, prev_block, place, span)
+ let c = self.lower_const_to_operand(subst, const_id, ty)?;
+ self.push_assignment(prev_block, place, c.into(), span);
+ Ok(())
}
- fn write_const_to_place(
+ fn lower_const_to_operand(
&mut self,
- c: Const,
- prev_block: BasicBlockId,
- place: Place,
- span: MirSpan,
- ) -> Result<()> {
- self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
- Ok(())
+ subst: Substitution,
+ const_id: GeneralConstId,
+ ty: Ty,
+ ) -> Result<Operand> {
+ let c = if subst.len(Interner) != 0 {
+ // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
+ intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
+ } else {
+ let name = const_id.name(self.db.upcast());
+ self.db
+ .const_eval(const_id.into(), subst)
+ .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
+ };
+ Ok(Operand::Constant(c))
}
fn write_bytes_to_place(
@@ -867,12 +1293,12 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId,
place: Place,
ty: Ty,
- fields: Vec<Operand>,
+ fields: Box<[Operand]>,
span: MirSpan,
) -> Result<BasicBlockId> {
let subst = match ty.kind(Interner) {
TyKind::Adt(_, subst) => subst.clone(),
- _ => not_supported!("Non ADT enum"),
+ _ => implementation_error!("Non ADT enum"),
};
self.push_assignment(
prev_block,
@@ -890,6 +1316,7 @@ impl MirLowerCtx<'_> {
place: Place,
mut current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let Some(args) = args
.map(|arg| {
@@ -904,21 +1331,22 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- self.lower_call(func, args, place, current, is_uninhabited)
+ self.lower_call(func, args.into(), place, current, is_uninhabited, span)
}
fn lower_call(
&mut self,
func: Operand,
- args: Vec<Operand>,
+ args: Box<[Operand]>,
place: Place,
current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
self.set_terminator(
current,
- Terminator::Call {
+ TerminatorKind::Call {
func,
args,
destination: place,
@@ -926,6 +1354,7 @@ impl MirLowerCtx<'_> {
cleanup: None,
from_hir_call: true,
},
+ span,
);
Ok(b)
}
@@ -934,15 +1363,15 @@ impl MirLowerCtx<'_> {
self.result.basic_blocks[source].terminator.is_none()
}
- fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
- self.result.basic_blocks[source].terminator = Some(terminator);
+ fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
+ self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
}
- fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
- self.set_terminator(source, Terminator::Goto { target });
+ fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
+ self.set_terminator(source, TerminatorKind::Goto { target }, span);
}
- fn expr_ty(&self, e: ExprId) -> Ty {
+ fn expr_ty_without_adjust(&self, e: ExprId) -> Ty {
self.infer[e].clone()
}
@@ -953,7 +1382,7 @@ impl MirLowerCtx<'_> {
ty = Some(x.target.clone());
}
}
- ty.unwrap_or_else(|| self.expr_ty(e))
+ ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
}
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
@@ -970,293 +1399,14 @@ impl MirLowerCtx<'_> {
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
}
- /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
- /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
- /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
- /// mismatched path block is `None`.
- ///
- /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
- /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
- /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
- /// so it should be an empty block.
- fn pattern_match(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- mut cond_place: Place,
- mut cond_ty: Ty,
- pattern: PatId,
- mut binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- Ok(match &self.body.pats[pattern] {
- Pat::Missing => return Err(MirLowerError::IncompleteExpr),
- Pat::Wild => (current, current_else),
- Pat::Tuple { args, ellipsis } => {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Tuple(_, s) => s,
- _ => {
- return Err(MirLowerError::TypeError(
- "non tuple type matched with tuple pattern",
- ))
- }
- };
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().enumerate().map(|(i, x)| {
- (
- PlaceElem::TupleField(i),
- *x,
- subst.at(Interner, i).assert_ty_ref(Interner).clone(),
- )
- }),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- Pat::Or(pats) => {
- let then_target = self.new_basic_block();
- let mut finished = false;
- for pat in &**pats {
- let (next, next_else) = self.pattern_match(
- current,
- None,
- cond_place.clone(),
- cond_ty.clone(),
- *pat,
- binding_mode,
- )?;
- self.set_goto(next, then_target);
- match next_else {
- Some(t) => {
- current = t;
- }
- None => {
- finished = true;
- break;
- }
- }
- }
- if !finished {
- let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(current, ce);
- }
- (then_target, current_else)
- }
- Pat::Record { .. } => not_supported!("record pattern"),
- Pat::Range { .. } => not_supported!("range pattern"),
- Pat::Slice { .. } => not_supported!("slice pattern"),
- Pat::Path(_) => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- &[],
- &None,
- )?
- }
- Pat::Lit(l) => {
- let then_target = self.new_basic_block();
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- match &self.body.exprs[*l] {
- Expr::Literal(l) => match l {
- hir_def::expr::Literal::Int(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(
- *x as u128,
- then_target,
- else_target,
- ),
- },
- );
- }
- hir_def::expr::Literal::Uint(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(*x, then_target, else_target),
- },
- );
- }
- _ => not_supported!("non int path literal"),
- },
- _ => not_supported!("expression path literal"),
- }
- (then_target, Some(else_target))
- }
- Pat::Bind { id, subpat } => {
- let target_place = self.result.binding_locals[*id];
- let mode = self.body.bindings[*id].mode;
- if let Some(subpat) = subpat {
- (current, current_else) = self.pattern_match(
- current,
- current_else,
- cond_place.clone(),
- cond_ty,
- *subpat,
- binding_mode,
- )?
- }
- if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
- binding_mode = mode;
- }
- self.push_storage_live(*id, current);
- self.push_assignment(
- current,
- target_place.into(),
- match binding_mode {
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
- Operand::Copy(cond_place).into()
- }
- BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
- BindingAnnotation::RefMut => Rvalue::Ref(
- BorrowKind::Mut { allow_two_phase_borrow: false },
- cond_place,
- ),
- },
- pattern.into(),
- );
- (current, current_else)
- }
- Pat::TupleStruct { path: _, args, ellipsis } => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- args,
- ellipsis,
- )?
- }
- Pat::Ref { .. } => not_supported!("& pattern"),
- Pat::Box { .. } => not_supported!("box pattern"),
- Pat::ConstBlock(_) => not_supported!("const block pattern"),
- })
- }
-
- fn pattern_matching_variant(
- &mut self,
- mut cond_ty: Ty,
- mut binding_mode: BindingAnnotation,
- mut cond_place: Place,
- variant: VariantId,
- current: BasicBlockId,
- span: MirSpan,
- current_else: Option<BasicBlockId>,
- args: &[PatId],
- ellipsis: &Option<usize>,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Adt(_, s) => s,
- _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
- };
- let fields_type = self.db.field_types(variant);
- Ok(match variant {
- VariantId::EnumVariantId(v) => {
- let e = self.db.const_eval_discriminant(v)? as u128;
- let next = self.new_basic_block();
- let tmp = self.discr_temp_place();
- self.push_assignment(
- current,
- tmp.clone(),
- Rvalue::Discriminant(cond_place.clone()),
- span,
- );
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(tmp),
- targets: SwitchTargets::static_if(e, next, else_target),
- },
- );
- let enum_data = self.db.enum_data(v.parent);
- let fields =
- enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- next,
- Some(else_target),
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::StructId(s) => {
- let struct_data = self.db.struct_data(s);
- let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::UnionId(_) => {
- return Err(MirLowerError::TypeError("pattern matching on union"))
- }
- })
- }
-
- fn pattern_match_tuple_like(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
- ellipsis: Option<usize>,
- cond_place: &Place,
- binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- if ellipsis.is_some() {
- not_supported!("tuple like pattern with ellipsis");
- }
- for (proj, arg, ty) in args {
- let mut cond_place = cond_place.clone();
- cond_place.projection.push(proj);
- (current, current_else) =
- self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
- }
- Ok((current, current_else))
- }
-
- fn discr_temp_place(&mut self) -> Place {
+ fn discr_temp_place(&mut self, current: BasicBlockId) -> Place {
match &self.discr_temp {
Some(x) => x.clone(),
None => {
- let tmp: Place =
- self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+ let tmp: Place = self
+ .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown)
+ .expect("discr_ty is never unsized")
+ .into();
self.discr_temp = Some(tmp.clone());
tmp
}
@@ -1266,19 +1416,34 @@ impl MirLowerCtx<'_> {
fn lower_loop(
&mut self,
prev_block: BasicBlockId,
+ place: Place,
label: Option<LabelId>,
+ span: MirSpan,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> {
- if label.is_some() {
- not_supported!("loop with label");
- }
let begin = self.new_basic_block();
- let prev =
- mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
- self.set_goto(prev_block, begin);
+ let prev = mem::replace(
+ &mut self.current_loop_blocks,
+ Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }),
+ );
+ let prev_label = if let Some(label) = label {
+ // We should generate the end now, to make sure that it wouldn't change later. It is
+ // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but
+ // it should not affect correctness.
+ self.current_loop_end()?;
+ self.labeled_loop_blocks
+ .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
+ } else {
+ None
+ };
+ self.set_goto(prev_block, begin, span);
f(self, begin)?;
- let my = mem::replace(&mut self.current_loop_blocks, prev)
- .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+ let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
+ MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
+ )?;
+ if let Some(prev) = prev_label {
+ self.labeled_loop_blocks.insert(label.unwrap(), prev);
+ }
Ok(my.end)
}
@@ -1290,14 +1455,15 @@ impl MirLowerCtx<'_> {
&mut self,
b1: Option<BasicBlockId>,
b2: Option<BasicBlockId>,
+ span: MirSpan,
) -> Option<BasicBlockId> {
match (b1, b2) {
(None, None) => None,
(None, Some(b)) | (Some(b), None) => Some(b),
(Some(b1), Some(b2)) => {
let bm = self.new_basic_block();
- self.set_goto(b1, bm);
- self.set_goto(b2, bm);
+ self.set_goto(b1, bm, span);
+ self.set_goto(b2, bm, span);
Some(bm)
}
}
@@ -1307,7 +1473,9 @@ impl MirLowerCtx<'_> {
let r = match self
.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end
{
Some(x) => x,
@@ -1315,7 +1483,9 @@ impl MirLowerCtx<'_> {
let s = self.new_basic_block();
self.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end = Some(s);
s
}
@@ -1327,36 +1497,28 @@ impl MirLowerCtx<'_> {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
}
- /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
- /// the appropriated places.
- fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
- // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
- // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
- // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
- // allow this:
- //
- // ```
- // let x;
- // loop {
- // let y = 2;
- // x = &y;
- // if some_condition {
- // break; // we need to add a StorageDead(y) above this to kill the x borrow
- // }
- // }
- // use(x)
- // ```
- // But I think this approach work for mutability analysis, as user can't write code which mutates a binding
- // after StorageDead, except loops, which are handled by this hack.
+ /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
+ /// `Drop` in the appropriated places.
+ fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
let span = self.body.bindings[b]
.definitions
.first()
.copied()
.map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown);
- let l = self.result.binding_locals[b];
- self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
+ let l = self.binding_local(b)?;
+ self.push_storage_live_for_local(l, current, span)
+ }
+
+ fn push_storage_live_for_local(
+ &mut self,
+ l: LocalId,
+ current: BasicBlockId,
+ span: MirSpan,
+ ) -> Result<()> {
+ self.drop_scopes.last_mut().unwrap().locals.push(l);
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+ Ok(())
}
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@@ -1366,81 +1528,204 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place(
&mut self,
- label: Option<LabelId>,
- statements: &[hir_def::expr::Statement],
+ statements: &[hir_def::hir::Statement],
mut current: BasicBlockId,
tail: Option<ExprId>,
place: Place,
+ span: MirSpan,
) -> Result<Option<Idx<BasicBlock>>> {
- if label.is_some() {
- not_supported!("block with label");
- }
+ let scope = self.push_drop_scope();
for statement in statements.iter() {
match statement {
- hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+ hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
if let Some(expr_id) = initializer {
let else_block;
let Some((init_place, c)) =
self.lower_expr_as_place(current, *expr_id, true)?
else {
+ scope.pop_assume_dropped(self);
return Ok(None);
};
current = c;
- (current, else_block) = self.pattern_match(
- current,
- None,
- init_place,
- self.expr_ty_after_adjustments(*expr_id),
- *pat,
- BindingAnnotation::Unannotated,
- )?;
+ (current, else_block) =
+ self.pattern_match(current, None, init_place, *pat)?;
match (else_block, else_branch) {
(None, _) => (),
(Some(else_block), None) => {
- self.set_terminator(else_block, Terminator::Unreachable);
+ self.set_terminator(else_block, TerminatorKind::Unreachable, span);
}
(Some(else_block), Some(else_branch)) => {
if let Some((_, b)) =
self.lower_expr_as_place(else_block, *else_branch, true)?
{
- self.set_terminator(b, Terminator::Unreachable);
+ self.set_terminator(b, TerminatorKind::Unreachable, span);
}
}
}
} else {
+ let mut err = None;
self.body.walk_bindings_in_pat(*pat, |b| {
- self.push_storage_live(b, current);
+ if let Err(e) = self.push_storage_live(b, current) {
+ err = Some(e);
+ }
});
+ if let Some(e) = err {
+ return Err(e);
+ }
}
}
- hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+ hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
+ let scope2 = self.push_drop_scope();
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
+ scope2.pop_assume_dropped(self);
+ scope.pop_assume_dropped(self);
return Ok(None);
};
- current = c;
+ current = scope2.pop_and_drop(self, c);
}
}
}
- match tail {
- Some(tail) => self.lower_expr_to_place(tail, place, current),
- None => Ok(Some(current)),
+ if let Some(tail) = tail {
+ let Some(c) = self.lower_expr_to_place(tail, place, current)? else {
+ scope.pop_assume_dropped(self);
+ return Ok(None);
+ };
+ current = c;
}
+ current = scope.pop_and_drop(self, current);
+ Ok(Some(current))
}
-}
-fn pattern_matching_dereference(
- cond_ty: &mut Ty,
- binding_mode: &mut BindingAnnotation,
- cond_place: &mut Place,
-) {
- while let Some((ty, _, mu)) = cond_ty.as_reference() {
- if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
- *binding_mode = BindingAnnotation::RefMut;
- } else {
- *binding_mode = BindingAnnotation::Ref;
+ fn lower_params_and_bindings(
+ &mut self,
+ params: impl Iterator<Item = (PatId, Ty)> + Clone,
+ pick_binding: impl Fn(BindingId) -> bool,
+ ) -> Result<BasicBlockId> {
+ let base_param_count = self.result.param_locals.len();
+ self.result.param_locals.extend(params.clone().map(|(x, ty)| {
+ let local_id = self.result.locals.alloc(Local { ty });
+ self.drop_scopes.last_mut().unwrap().locals.push(local_id);
+ if let Pat::Bind { id, subpat: None } = self.body[x] {
+ if matches!(
+ self.body.bindings[id].mode,
+ BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+ ) {
+ self.result.binding_locals.insert(id, local_id);
+ }
+ }
+ local_id
+ }));
+ // and then rest of bindings
+ for (id, _) in self.body.bindings.iter() {
+ if !pick_binding(id) {
+ continue;
+ }
+ if !self.result.binding_locals.contains_idx(id) {
+ self.result
+ .binding_locals
+ .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
+ }
+ }
+ let mut current = self.result.start_block;
+ for ((param, _), local) in
+ params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
+ {
+ if let Pat::Bind { id, .. } = self.body[param] {
+ if local == self.binding_local(id)? {
+ continue;
+ }
+ }
+ let r = self.pattern_match(current, None, local.into(), param)?;
+ if let Some(b) = r.1 {
+ self.set_terminator(b, TerminatorKind::Unreachable, param.into());
+ }
+ current = r.0;
+ }
+ Ok(current)
+ }
+
+ fn binding_local(&self, b: BindingId) -> Result<LocalId> {
+ match self.result.binding_locals.get(b) {
+ Some(x) => Ok(*x),
+ None => {
+ // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which
+ // is a hir lowering problem IMO.
+ // never!("Using unaccessable local for binding is always a bug");
+ Err(MirLowerError::UnaccessableLocal)
+ }
+ }
+ }
+
+ fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
+ let r = self.db.const_eval_discriminant(variant);
+ match r {
+ Ok(r) => Ok(r),
+ Err(e) => {
+ let data = self.db.enum_data(variant.parent);
+ let name = format!(
+ "{}::{}",
+ data.name.display(self.db.upcast()),
+ data.variants[variant.local_id].name.display(self.db.upcast())
+ );
+ Err(MirLowerError::ConstEvalError(name, Box::new(e)))
+ }
+ }
+ }
+
+ fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
+ for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
+ self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
+ }
+ current
+ }
+
+ fn push_drop_scope(&mut self) -> DropScopeToken {
+ self.drop_scopes.push(DropScope::default());
+ DropScopeToken
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_assume_dropped_internal(&mut self) {
+ self.drop_scopes.pop();
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
+ let scope = self.drop_scopes.pop().unwrap();
+ self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
+ current
+ }
+
+ fn pop_drop_scope_assert_finished(
+ &mut self,
+ mut current: BasicBlockId,
+ ) -> Result<BasicBlockId> {
+ current = self.pop_drop_scope_internal(current);
+ if !self.drop_scopes.is_empty() {
+ implementation_error!("Mismatched count between drop scope push and pops");
+ }
+ Ok(current)
+ }
+
+ fn emit_drop_and_storage_dead_for_scope(
+ &mut self,
+ scope: &DropScope,
+ current: &mut Idx<BasicBlock>,
+ ) {
+ for &l in scope.locals.iter().rev() {
+ if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
+ let prev = std::mem::replace(current, self.new_basic_block());
+ self.set_terminator(
+ prev,
+ TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
+ MirSpan::Unknown,
+ );
+ }
+ self.push_statement(
+ *current,
+ StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
+ );
}
- *cond_ty = ty.clone();
- cond_place.projection.push(ProjectionElem::Deref);
}
}
@@ -1452,6 +1737,26 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
(_, _) => CastKind::IntToInt,
},
+ (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
+ (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
+ (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
+ CastKind::Pointer(if a == b {
+ PointerCast::MutToConstPointer
+ } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ {
+ // slice to slice cast is no-op (metadata is not touched), so we use this
+ PointerCast::MutToConstPointer
+ } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+ PointerCast::Unsize
+ } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
+ PointerCast::ArrayToPointer
+ } else {
+ // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
+ // for it in `PointerCast` so we use `MutToConstPointer`
+ PointerCast::MutToConstPointer
+ })
+ }
// Enum to int casts
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
CastKind::IntToInt
@@ -1460,20 +1765,122 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
})
}
+pub fn mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+) -> Result<Arc<MirBody>> {
+ let (owner, expr) = db.lookup_intern_closure(closure.into());
+ let body = db.body(owner);
+ let infer = db.infer(owner);
+ let Expr::Closure { args, body: root, .. } = &body[expr] else {
+ implementation_error!("closure expression is not closure");
+ };
+ let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
+ implementation_error!("closure expression is not closure");
+ };
+ let (captures, kind) = infer.closure_info(&closure);
+ let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
+ // 0 is return local
+ ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
+ let closure_local = ctx.result.locals.alloc(Local {
+ ty: match kind {
+ FnTrait::FnOnce => infer[expr].clone(),
+ FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ },
+ });
+ ctx.result.param_locals.push(closure_local);
+ let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
+ implementation_error!("closure has not callable sig");
+ };
+ let current = ctx.lower_params_and_bindings(
+ args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
+ |_| true,
+ )?;
+ if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
+ }
+ let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
+ for (i, capture) in captures.iter().enumerate() {
+ let local = ctx.binding_local(capture.place.local)?;
+ upvar_map.entry(local).or_default().push((capture, i));
+ }
+ let mut err = None;
+ let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
+ let closure_projection = match kind {
+ FnTrait::FnOnce => vec![],
+ FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
+ };
+ ctx.result.walk_places(|p| {
+ if let Some(x) = upvar_map.get(&p.local) {
+ let r = x.iter().find(|x| {
+ if p.projection.len() < x.0.place.projections.len() {
+ return false;
+ }
+ for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
+ match (x, y) {
+ (ProjectionElem::Deref, ProjectionElem::Deref) => (),
+ (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
+ (
+ ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::TupleOrClosureField(y),
+ ) if x == y => (),
+ _ => return false,
+ }
+ }
+ true
+ });
+ match r {
+ Some(x) => {
+ p.local = closure_local;
+ let mut next_projs = closure_projection.clone();
+ next_projs.push(PlaceElem::TupleOrClosureField(x.1));
+ let prev_projs = mem::take(&mut p.projection);
+ if x.0.kind != CaptureKind::ByValue {
+ next_projs.push(ProjectionElem::Deref);
+ }
+ next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len()));
+ p.projection = next_projs.into();
+ }
+ None => err = Some(p.clone()),
+ }
+ }
+ });
+ ctx.result.binding_locals = ctx
+ .result
+ .binding_locals
+ .into_iter()
+ .filter(|x| ctx.body[x.0].owner == Some(expr))
+ .collect();
+ if let Some(err) = err {
+ return Err(MirLowerError::UnresolvedUpvar(err));
+ }
+ ctx.result.shrink_to_fit();
+ Ok(Arc::new(ctx.result))
+}
+
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
let _p = profile::span("mir_body_query").detail(|| match def {
- DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
- DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
- DefWithBodyId::ConstId(it) => {
- db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
- }
+ DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::ConstId(it) => db
+ .const_data(it)
+ .name
+ .clone()
+ .unwrap_or_else(Name::missing)
+ .display(db.upcast())
+ .to_string(),
DefWithBodyId::VariantId(it) => {
- db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
}
});
let body = db.body(def);
let infer = db.infer(def);
- let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ result.shrink_to_fit();
Ok(Arc::new(result))
}
@@ -1497,85 +1904,39 @@ pub fn lower_to_mir(
if let Some((_, x)) = infer.type_mismatches().next() {
return Err(MirLowerError::TypeMismatch(x.clone()));
}
- let mut basic_blocks = Arena::new();
- let start_block =
- basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
- let mut locals = Arena::new();
+ let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local
- locals.alloc(Local { ty: infer[root_expr].clone() });
- let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
- // 1 to param_len is for params
- let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
- let substs = TyBuilder::placeholder_subst(db, fid);
- let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
- body.params
- .iter()
- .zip(callable_sig.params().iter())
- .map(|(&x, ty)| {
- let local_id = locals.alloc(Local { ty: ty.clone() });
- if let Pat::Bind { id, subpat: None } = body[x] {
- if matches!(
- body.bindings[id].mode,
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable
- ) {
- binding_locals.insert(id, local_id);
- }
- }
- local_id
- })
- .collect()
- } else {
- if !body.params.is_empty() {
- return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
- }
- vec![]
- };
- // and then rest of bindings
- for (id, _) in body.bindings.iter() {
- if !binding_locals.contains_idx(id) {
- binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
+ ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
+ let binding_picker = |b: BindingId| {
+ if root_expr == body.body_expr {
+ body[b].owner.is_none()
+ } else {
+ body[b].owner == Some(root_expr)
}
- }
- let mir = MirBody {
- basic_blocks,
- locals,
- start_block,
- binding_locals,
- param_locals,
- owner,
- arg_count: body.params.len(),
- };
- let mut ctx = MirLowerCtx {
- result: mir,
- db,
- infer,
- body,
- owner,
- current_loop_blocks: None,
- discr_temp: None,
};
- let mut current = start_block;
- for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
- if let Pat::Bind { id, .. } = body[param] {
- if local == ctx.result.binding_locals[id] {
- continue;
+ // 1 to param_len is for params
+ // FIXME: replace with let chain once it becomes stable
+ let current = 'b: {
+ if body.body_expr == root_expr {
+ // otherwise it's an inline const, and has no parameter
+ if let DefWithBodyId::FunctionId(fid) = owner {
+ let substs = TyBuilder::placeholder_subst(db, fid);
+ let callable_sig =
+ db.callable_item_signature(fid.into()).substitute(Interner, &substs);
+ break 'b ctx.lower_params_and_bindings(
+ body.params
+ .iter()
+ .zip(callable_sig.params().iter())
+ .map(|(x, y)| (*x, y.clone())),
+ binding_picker,
+ )?;
}
}
- let r = ctx.pattern_match(
- current,
- None,
- local.into(),
- ctx.result.locals[local].ty.clone(),
- param,
- BindingAnnotation::Unannotated,
- )?;
- if let Some(b) = r.1 {
- ctx.set_terminator(b, Terminator::Unreachable);
- }
- current = r.0;
- }
- if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
- ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+ ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
+ };
+ if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
}
Ok(ctx.result)
}