use crate::plan::values::ValuePlan;
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 ends_with_diverge(&self) -> bool {
self.postlude
.last()
.is_some_and(LoweredStatement::ends_with_diverge)
}
}
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 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,
}
}
}
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()
}
}
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()
}
}
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(),
}
}
}