use crate::plan::values::ValuePlan;
use crate::utils::output_references_var;
use syntax::types::Type;
pub(crate) enum PlacePlan<'a> {
Statement,
Return,
Assign {
local: &'a str,
target_ty: Option<&'a Type>,
},
}
impl PlacePlan<'_> {
pub(crate) fn is_return(&self) -> bool {
matches!(self, PlacePlan::Return)
}
}
pub(crate) struct LoweredBlock {
pub(crate) statements: Vec<LoweredStatement>,
}
pub(crate) enum LoweredStatement {
If(IfPlan),
Loop(LoopPlan),
Block(LoweredBlock),
Break {
directive: String,
label: Option<String>,
},
Continue {
directive: String,
label: Option<String>,
},
Const(ConstPlan),
Return(ReturnStatementPlan),
BreakValue(BreakValuePlan),
Let(LetPlan),
Assign(AssignPlan),
Expression(ExpressionStatementPlan),
Match(MatchStatementPlan),
Select(SelectStatementPlan),
Switch(SwitchStatementPlan),
WhileLet(WhileLetPlan),
TempBind {
name: String,
value: String,
},
ClosureBind {
name: String,
closure_open: String,
body: LoweredBlock,
closure_close: String,
},
RawGo(String),
DivergingRawGo(String),
UnreachablePanic,
}
pub(crate) struct ConstPlan {
pub(crate) directive: String,
pub(crate) is_const: bool,
pub(crate) name: String,
pub(crate) ty_str: String,
pub(crate) value: ValuePlan,
}
pub(crate) struct ReturnStatementPlan {
pub(crate) directive: String,
pub(crate) form: ReturnForm,
}
pub(crate) enum ReturnForm {
Plain {
value: ValuePlan,
},
Unit {
side_effect: Option<LoweredBlock>,
},
Multi {
values: Vec<String>,
},
LoweredAbi {
body: LoweredBlock,
},
Wrapped {
body: LoweredBlock,
},
}
pub(crate) struct BreakValuePlan {
pub(crate) directive: String,
pub(crate) value: ValuePlan,
pub(crate) disposition: BreakValueDisposition,
pub(crate) label: Option<String>,
}
pub(crate) enum BreakValueDisposition {
Diverged,
UnitCallIntoResult { result_var: String },
AssignToResult { result_var: String },
Discard,
}
pub(crate) struct LetPlan {
pub(crate) directive: String,
pub(crate) form: LetForm,
}
pub(crate) enum LetForm {
Never {
declaration: Option<String>,
body: LoweredBlock,
},
SimpleIdentifier {
body: LoweredBlock,
},
Discard {
body: LoweredBlock,
},
ComplexPattern {
body: LoweredBlock,
},
MultiValueCall {
body: LoweredBlock,
},
LetElse {
body: LoweredBlock,
},
}
impl LetForm {
pub(crate) fn body(&self) -> &LoweredBlock {
match self {
LetForm::Never { body, .. }
| LetForm::SimpleIdentifier { body }
| LetForm::Discard { body }
| LetForm::ComplexPattern { body }
| LetForm::MultiValueCall { body }
| LetForm::LetElse { body } => body,
}
}
}
pub(crate) struct AssignPlan {
pub(crate) directive: String,
pub(crate) form: AssignForm,
}
pub(crate) enum AssignForm {
Compound {
target_capture: Vec<LoweredStatement>,
target_str: String,
kind: CompoundKind,
},
Simple {
target_capture: Vec<LoweredStatement>,
target_str: String,
value: ValuePlan,
},
NilClear {
target_capture: Vec<LoweredStatement>,
target_str: String,
},
Discard { body: LoweredBlock },
NeverTyped { body: LoweredBlock },
}
pub(crate) enum CompoundKind {
Increment,
Decrement,
OpAssign {
op_text: String,
rhs: ValuePlan,
},
}
pub(crate) struct ExpressionStatementPlan {
pub(crate) directive: String,
pub(crate) form: ExpressionStatementForm,
}
pub(crate) enum ExpressionStatementForm {
Async {
value: ValuePlan,
},
AsyncBlock {
keyword: String,
body: LoweredBlock,
},
Propagate {
body: LoweredBlock,
},
Discard {
body: LoweredBlock,
},
}
pub(crate) struct MatchStatementPlan {
pub(crate) directive: String,
pub(crate) body: LoweredBlock,
}
pub(crate) struct SwitchStatementPlan {
pub(crate) directive: String,
pub(crate) kind: SwitchKind,
pub(crate) cases: Vec<SwitchCasePlan>,
pub(crate) default: Option<LoweredBlock>,
pub(crate) postlude: Vec<LoweredStatement>,
}
pub(crate) enum SwitchKind {
Value { subject: String },
Type {
subject: String,
binding: Option<String>,
},
}
pub(crate) struct SwitchCasePlan {
pub(crate) labels: String,
pub(crate) body: LoweredBlock,
}
impl SwitchStatementPlan {
fn references_var(&self, var: &str) -> bool {
let kind_references = match &self.kind {
SwitchKind::Value { subject } => output_references_var(subject, var),
SwitchKind::Type { subject, binding } => {
output_references_var(subject, var)
|| binding
.as_deref()
.is_some_and(|binding| output_references_var(binding, var))
}
};
kind_references
|| self.cases.iter().any(|case| case.references_var(var))
|| self
.default
.as_ref()
.is_some_and(|body| body.references_var(var))
|| self.postlude.iter().any(|s| s.references_var(var))
}
fn ends_with_diverge(&self) -> bool {
self.postlude
.last()
.is_some_and(LoweredStatement::ends_with_diverge)
}
}
impl SwitchCasePlan {
pub(crate) fn references_var(&self, var: &str) -> bool {
output_references_var(&self.labels, var) || self.body.references_var(var)
}
}
pub(crate) struct SelectStatementPlan {
pub(crate) directive: String,
pub(crate) setup: Vec<LoweredStatement>,
pub(crate) retry_loop: bool,
pub(crate) arms: Vec<SelectArmPlan>,
pub(crate) postlude: Vec<LoweredStatement>,
}
pub(crate) enum SelectArmPlan {
Receive {
receive_vars: Option<String>,
channel: String,
body: LoweredBlock,
},
Send {
operation: String,
body: LoweredBlock,
},
Default { body: LoweredBlock },
}
impl SelectStatementPlan {
fn references_var(&self, var: &str) -> bool {
self.setup.iter().any(|s| s.references_var(var))
|| self.arms.iter().any(|arm| arm.references_var(var))
|| self.postlude.iter().any(|s| s.references_var(var))
}
fn ends_with_diverge(&self) -> bool {
self.postlude
.last()
.is_some_and(LoweredStatement::ends_with_diverge)
|| self.all_arms_diverge()
}
pub(crate) fn all_arms_diverge(&self) -> bool {
!self.arms.is_empty() && self.arms.iter().all(|arm| arm.body().ends_with_diverge())
}
}
impl SelectArmPlan {
pub(crate) fn body(&self) -> &LoweredBlock {
match self {
SelectArmPlan::Receive { body, .. }
| SelectArmPlan::Send { body, .. }
| SelectArmPlan::Default { body } => body,
}
}
fn references_var(&self, var: &str) -> bool {
let header_references = match self {
SelectArmPlan::Receive {
receive_vars,
channel,
..
} => {
receive_vars
.as_deref()
.is_some_and(|vars| output_references_var(vars, var))
|| output_references_var(channel, var)
}
SelectArmPlan::Send { operation, .. } => output_references_var(operation, var),
SelectArmPlan::Default { .. } => false,
};
header_references || self.body().references_var(var)
}
}
pub(crate) struct WhileLetPlan {
pub(crate) directive: String,
pub(crate) body: LoweredBlock,
}
pub(crate) struct LoopPlan {
pub(crate) directive: String,
pub(crate) prologue: String,
pub(crate) label: Option<String>,
pub(crate) header: String,
pub(crate) body: LoweredBlock,
}
pub(crate) struct IfPlan {
pub(crate) directive: String,
pub(crate) condition_setup: String,
pub(crate) condition: String,
pub(crate) then_body: LoweredBlock,
pub(crate) else_arm: ElseArm,
}
pub(crate) enum ElseArm {
None,
ElseIf(Box<IfPlan>),
Else {
body: LoweredBlock,
inline: bool,
},
}
impl LoweredBlock {
pub(crate) fn ends_with_diverge(&self) -> bool {
self.statements
.last()
.is_some_and(LoweredStatement::ends_with_diverge)
}
pub(crate) fn is_empty(&self) -> bool {
self.statements.is_empty()
}
#[allow(dead_code)]
pub(crate) fn references_var(&self, var: &str) -> bool {
self.statements
.iter()
.any(|statement| statement.references_var(var))
}
}
impl LoweredStatement {
fn ends_with_diverge(&self) -> bool {
match self {
LoweredStatement::If(plan) => plan.ends_with_diverge(),
LoweredStatement::Loop(_) | LoweredStatement::Block(_) | LoweredStatement::Const(_) => {
false
}
LoweredStatement::Break { .. } | LoweredStatement::Continue { .. } => true,
LoweredStatement::Return(_) => true,
LoweredStatement::BreakValue(_) => true,
LoweredStatement::Let(plan) => plan.form.body().ends_with_diverge(),
LoweredStatement::Assign(plan) => match &plan.form {
AssignForm::Compound { .. }
| AssignForm::Simple { .. }
| AssignForm::NilClear { .. } => false,
AssignForm::Discard { body } | AssignForm::NeverTyped { body } => {
body.ends_with_diverge()
}
},
LoweredStatement::Expression(plan) => match &plan.form {
ExpressionStatementForm::Async { .. }
| ExpressionStatementForm::AsyncBlock { .. } => false,
ExpressionStatementForm::Propagate { body }
| ExpressionStatementForm::Discard { body } => body.ends_with_diverge(),
},
LoweredStatement::Match(plan) => plan.body.ends_with_diverge(),
LoweredStatement::Select(plan) => plan.ends_with_diverge(),
LoweredStatement::Switch(plan) => plan.ends_with_diverge(),
LoweredStatement::WhileLet(plan) => plan.body.ends_with_diverge(),
LoweredStatement::TempBind { .. } | LoweredStatement::ClosureBind { .. } => false,
LoweredStatement::RawGo(_) => false,
LoweredStatement::DivergingRawGo(_) | LoweredStatement::UnreachablePanic => true,
}
}
pub(crate) fn blocks_fallthrough(&self) -> bool {
!matches!(self, LoweredStatement::WhileLet(_)) && self.ends_with_diverge()
}
pub(crate) fn references_var(&self, var: &str) -> bool {
match self {
LoweredStatement::If(plan) => plan.references_var(var),
LoweredStatement::Loop(plan) => plan.references_var(var),
LoweredStatement::Block(body) => body.references_var(var),
LoweredStatement::Break { label, .. } | LoweredStatement::Continue { label, .. } => {
label.as_deref() == Some(var)
}
LoweredStatement::Const(plan) => plan.value.references_var(var),
LoweredStatement::Return(plan) => match &plan.form {
ReturnForm::Plain { value } => value.references_var(var),
ReturnForm::Unit { side_effect } => {
side_effect.as_ref().is_some_and(|b| b.references_var(var))
}
ReturnForm::Multi { values } => {
values.iter().any(|v| output_references_var(v, var))
}
ReturnForm::LoweredAbi { body } | ReturnForm::Wrapped { body } => {
body.references_var(var)
}
},
LoweredStatement::BreakValue(plan) => {
if plan.value.references_var(var) {
return true;
}
match &plan.disposition {
BreakValueDisposition::UnitCallIntoResult { result_var }
| BreakValueDisposition::AssignToResult { result_var } => result_var == var,
BreakValueDisposition::Diverged | BreakValueDisposition::Discard => false,
}
}
LoweredStatement::Let(plan) => {
let declaration_ref = match &plan.form {
LetForm::Never { declaration, .. } => declaration
.as_ref()
.is_some_and(|s| output_references_var(s, var)),
_ => false,
};
declaration_ref || plan.form.body().references_var(var)
}
LoweredStatement::Assign(plan) => match &plan.form {
AssignForm::Compound {
target_capture,
target_str,
kind,
} => {
if target_capture.iter().any(|s| s.references_var(var)) {
return true;
}
if output_references_var(target_str, var) {
return true;
}
match kind {
CompoundKind::Increment | CompoundKind::Decrement => false,
CompoundKind::OpAssign { rhs, .. } => rhs.references_var(var),
}
}
AssignForm::Simple {
target_capture,
target_str,
value,
} => {
target_capture.iter().any(|s| s.references_var(var))
|| output_references_var(target_str, var)
|| value.references_var(var)
}
AssignForm::NilClear {
target_capture,
target_str,
} => {
target_capture.iter().any(|s| s.references_var(var))
|| output_references_var(target_str, var)
}
AssignForm::Discard { body } | AssignForm::NeverTyped { body } => {
body.references_var(var)
}
},
LoweredStatement::Expression(plan) => match &plan.form {
ExpressionStatementForm::Async { value } => value.references_var(var),
ExpressionStatementForm::AsyncBlock { body, .. } => body.references_var(var),
ExpressionStatementForm::Propagate { body }
| ExpressionStatementForm::Discard { body } => body.references_var(var),
},
LoweredStatement::Match(plan) => plan.body.references_var(var),
LoweredStatement::Select(plan) => plan.references_var(var),
LoweredStatement::Switch(plan) => plan.references_var(var),
LoweredStatement::WhileLet(plan) => plan.body.references_var(var),
LoweredStatement::TempBind { name, value } => {
output_references_var(name, var) || output_references_var(value, var)
}
LoweredStatement::ClosureBind {
name,
closure_open,
body,
closure_close,
} => {
output_references_var(name, var)
|| output_references_var(closure_open, var)
|| body.references_var(var)
|| output_references_var(closure_close, var)
}
LoweredStatement::RawGo(code) | LoweredStatement::DivergingRawGo(code) => {
output_references_var(code, var)
}
LoweredStatement::UnreachablePanic => false,
}
}
}
impl IfPlan {
fn ends_with_diverge(&self) -> bool {
if !self.then_body.ends_with_diverge() {
return false;
}
match &self.else_arm {
ElseArm::None => false,
ElseArm::ElseIf(inner) if inner.condition_setup.is_empty() => inner.ends_with_diverge(),
ElseArm::ElseIf(_) => false,
ElseArm::Else { body, .. } => body.ends_with_diverge(),
}
}
#[allow(dead_code)]
fn references_var(&self, var: &str) -> bool {
output_references_var(&self.condition_setup, var)
|| output_references_var(&self.condition, var)
|| self.then_body.references_var(var)
|| match &self.else_arm {
ElseArm::None => false,
ElseArm::ElseIf(plan) => plan.references_var(var),
ElseArm::Else { body, .. } => body.references_var(var),
}
}
}
impl LoopPlan {
#[allow(dead_code)]
fn references_var(&self, var: &str) -> bool {
output_references_var(&self.prologue, var)
|| output_references_var(&self.header, var)
|| self.body.references_var(var)
}
}