use super::*;
pub(crate) fn is_multiret_results(results: crate::transformer::ResultPack) -> bool {
match results {
crate::transformer::ResultPack::Open(_) => true,
crate::transformer::ResultPack::Fixed(range) => range.len > 1,
crate::transformer::ResultPack::Ignore => false,
}
}
pub(crate) fn expr_for_fixed_def(lowering: &ProtoLowering<'_>, def_id: DefId) -> Option<HirExpr> {
if let Some(expr) = expr_for_dup_safe_fixed_def(lowering, def_id) {
return Some(expr);
}
let def_instr = lowering.dataflow.def_instr(def_id);
let def_reg = lowering.dataflow.def_reg(def_id);
let def_block = lowering.dataflow.def_block(def_id);
let instr = lowering.proto.instrs.get(def_instr.index())?;
match instr {
LowInstr::GetTable(get_table) if get_table.dst == def_reg => {
Some(lower_table_access_expr_inline(
lowering,
def_block,
def_instr,
get_table.base,
get_table.key,
))
}
LowInstr::NewTable(new_table) if new_table.dst == def_reg => {
Some(HirExpr::TableConstructor(Box::default()))
}
LowInstr::Call(call) => expr_for_fixed_call(lowering, def_block, def_instr, call, def_reg),
LowInstr::VarArg(vararg) => expr_for_fixed_vararg(vararg.results, def_reg),
LowInstr::Closure(closure) if closure.dst == def_reg => {
Some(HirExpr::Closure(Box::new(HirClosureExpr {
proto: lowering.child_refs[closure.proto.index()],
captures: closure
.captures
.iter()
.map(|capture| HirCapture {
value: match capture.source {
crate::transformer::CaptureSource::Reg(reg) if reg == closure.dst => {
HirExpr::TempRef(lowering.bindings.fixed_temps[def_id.index()])
}
crate::transformer::CaptureSource::Reg(reg) => {
expr_for_reg_use_inline(lowering, def_block, def_instr, reg)
}
crate::transformer::CaptureSource::Upvalue(upvalue) => {
HirExpr::UpvalueRef(UpvalueId(upvalue.index()))
}
},
})
.collect(),
})))
}
LowInstr::GenericForCall(for_call) => expr_for_generic_for_call(for_call.results, def_reg),
LowInstr::SetUpvalue(_)
| LowInstr::SetTable(_)
| LowInstr::SetList(_)
| LowInstr::TailCall(_)
| LowInstr::Return(_)
| LowInstr::Close(_)
| LowInstr::NumericForInit(_)
| LowInstr::NumericForLoop(_)
| LowInstr::GenericForLoop(_)
| LowInstr::Jump(_)
| LowInstr::Branch(_) => None,
_ => None,
}
}
pub(crate) fn expr_for_fixed_def_single_eval(
lowering: &ProtoLowering<'_>,
def_id: DefId,
) -> Option<HirExpr> {
let def_instr = lowering.dataflow.def_instr(def_id);
let def_reg = lowering.dataflow.def_reg(def_id);
let def_block = lowering.dataflow.def_block(def_id);
let instr = lowering.proto.instrs.get(def_instr.index())?;
match instr {
LowInstr::Move(move_instr) if move_instr.dst == def_reg => {
return Some(expr_for_reg_use_single_eval(
lowering,
def_block,
def_instr,
move_instr.src,
));
}
LowInstr::GetTable(get_table) if get_table.dst == def_reg => {
return Some(lower_table_access_expr_single_eval(
lowering,
def_block,
def_instr,
get_table.base,
get_table.key,
));
}
_ => {}
}
expr_for_fixed_def(lowering, def_id)
}
pub(crate) fn expr_for_dup_safe_fixed_def(
lowering: &ProtoLowering<'_>,
def_id: DefId,
) -> Option<HirExpr> {
let def_instr = lowering.dataflow.def_instr(def_id);
let def_reg = lowering.dataflow.def_reg(def_id);
let def_block = lowering.dataflow.def_block(def_id);
let instr = lowering.proto.instrs.get(def_instr.index())?;
match instr {
LowInstr::Move(move_instr) if move_instr.dst == def_reg => Some(expr_for_reg_use_inline(
lowering,
def_block,
def_instr,
move_instr.src,
)),
LowInstr::LoadNil(load_nil) if reg_in_range(load_nil.dst, def_reg) => Some(HirExpr::Nil),
LowInstr::LoadBool(load_bool) if load_bool.dst == def_reg => {
Some(HirExpr::Boolean(load_bool.value))
}
LowInstr::LoadConst(load_const) if load_const.dst == def_reg => {
Some(expr_for_const(lowering.proto, load_const.value))
}
LowInstr::LoadInteger(load_integer) if load_integer.dst == def_reg => {
Some(HirExpr::Integer(load_integer.value))
}
LowInstr::LoadNumber(load_number) if load_number.dst == def_reg => {
Some(HirExpr::Number(load_number.value))
}
LowInstr::UnaryOp(unary) if unary.dst == def_reg => {
Some(HirExpr::Unary(Box::new(HirUnaryExpr {
op: lower_unary_op(unary.op),
expr: expr_for_reg_use_inline(lowering, def_block, def_instr, unary.src),
})))
}
LowInstr::BinaryOp(binary) if binary.dst == def_reg => {
Some(super::super::helpers::binary_expr(
lower_binary_op(binary.op),
expr_for_value_operand_inline(lowering, def_block, def_instr, binary.lhs),
expr_for_value_operand_inline(lowering, def_block, def_instr, binary.rhs),
))
}
LowInstr::Concat(concat) if concat.dst == def_reg => {
let value = concat_expr((0..concat.src.len).map(|offset| {
expr_for_reg_use_inline(
lowering,
def_block,
def_instr,
Reg(concat.src.start.index() + offset),
)
}));
Some(value)
}
LowInstr::GetUpvalue(get_upvalue) if get_upvalue.dst == def_reg => {
Some(HirExpr::UpvalueRef(UpvalueId(get_upvalue.src.index())))
}
_ => None,
}
}
fn expr_for_fixed_call(
lowering: &ProtoLowering<'_>,
block: BlockRef,
instr_ref: InstrRef,
call: &crate::transformer::CallInstr,
reg: Reg,
) -> Option<HirExpr> {
let ResultPack::Fixed(results) = call.results else {
return None;
};
if results.len != 1 || results.start != reg {
return None;
}
Some(HirExpr::Call(Box::new(HirCallExpr {
callee: expr_for_reg_use_single_eval(lowering, block, instr_ref, call.callee),
args: lower_value_pack_single_eval(lowering, block, instr_ref, call.args),
multiret: false,
method: matches!(call.kind, CallKind::Method),
method_name: lower_method_name(lowering.proto, call.method_name),
})))
}
fn expr_for_fixed_vararg(results: ResultPack, reg: Reg) -> Option<HirExpr> {
let ResultPack::Fixed(range) = results else {
return None;
};
if range.len == 1 && range.start == reg {
Some(HirExpr::VarArg)
} else {
None
}
}
fn expr_for_generic_for_call(results: ResultPack, reg: Reg) -> Option<HirExpr> {
let ResultPack::Fixed(range) = results else {
return None;
};
if range.len == 1 && range.start == reg {
Some(unresolved_expr("generic-for-call"))
} else {
None
}
}