mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
1210 lines
51 KiB
Rust
1210 lines
51 KiB
Rust
//! This module generates a polymorphic MIR from a hir body
|
|
|
|
use std::{iter, mem, sync::Arc};
|
|
|
|
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
|
use hir_def::{
|
|
body::Body,
|
|
expr::{
|
|
Array, BindingAnnotation, ExprId, LabelId, Literal, MatchArm, Pat, PatId, RecordLitField,
|
|
},
|
|
layout::LayoutError,
|
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
|
DefWithBodyId, EnumVariantId, HasModule,
|
|
};
|
|
use la_arena::ArenaMap;
|
|
|
|
use crate::{
|
|
consteval::ConstEvalError, db::HirDatabase, layout::layout_of_ty, mapping::ToChalk,
|
|
utils::generics, Adjust, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
|
};
|
|
|
|
use super::*;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
struct LoopBlocks {
|
|
begin: BasicBlockId,
|
|
end: BasicBlockId,
|
|
}
|
|
|
|
struct MirLowerCtx<'a> {
|
|
result: MirBody,
|
|
owner: DefWithBodyId,
|
|
binding_locals: ArenaMap<PatId, LocalId>,
|
|
current_loop_blocks: Option<LoopBlocks>,
|
|
discr_temp: Option<Place>,
|
|
db: &'a dyn HirDatabase,
|
|
body: &'a Body,
|
|
infer: &'a InferenceResult,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum MirLowerError {
|
|
ConstEvalError(Box<ConstEvalError>),
|
|
LayoutError(LayoutError),
|
|
IncompleteExpr,
|
|
UnresolvedName,
|
|
MissingFunctionDefinition,
|
|
TypeError(&'static str),
|
|
NotSupported(String),
|
|
ContinueWithoutLoop,
|
|
BreakWithoutLoop,
|
|
Loop,
|
|
}
|
|
|
|
macro_rules! not_supported {
|
|
($x: expr) => {
|
|
return Err(MirLowerError::NotSupported(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)
|
|
}
|
|
}
|
|
|
|
type Result<T> = std::result::Result<T, MirLowerError>;
|
|
|
|
impl MirLowerCtx<'_> {
|
|
fn temp(&mut self, ty: Ty) -> Result<LocalId> {
|
|
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
|
|
not_supported!("unsized temporaries");
|
|
}
|
|
Ok(self.result.locals.alloc(Local { mutability: Mutability::Not, ty }))
|
|
}
|
|
|
|
fn lower_expr_as_place(&self, expr_id: ExprId) -> Option<Place> {
|
|
let adjustments = self.infer.expr_adjustments.get(&expr_id);
|
|
let mut r = self.lower_expr_as_place_without_adjust(expr_id)?;
|
|
for adjustment in adjustments.iter().flat_map(|x| x.iter()) {
|
|
match adjustment.kind {
|
|
Adjust::NeverToAny => return Some(r),
|
|
Adjust::Deref(None) => {
|
|
r.projection.push(ProjectionElem::Deref);
|
|
}
|
|
Adjust::Deref(Some(_)) => return None,
|
|
Adjust::Borrow(_) => return None,
|
|
Adjust::Pointer(_) => return None,
|
|
}
|
|
}
|
|
Some(r)
|
|
}
|
|
|
|
fn lower_expr_as_place_without_adjust(&self, expr_id: ExprId) -> Option<Place> {
|
|
match &self.body.exprs[expr_id] {
|
|
Expr::Path(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())?;
|
|
let pr = match pr {
|
|
ResolveValueResult::ValueNs(v) => v,
|
|
ResolveValueResult::Partial(..) => return None,
|
|
};
|
|
match pr {
|
|
ValueNs::LocalBinding(pat_id) => Some(self.binding_locals[pat_id].into()),
|
|
_ => None,
|
|
}
|
|
}
|
|
Expr::UnaryOp { expr, op } => match op {
|
|
hir_def::expr::UnaryOp::Deref => {
|
|
let mut r = self.lower_expr_as_place(*expr)?;
|
|
r.projection.push(ProjectionElem::Deref);
|
|
Some(r)
|
|
}
|
|
_ => None,
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn lower_expr_to_some_operand(
|
|
&mut self,
|
|
expr_id: ExprId,
|
|
current: BasicBlockId,
|
|
) -> Result<(Operand, BasicBlockId)> {
|
|
if !self.has_adjustments(expr_id) {
|
|
match &self.body.exprs[expr_id] {
|
|
Expr::Literal(l) => {
|
|
let ty = self.expr_ty(expr_id);
|
|
return Ok((self.lower_literal_to_operand(ty, l)?, current));
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
let (p, current) = self.lower_expr_to_some_place(expr_id, current)?;
|
|
Ok((Operand::Copy(p), current))
|
|
}
|
|
|
|
fn lower_expr_to_some_place(
|
|
&mut self,
|
|
expr_id: ExprId,
|
|
prev_block: BasicBlockId,
|
|
) -> Result<(Place, BasicBlockId)> {
|
|
if let Some(p) = self.lower_expr_as_place(expr_id) {
|
|
return Ok((p, prev_block));
|
|
}
|
|
let mut ty = self.expr_ty(expr_id);
|
|
if let Some(x) = self.infer.expr_adjustments.get(&expr_id) {
|
|
if let Some(x) = x.last() {
|
|
ty = x.target.clone();
|
|
}
|
|
}
|
|
let place = self.temp(ty)?;
|
|
Ok((place.into(), self.lower_expr_to_place(expr_id, place.into(), prev_block)?))
|
|
}
|
|
|
|
fn lower_expr_to_place(
|
|
&mut self,
|
|
expr_id: ExprId,
|
|
place: Place,
|
|
prev_block: BasicBlockId,
|
|
) -> Result<BasicBlockId> {
|
|
if let Some(x) = self.infer.expr_adjustments.get(&expr_id) {
|
|
if x.len() > 0 {
|
|
let tmp = self.temp(self.expr_ty(expr_id))?;
|
|
let current =
|
|
self.lower_expr_to_place_without_adjust(expr_id, tmp.into(), prev_block)?;
|
|
let mut r = Place::from(tmp);
|
|
for adjustment in x {
|
|
match &adjustment.kind {
|
|
Adjust::NeverToAny => (),
|
|
Adjust::Deref(None) => {
|
|
r.projection.push(ProjectionElem::Deref);
|
|
}
|
|
Adjust::Deref(Some(_)) => not_supported!("overloaded dereference"),
|
|
Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
|
|
let tmp = self.temp(adjustment.target.clone())?;
|
|
self.push_assignment(
|
|
current,
|
|
tmp.into(),
|
|
Rvalue::Ref(BorrowKind::from_chalk(*m), r),
|
|
);
|
|
r = tmp.into();
|
|
}
|
|
Adjust::Pointer(cast) => {
|
|
let target = &adjustment.target;
|
|
let tmp = self.temp(target.clone())?;
|
|
self.push_assignment(
|
|
current,
|
|
tmp.into(),
|
|
Rvalue::Cast(
|
|
CastKind::Pointer(cast.clone()),
|
|
Operand::Copy(r).into(),
|
|
target.clone(),
|
|
),
|
|
);
|
|
r = tmp.into();
|
|
}
|
|
}
|
|
}
|
|
self.push_assignment(current, place, Operand::Copy(r).into());
|
|
return Ok(current);
|
|
}
|
|
}
|
|
self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
|
|
}
|
|
|
|
fn lower_expr_to_place_without_adjust(
|
|
&mut self,
|
|
expr_id: ExprId,
|
|
place: Place,
|
|
mut current: BasicBlockId,
|
|
) -> Result<BasicBlockId> {
|
|
match &self.body.exprs[expr_id] {
|
|
Expr::Missing => Err(MirLowerError::IncompleteExpr),
|
|
Expr::Path(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(MirLowerError::UnresolvedName)?;
|
|
let pr = match pr {
|
|
ResolveValueResult::ValueNs(v) => v,
|
|
ResolveValueResult::Partial(..) => {
|
|
return match self
|
|
.infer
|
|
.assoc_resolutions_for_expr(expr_id)
|
|
.ok_or(MirLowerError::UnresolvedName)?
|
|
.0
|
|
//.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
|
|
{
|
|
hir_def::AssocItemId::ConstId(c) => self.lower_const(c, current, place),
|
|
_ => return Err(MirLowerError::UnresolvedName),
|
|
};
|
|
}
|
|
};
|
|
match pr {
|
|
ValueNs::LocalBinding(pat_id) => {
|
|
self.push_assignment(
|
|
current,
|
|
place,
|
|
Operand::Copy(self.binding_locals[pat_id].into()).into(),
|
|
);
|
|
Ok(current)
|
|
}
|
|
ValueNs::ConstId(const_id) => self.lower_const(const_id, current, place),
|
|
ValueNs::EnumVariantId(variant_id) => {
|
|
let ty = self.infer.type_of_expr[expr_id].clone();
|
|
self.lower_enum_variant(variant_id, current, place, ty, vec![])
|
|
}
|
|
ValueNs::GenericParam(p) => {
|
|
let Some(def) = self.owner.as_generic_def_id() else {
|
|
not_supported!("owner without generic def id");
|
|
};
|
|
let gen = generics(self.db.upcast(), def);
|
|
let ty = self.expr_ty(expr_id);
|
|
self.push_assignment(
|
|
current,
|
|
place,
|
|
Operand::Constant(
|
|
ConstData {
|
|
ty,
|
|
value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
|
|
DebruijnIndex::INNERMOST,
|
|
gen.param_idx(p.into()).ok_or(MirLowerError::TypeError(
|
|
"fail to lower const generic param",
|
|
))?,
|
|
)),
|
|
}
|
|
.intern(Interner),
|
|
)
|
|
.into(),
|
|
);
|
|
Ok(current)
|
|
}
|
|
ValueNs::StructId(_) => {
|
|
// It's probably a unit struct or a zero sized function, so no action is needed.
|
|
Ok(current)
|
|
}
|
|
x => {
|
|
not_supported!("unknown name {x:?} in value name space");
|
|
}
|
|
}
|
|
}
|
|
Expr::If { condition, then_branch, else_branch } => {
|
|
let (discr, current) = self.lower_expr_to_some_operand(*condition, current)?;
|
|
let start_of_then = self.new_basic_block();
|
|
let end = self.new_basic_block();
|
|
let end_of_then =
|
|
self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?;
|
|
self.set_goto(end_of_then, end);
|
|
let mut start_of_else = end;
|
|
if let Some(else_branch) = else_branch {
|
|
start_of_else = self.new_basic_block();
|
|
let end_of_else =
|
|
self.lower_expr_to_place(*else_branch, place, start_of_else)?;
|
|
self.set_goto(end_of_else, end);
|
|
}
|
|
self.set_terminator(
|
|
current,
|
|
Terminator::SwitchInt {
|
|
discr,
|
|
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
|
|
},
|
|
);
|
|
Ok(end)
|
|
}
|
|
Expr::Let { pat, expr } => {
|
|
let (cond_place, current) = self.lower_expr_to_some_place(*expr, current)?;
|
|
let result = self.new_basic_block();
|
|
let (then_target, else_target) = self.pattern_match(
|
|
current,
|
|
None,
|
|
cond_place,
|
|
self.expr_ty(*expr),
|
|
*pat,
|
|
BindingAnnotation::Unannotated,
|
|
)?;
|
|
self.write_bytes_to_place(then_target, place.clone(), vec![1], TyBuilder::bool())?;
|
|
self.set_goto(then_target, result);
|
|
if let Some(else_target) = else_target {
|
|
self.write_bytes_to_place(else_target, place, vec![0], TyBuilder::bool())?;
|
|
self.set_goto(else_target, result);
|
|
}
|
|
Ok(result)
|
|
}
|
|
Expr::Block { id: _, statements, tail, label } => {
|
|
if label.is_some() {
|
|
not_supported!("block with label");
|
|
}
|
|
for statement in statements.iter() {
|
|
match statement {
|
|
hir_def::expr::Statement::Let {
|
|
pat,
|
|
initializer,
|
|
else_branch,
|
|
type_ref: _,
|
|
} => match initializer {
|
|
Some(expr_id) => {
|
|
let else_block;
|
|
let init_place;
|
|
(init_place, current) =
|
|
self.lower_expr_to_some_place(*expr_id, current)?;
|
|
(current, else_block) = self.pattern_match(
|
|
current,
|
|
None,
|
|
init_place,
|
|
self.expr_ty(*expr_id),
|
|
*pat,
|
|
BindingAnnotation::Unannotated,
|
|
)?;
|
|
match (else_block, else_branch) {
|
|
(None, _) => (),
|
|
(Some(else_block), None) => {
|
|
self.set_terminator(else_block, Terminator::Unreachable);
|
|
}
|
|
(Some(else_block), Some(else_branch)) => {
|
|
let (_, b) = self
|
|
.lower_expr_to_some_place(*else_branch, else_block)?;
|
|
self.set_terminator(b, Terminator::Unreachable);
|
|
}
|
|
}
|
|
}
|
|
None => continue,
|
|
},
|
|
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
|
let ty = self.expr_ty(*expr);
|
|
let temp = self.temp(ty)?;
|
|
current = self.lower_expr_to_place(*expr, temp.into(), current)?;
|
|
}
|
|
}
|
|
}
|
|
match tail {
|
|
Some(tail) => self.lower_expr_to_place(*tail, place, current),
|
|
None => Ok(current),
|
|
}
|
|
}
|
|
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin, _| {
|
|
let (_, block) = this.lower_expr_to_some_place(*body, begin)?;
|
|
this.set_goto(block, begin);
|
|
Ok(())
|
|
}),
|
|
Expr::While { condition, body, label } => {
|
|
self.lower_loop(current, *label, |this, begin, end| {
|
|
let (discr, to_switch) = this.lower_expr_to_some_operand(*condition, begin)?;
|
|
let after_cond = this.new_basic_block();
|
|
this.set_terminator(
|
|
to_switch,
|
|
Terminator::SwitchInt {
|
|
discr,
|
|
targets: SwitchTargets::static_if(1, after_cond, end),
|
|
},
|
|
);
|
|
let (_, block) = this.lower_expr_to_some_place(*body, after_cond)?;
|
|
this.set_goto(block, begin);
|
|
Ok(())
|
|
})
|
|
}
|
|
Expr::For { .. } => not_supported!("for loop"),
|
|
Expr::Call { callee, args, .. } => {
|
|
let callee_ty = self.expr_ty(*callee);
|
|
match &callee_ty.data(Interner).kind {
|
|
chalk_ir::TyKind::FnDef(..) => {
|
|
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
|
self.lower_call(func, args.iter().copied(), place, current)
|
|
}
|
|
TyKind::Scalar(_)
|
|
| TyKind::Tuple(_, _)
|
|
| TyKind::Array(_, _)
|
|
| TyKind::Adt(_, _)
|
|
| TyKind::Str
|
|
| TyKind::Foreign(_)
|
|
| TyKind::Slice(_) => {
|
|
return Err(MirLowerError::TypeError("function call on data type"))
|
|
}
|
|
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"),
|
|
}
|
|
}
|
|
Expr::MethodCall { receiver, args, .. } => {
|
|
let (func_id, generic_args) =
|
|
self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedName)?;
|
|
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.lower_call(
|
|
func,
|
|
iter::once(*receiver).chain(args.iter().copied()),
|
|
place,
|
|
current,
|
|
)
|
|
}
|
|
Expr::Match { expr, arms } => {
|
|
let (cond_place, mut current) = self.lower_expr_to_some_place(*expr, current)?;
|
|
let cond_ty = self.expr_ty(*expr);
|
|
let end = self.new_basic_block();
|
|
for MatchArm { pat, guard, expr } in arms.iter() {
|
|
if guard.is_some() {
|
|
not_supported!("pattern matching with guard");
|
|
}
|
|
let (then, otherwise) = self.pattern_match(
|
|
current,
|
|
None,
|
|
cond_place.clone(),
|
|
cond_ty.clone(),
|
|
*pat,
|
|
BindingAnnotation::Unannotated,
|
|
)?;
|
|
let block = self.lower_expr_to_place(*expr, place.clone(), then)?;
|
|
self.set_goto(block, end);
|
|
match otherwise {
|
|
Some(o) => current = o,
|
|
None => {
|
|
// The current pattern was irrefutable, so there is no need to generate code
|
|
// for the rest of patterns
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if self.is_unterminated(current) {
|
|
self.set_terminator(current, Terminator::Unreachable);
|
|
}
|
|
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);
|
|
let otherwise = self.new_basic_block();
|
|
Ok(otherwise)
|
|
}
|
|
},
|
|
Expr::Break { expr, label } => {
|
|
if expr.is_some() {
|
|
not_supported!("break with value");
|
|
}
|
|
match label {
|
|
Some(_) => not_supported!("break with label"),
|
|
None => {
|
|
let loop_data =
|
|
self.current_loop_blocks.ok_or(MirLowerError::BreakWithoutLoop)?;
|
|
self.set_goto(current, loop_data.end);
|
|
Ok(self.new_basic_block())
|
|
}
|
|
}
|
|
}
|
|
Expr::Return { expr } => {
|
|
if let Some(expr) = expr {
|
|
current = self.lower_expr_to_place(*expr, return_slot().into(), current)?;
|
|
}
|
|
self.set_terminator(current, Terminator::Return);
|
|
Ok(self.new_basic_block())
|
|
}
|
|
Expr::Yield { .. } => not_supported!("yield"),
|
|
Expr::RecordLit { fields, .. } => {
|
|
let variant_id = self
|
|
.infer
|
|
.variant_resolution_for_expr(expr_id)
|
|
.ok_or(MirLowerError::UnresolvedName)?;
|
|
let subst = match self.expr_ty(expr_id).kind(Interner) {
|
|
TyKind::Adt(_, s) => s.clone(),
|
|
_ => not_supported!("Non ADT record literal"),
|
|
};
|
|
let variant_data = variant_id.variant_data(self.db.upcast());
|
|
match variant_id {
|
|
VariantId::EnumVariantId(_) | VariantId::StructId(_) => {
|
|
let mut operands = vec![None; variant_data.fields().len()];
|
|
for RecordLitField { name, expr } in fields.iter() {
|
|
let field_id =
|
|
variant_data.field(name).ok_or(MirLowerError::UnresolvedName)?;
|
|
let op;
|
|
(op, current) = self.lower_expr_to_some_operand(*expr, current)?;
|
|
operands[u32::from(field_id.into_raw()) as usize] = Some(op);
|
|
}
|
|
self.push_assignment(
|
|
current,
|
|
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"),
|
|
)?,
|
|
),
|
|
);
|
|
Ok(current)
|
|
}
|
|
VariantId::UnionId(union_id) => {
|
|
let [RecordLitField { name, expr }] = fields.as_ref() else {
|
|
not_supported!("Union record literal with more than one field");
|
|
};
|
|
let local_id =
|
|
variant_data.field(name).ok_or(MirLowerError::UnresolvedName)?;
|
|
let mut place = place;
|
|
place
|
|
.projection
|
|
.push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
|
|
self.lower_expr_to_place(*expr, place, current)
|
|
}
|
|
}
|
|
}
|
|
Expr::Field { expr, name } => {
|
|
let (mut current_place, current) = self.lower_expr_to_some_place(*expr, current)?;
|
|
if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
|
|
let index = name
|
|
.as_tuple_index()
|
|
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
|
|
current_place.projection.push(ProjectionElem::TupleField(index))
|
|
} else {
|
|
let field = self
|
|
.infer
|
|
.field_resolution(expr_id)
|
|
.ok_or(MirLowerError::UnresolvedName)?;
|
|
current_place.projection.push(ProjectionElem::Field(field));
|
|
}
|
|
self.push_assignment(current, place, Operand::Copy(current_place).into());
|
|
Ok(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::Cast { expr, type_ref: _ } => {
|
|
let (x, current) = self.lower_expr_to_some_operand(*expr, current)?;
|
|
let source_ty = self.infer[*expr].clone();
|
|
let target_ty = self.infer[expr_id].clone();
|
|
self.push_assignment(
|
|
current,
|
|
place,
|
|
Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty),
|
|
);
|
|
Ok(current)
|
|
}
|
|
Expr::Ref { expr, rawness: _, mutability } => {
|
|
let p;
|
|
(p, current) = self.lower_expr_to_some_place(*expr, current)?;
|
|
let bk = BorrowKind::from_hir(*mutability);
|
|
self.push_assignment(current, place, Rvalue::Ref(bk, p));
|
|
Ok(current)
|
|
}
|
|
Expr::Box { .. } => not_supported!("box expression"),
|
|
Expr::UnaryOp { expr, op } => match op {
|
|
hir_def::expr::UnaryOp::Deref => {
|
|
let (mut tmp, current) = self.lower_expr_to_some_place(*expr, current)?;
|
|
tmp.projection.push(ProjectionElem::Deref);
|
|
self.push_assignment(current, place, Operand::Copy(tmp).into());
|
|
Ok(current)
|
|
}
|
|
hir_def::expr::UnaryOp::Not => {
|
|
let (op, current) = self.lower_expr_to_some_operand(*expr, current)?;
|
|
self.push_assignment(current, place, Rvalue::UnaryOp(UnOp::Not, op));
|
|
Ok(current)
|
|
}
|
|
hir_def::expr::UnaryOp::Neg => {
|
|
let (op, current) = self.lower_expr_to_some_operand(*expr, current)?;
|
|
self.push_assignment(current, place, Rvalue::UnaryOp(UnOp::Neg, op));
|
|
Ok(current)
|
|
}
|
|
},
|
|
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 Some(lhs_place) = self.lower_expr_as_place(*lhs) else {
|
|
not_supported!("assignment to complex place");
|
|
};
|
|
let rhs_op;
|
|
(rhs_op, current) = self.lower_expr_to_some_operand(*rhs, current)?;
|
|
self.push_assignment(current, lhs_place, rhs_op.into());
|
|
return Ok(current);
|
|
}
|
|
let lhs_op;
|
|
(lhs_op, current) = self.lower_expr_to_some_operand(*lhs, current)?;
|
|
let rhs_op;
|
|
(rhs_op, current) = self.lower_expr_to_some_operand(*rhs, current)?;
|
|
self.push_assignment(
|
|
current,
|
|
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::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
|
|
hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
|
|
hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
|
|
},
|
|
lhs_op,
|
|
rhs_op,
|
|
),
|
|
);
|
|
Ok(current)
|
|
}
|
|
Expr::Range { .. } => not_supported!("range"),
|
|
Expr::Index { base, index } => {
|
|
let mut p_base;
|
|
(p_base, current) = self.lower_expr_to_some_place(*base, current)?;
|
|
let l_index = self.temp(self.expr_ty(*index))?;
|
|
current = self.lower_expr_to_place(*index, l_index.into(), current)?;
|
|
p_base.projection.push(ProjectionElem::Index(l_index));
|
|
self.push_assignment(current, place, Operand::Copy(p_base).into());
|
|
Ok(current)
|
|
}
|
|
Expr::Closure { .. } => not_supported!("closure"),
|
|
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
|
let r = Rvalue::Aggregate(
|
|
AggregateKind::Tuple(self.expr_ty(expr_id)),
|
|
exprs
|
|
.iter()
|
|
.map(|x| {
|
|
let o;
|
|
(o, current) = self.lower_expr_to_some_operand(*x, current)?;
|
|
Ok(o)
|
|
})
|
|
.collect::<Result<_>>()?,
|
|
);
|
|
self.push_assignment(current, place, r);
|
|
Ok(current)
|
|
}
|
|
Expr::Unsafe { body } => self.lower_expr_to_place(*body, place, current),
|
|
Expr::Array(l) => match l {
|
|
Array::ElementList { elements, .. } => {
|
|
let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
|
|
TyKind::Array(ty, _) => ty.clone(),
|
|
_ => {
|
|
return Err(MirLowerError::TypeError(
|
|
"Array expression with non array type",
|
|
))
|
|
}
|
|
};
|
|
let r = Rvalue::Aggregate(
|
|
AggregateKind::Array(elem_ty),
|
|
elements
|
|
.iter()
|
|
.map(|x| {
|
|
let o;
|
|
(o, current) = self.lower_expr_to_some_operand(*x, current)?;
|
|
Ok(o)
|
|
})
|
|
.collect::<Result<_>>()?,
|
|
);
|
|
self.push_assignment(current, place, r);
|
|
Ok(current)
|
|
}
|
|
Array::Repeat { .. } => not_supported!("array repeat"),
|
|
},
|
|
Expr::Literal(l) => {
|
|
let ty = self.expr_ty(expr_id);
|
|
let op = self.lower_literal_to_operand(ty, l)?;
|
|
self.push_assignment(current, place, op.into());
|
|
Ok(current)
|
|
}
|
|
Expr::Underscore => not_supported!("underscore"),
|
|
}
|
|
}
|
|
|
|
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())?
|
|
.size
|
|
.bytes_usize();
|
|
let bytes = match l {
|
|
hir_def::expr::Literal::String(b) => {
|
|
let b = b.as_bytes();
|
|
let mut data = vec![];
|
|
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![];
|
|
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 {
|
|
8 => f.into_f64().to_le_bytes().into(),
|
|
4 => f.into_f32().to_le_bytes().into(),
|
|
_ => {
|
|
return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
|
|
}
|
|
},
|
|
};
|
|
Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))
|
|
}
|
|
|
|
fn new_basic_block(&mut self) -> BasicBlockId {
|
|
self.result.basic_blocks.alloc(BasicBlock::default())
|
|
}
|
|
|
|
fn lower_const(
|
|
&mut self,
|
|
const_id: hir_def::ConstId,
|
|
prev_block: BasicBlockId,
|
|
place: Place,
|
|
) -> Result<BasicBlockId> {
|
|
let c = self.db.const_eval(const_id)?;
|
|
self.write_const_to_place(c, prev_block, place)
|
|
}
|
|
|
|
fn write_const_to_place(
|
|
&mut self,
|
|
c: Const,
|
|
prev_block: BasicBlockId,
|
|
place: Place,
|
|
) -> Result<BasicBlockId> {
|
|
self.push_assignment(prev_block, place, Operand::Constant(c).into());
|
|
Ok(prev_block)
|
|
}
|
|
|
|
fn write_bytes_to_place(
|
|
&mut self,
|
|
prev_block: BasicBlockId,
|
|
place: Place,
|
|
cv: Vec<u8>,
|
|
ty: Ty,
|
|
) -> Result<BasicBlockId> {
|
|
self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into());
|
|
Ok(prev_block)
|
|
}
|
|
|
|
fn lower_enum_variant(
|
|
&mut self,
|
|
variant_id: EnumVariantId,
|
|
prev_block: BasicBlockId,
|
|
place: Place,
|
|
ty: Ty,
|
|
fields: Vec<Operand>,
|
|
) -> Result<BasicBlockId> {
|
|
let subst = match ty.kind(Interner) {
|
|
TyKind::Adt(_, subst) => subst.clone(),
|
|
_ => not_supported!("Non ADT enum"),
|
|
};
|
|
self.push_assignment(
|
|
prev_block,
|
|
place,
|
|
Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields),
|
|
);
|
|
Ok(prev_block)
|
|
}
|
|
|
|
fn lower_call(
|
|
&mut self,
|
|
func: Operand,
|
|
args: impl Iterator<Item = ExprId>,
|
|
place: Place,
|
|
mut current: BasicBlockId,
|
|
) -> Result<BasicBlockId> {
|
|
let args = args
|
|
.map(|arg| {
|
|
let temp;
|
|
(temp, current) = self.lower_expr_to_some_operand(arg, current)?;
|
|
Ok(temp)
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
let b = self.result.basic_blocks.alloc(BasicBlock {
|
|
statements: vec![],
|
|
terminator: None,
|
|
is_cleanup: false,
|
|
});
|
|
self.set_terminator(
|
|
current,
|
|
Terminator::Call {
|
|
func,
|
|
args,
|
|
destination: place,
|
|
target: Some(b),
|
|
cleanup: None,
|
|
from_hir_call: true,
|
|
},
|
|
);
|
|
Ok(b)
|
|
}
|
|
|
|
fn is_unterminated(&mut self, source: BasicBlockId) -> bool {
|
|
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_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
|
|
self.set_terminator(source, Terminator::Goto { target });
|
|
}
|
|
|
|
fn expr_ty(&self, e: ExprId) -> Ty {
|
|
self.infer[e].clone()
|
|
}
|
|
|
|
fn push_assignment(&mut self, block: BasicBlockId, place: Place, rvalue: Rvalue) {
|
|
self.result.basic_blocks[block].statements.push(Statement::Assign(place, rvalue));
|
|
}
|
|
|
|
/// 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(_) => not_supported!("or pattern"),
|
|
Pat::Record { .. } => not_supported!("record pattern"),
|
|
Pat::Range { .. } => not_supported!("range pattern"),
|
|
Pat::Slice { .. } => not_supported!("slice pattern"),
|
|
Pat::Path(_) => not_supported!("path pattern"),
|
|
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 { mode, name: _, subpat } => {
|
|
let target_place = self.binding_locals[pattern];
|
|
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_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,
|
|
),
|
|
},
|
|
);
|
|
(current, current_else)
|
|
}
|
|
Pat::TupleStruct { path: _, args, ellipsis } => {
|
|
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
|
not_supported!("unresolved variant");
|
|
};
|
|
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);
|
|
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()),
|
|
);
|
|
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"))
|
|
}
|
|
}
|
|
}
|
|
Pat::Ref { .. } => not_supported!("& pattern"),
|
|
Pat::Box { .. } => not_supported!("box pattern"),
|
|
Pat::ConstBlock(_) => not_supported!("const block pattern"),
|
|
})
|
|
}
|
|
|
|
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 {
|
|
match &self.discr_temp {
|
|
Some(x) => x.clone(),
|
|
None => {
|
|
let tmp: Place =
|
|
self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
|
|
self.discr_temp = Some(tmp.clone());
|
|
tmp
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lower_loop(
|
|
&mut self,
|
|
prev_block: BasicBlockId,
|
|
label: Option<LabelId>,
|
|
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId, BasicBlockId) -> Result<()>,
|
|
) -> Result<BasicBlockId> {
|
|
if label.is_some() {
|
|
not_supported!("loop with label");
|
|
}
|
|
let begin = self.new_basic_block();
|
|
let end = self.new_basic_block();
|
|
let prev = mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end }));
|
|
self.set_goto(prev_block, begin);
|
|
f(self, begin, end)?;
|
|
self.current_loop_blocks = prev;
|
|
Ok(end)
|
|
}
|
|
|
|
fn has_adjustments(&self, expr_id: ExprId) -> bool {
|
|
!self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true)
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
*cond_ty = ty.clone();
|
|
cond_place.projection.push(ProjectionElem::Deref);
|
|
}
|
|
}
|
|
|
|
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
|
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
|
|
(TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
|
|
(chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat,
|
|
(chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt,
|
|
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
|
|
(_, _) => CastKind::IntToInt,
|
|
},
|
|
// Enum to int casts
|
|
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
|
|
CastKind::IntToInt
|
|
}
|
|
(a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"),
|
|
})
|
|
}
|
|
|
|
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
|
|
let body = db.body(def);
|
|
let infer = db.infer(def);
|
|
Ok(Arc::new(lower_to_mir(db, def, &body, &infer, body.body_expr)?))
|
|
}
|
|
|
|
pub fn mir_body_recover(
|
|
_db: &dyn HirDatabase,
|
|
_cycle: &[String],
|
|
_def: &DefWithBodyId,
|
|
) -> Result<Arc<MirBody>> {
|
|
Err(MirLowerError::Loop)
|
|
}
|
|
|
|
pub fn lower_to_mir(
|
|
db: &dyn HirDatabase,
|
|
owner: DefWithBodyId,
|
|
body: &Body,
|
|
infer: &InferenceResult,
|
|
// FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we
|
|
// need to take this input explicitly.
|
|
root_expr: ExprId,
|
|
) -> Result<MirBody> {
|
|
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();
|
|
// 0 is return local
|
|
locals.alloc(Local { mutability: Mutability::Mut, ty: infer[root_expr].clone() });
|
|
let mut create_local_of_path = |p: PatId| {
|
|
// FIXME: mutablity is broken
|
|
locals.alloc(Local { mutability: Mutability::Not, ty: infer[p].clone() })
|
|
};
|
|
// 1 to param_len is for params
|
|
let mut binding_locals: ArenaMap<PatId, LocalId> =
|
|
body.params.iter().map(|&x| (x, create_local_of_path(x))).collect();
|
|
// and then rest of bindings
|
|
for (pat_id, _) in body.pats.iter() {
|
|
if !binding_locals.contains_idx(pat_id) {
|
|
binding_locals.insert(pat_id, create_local_of_path(pat_id));
|
|
}
|
|
}
|
|
let mir = MirBody { basic_blocks, locals, start_block, owner, arg_count: body.params.len() };
|
|
let mut ctx = MirLowerCtx {
|
|
result: mir,
|
|
db,
|
|
infer,
|
|
body,
|
|
binding_locals,
|
|
owner,
|
|
current_loop_blocks: None,
|
|
discr_temp: None,
|
|
};
|
|
let b = ctx.lower_expr_to_place(root_expr, return_slot().into(), start_block)?;
|
|
ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
|
|
Ok(ctx.result)
|
|
}
|