Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate loop continue and break during HIR lowering #565

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/rune/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ pub use self::expr_assign::ExprAssign;
pub use self::expr_await::ExprAwait;
pub use self::expr_binary::{BinOp, ExprBinary};
pub use self::expr_block::ExprBlock;
pub use self::expr_break::{ExprBreak, ExprBreakValue};
pub use self::expr_break::ExprBreak;
pub use self::expr_call::ExprCall;
pub use self::expr_closure::{ExprClosure, ExprClosureArgs};
pub use self::expr_continue::ExprContinue;
Expand Down
36 changes: 5 additions & 31 deletions crates/rune/src/ast/expr_break.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,12 @@ pub struct ExprBreak {
pub attributes: Vec<ast::Attribute>,
/// The return token.
pub break_token: T![break],
/// An optional expression to break with.
/// A label to break to.
#[rune(iter)]
pub expr: Option<Box<ExprBreakValue>>,
pub label: Option<ast::Label>,
/// An expression to break with.
#[rune(iter)]
pub expr: Option<Box<ast::Expr>>,
}

expr_parse!(Break, ExprBreak, "break expression");

/// Things that we can break on.
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum ExprBreakValue {
/// Breaking a value out of a loop.
Expr(ast::Expr),
/// Break and jump to the given label.
Label(ast::Label),
}

impl Parse for ExprBreakValue {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
Ok(match p.nth(0)? {
K!['label] => Self::Label(p.parse()?),
_ => Self::Expr(p.parse()?),
})
}
}

impl Peek for ExprBreakValue {
fn peek(p: &mut Peeker<'_>) -> bool {
match p.nth(0) {
K!['label] => true,
_ => ast::Expr::peek(p),
}
}
}
11 changes: 6 additions & 5 deletions crates/rune/src/compile/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,15 @@ impl<'arena> CompileBuildEntry<'_, 'arena> {
closure.ast.args.as_slice().iter().map(|(a, _)| a),
)?;

let captures = self.q.pool.item_type_hash(item_meta.item);

let arena = hir::Arena::new();
let mut cx = hir::lowering::Ctxt::with_query(
&arena,
self.q.borrow(),
item_meta.location.source_id,
);
let hir =
hir::lowering::expr_closure_secondary(&mut cx, &closure.ast, closure.captures)?;
let hir = hir::lowering::expr_closure_secondary(&mut cx, &closure.ast, captures)?;
let mut c = self.compiler1(location, &closure.ast, &mut asm);
assemble::expr_closure_secondary(&mut c, &hir, &closure.ast)?;

Expand All @@ -295,16 +296,16 @@ impl<'arena> CompileBuildEntry<'_, 'arena> {

use self::v1::assemble;

let span = &b.ast;
let captures = self.q.pool.item_type_hash(item_meta.item);

let arena = hir::Arena::new();
let mut cx = hir::lowering::Ctxt::with_query(
&arena,
self.q.borrow(),
item_meta.location.source_id,
);
let hir = hir::lowering::async_block_secondary(&mut cx, &b.ast, b.captures)?;
let mut c = self.compiler1(location, span, &mut asm);
let hir = hir::lowering::async_block_secondary(&mut cx, &b.ast, captures)?;
let mut c = self.compiler1(location, &b.ast, &mut asm);
assemble::async_block_secondary(&mut c, &hir)?;

if used.is_unused() {
Expand Down
52 changes: 19 additions & 33 deletions crates/rune/src/compile/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use crate::no_std::prelude::*;
use crate as rune;
use crate::ast::{self, Span, Spanned};
use crate::compile::ir;
use crate::compile::ir::eval::IrEvalBreak;
use crate::compile::{self, WithSpan};
use crate::hir;
use crate::indexing::index;
Expand Down Expand Up @@ -389,27 +388,26 @@ pub(crate) struct IrBreak {
/// The span of the break.
#[rune(span)]
pub(crate) span: Span,
/// The kind of the break.
pub(crate) kind: BreakKind,
/// The label of the break.
pub(crate) label: Option<Box<str>>,
/// The value of the break.
pub(crate) expr: Option<Box<Ir>>,
}

impl IrBreak {
fn compile_ast(
span: Span,
cx: &mut Ctxt<'_, '_>,
hir: Option<&hir::ExprBreakValue>,
hir: &hir::ExprBreak,
) -> compile::Result<Self> {
let kind = match hir {
Some(expr) => match *expr {
hir::ExprBreakValue::Expr(e) => ir::BreakKind::Ir(Box::new(compiler::expr(e, cx)?)),
hir::ExprBreakValue::Label(label) => {
ir::BreakKind::Label(cx.resolve(label)?.into())
}
},
None => ir::BreakKind::Inherent,
let label = hir.label.map(Into::into);

let expr = match hir.expr {
Some(e) => Some(Box::new(compiler::expr(e, cx)?)),
None => None,
};

Ok(ir::IrBreak { span, kind })
Ok(ir::IrBreak { span, label, expr })
}

/// Evaluate the break into an [ir::EvalOutcome].
Expand All @@ -420,28 +418,16 @@ impl IrBreak {
return e.into();
}

match &self.kind {
BreakKind::Ir(ir) => match ir::eval_ir(ir, interp, used) {
Ok(value) => ir::EvalOutcome::Break(span, IrEvalBreak::Value(value)),
Err(err) => err,
let expr = match &self.expr {
Some(ir) => match ir::eval_ir(ir, interp, used) {
Ok(value) => Some(value),
Err(err) => return err,
},
BreakKind::Label(label) => {
ir::EvalOutcome::Break(span, IrEvalBreak::Label(label.clone()))
}
BreakKind::Inherent => ir::EvalOutcome::Break(span, IrEvalBreak::Inherent),
}
}
}
None => None,
};

/// The kind of a break expression.
#[derive(Debug, Clone)]
pub(crate) enum BreakKind {
/// Break to the next loop.
Inherent,
/// Break to the given label.
Label(Box<str>),
/// Break with the value acquired from evaluating the ir.
Ir(Box<Ir>),
ir::EvalOutcome::Break(span, self.label.clone(), expr)
}
}

/// Tuple expression.
Expand Down
16 changes: 1 addition & 15 deletions crates/rune/src/compile/ir/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::ast::{self, Span, Spanned};
use crate::compile::ir;
use crate::compile::{self, ErrorKind};
use crate::hir;
use crate::parse::Resolve;
use crate::query::Query;
use crate::runtime::{Bytes, Shared};
use crate::SourceId;
Expand All @@ -21,16 +20,6 @@ pub(crate) struct Ctxt<'a, 'arena> {
pub(crate) q: Query<'a, 'arena>,
}

impl Ctxt<'_, '_> {
/// Resolve the given resolvable value.
pub(crate) fn resolve<'s, T>(&'s self, value: &T) -> compile::Result<T::Output>
where
T: Resolve<'s>,
{
value.resolve(resolve_context!(self.q))
}
}

#[instrument]
pub(crate) fn expr(hir: &hir::Expr<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result<ir::Ir> {
let span = hir.span();
Expand Down Expand Up @@ -427,10 +416,7 @@ fn expr_loop(
) -> compile::Result<ir::IrLoop> {
Ok(ir::IrLoop {
span,
label: match hir.label {
Some(label) => Some(c.resolve(label)?.into()),
None => None,
},
label: hir.label.map(|l| l.into()),
condition: match hir.condition {
Some(hir) => Some(Box::new(condition(hir, c)?)),
None => None,
Expand Down
57 changes: 23 additions & 34 deletions crates/rune/src/compile/ir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum EvalOutcome {
/// A compile error.
Error(compile::Error),
/// Break until the next loop, or the optional label.
Break(Span, IrEvalBreak),
Break(Span, Option<Box<str>>, Option<ir::Value>),
}

impl EvalOutcome {
Expand All @@ -46,16 +46,6 @@ where
}
}

/// The value of a break.
pub enum IrEvalBreak {
/// Break the next nested loop.
Inherent,
/// The break had a value.
Value(ir::Value),
/// The break had a label.
Label(Box<str>),
}

fn eval_ir_assign(
ir: &ir::IrAssign,
interp: &mut ir::Interpreter<'_, '_>,
Expand Down Expand Up @@ -241,47 +231,46 @@ fn eval_ir_loop(

let guard = interp.scopes.push();

loop {
let value = loop {
if let Some(condition) = &ir.condition {
interp.scopes.clear_current().with_span(condition)?;

let value = eval_ir_condition(condition, interp, used)?;

if !as_bool(condition.span(), value)? {
break;
break None;
}
}

match eval_ir_scope(&ir.body, interp, used) {
Ok(..) => (),
Err(outcome) => match outcome {
EvalOutcome::Break(span, b) => match b {
IrEvalBreak::Inherent => break,
IrEvalBreak::Label(l) => {
if ir.label.as_ref() == Some(&l) {
break;
}

return Err(EvalOutcome::Break(span, IrEvalBreak::Label(l)));
}
IrEvalBreak::Value(value) => {
if ir.condition.is_none() {
return Ok(value);
}

return Err(EvalOutcome::from(compile::Error::msg(
span,
"break with value is not supported for unconditional loops",
)));
EvalOutcome::Break(span, label, expr) => {
if label.as_deref() == ir.label.as_deref() {
break expr;
} else {
return Err(EvalOutcome::Break(span, label, expr));
}
},
}
outcome => return Err(outcome),
},
};
}
};

interp.scopes.pop(guard).with_span(ir)?;
Ok(ir::Value::Unit)

if let Some(value) = value {
if ir.condition.is_some() {
return Err(EvalOutcome::from(compile::Error::msg(
span,
"break with value is not supported for unconditional loops",
)));
}

Ok(value)
} else {
Ok(ir::Value::Unit)
}
}

fn eval_ir_object(
Expand Down
4 changes: 2 additions & 2 deletions crates/rune/src/compile/ir/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Interpreter<'_, '_> {
ir::EvalOutcome::NotConst(span) => {
return Err(compile::Error::new(span, IrErrorKind::NotConst))
}
ir::EvalOutcome::Break(span, _) => {
ir::EvalOutcome::Break(span, _, _) => {
return Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
}
},
Expand Down Expand Up @@ -76,7 +76,7 @@ impl Interpreter<'_, '_> {
ir::EvalOutcome::NotConst(span) => {
Err(compile::Error::new(span, IrErrorKind::NotConst))
}
ir::EvalOutcome::Break(span, _) => {
ir::EvalOutcome::Break(span, _, _) => {
Err(compile::Error::new(span, IrErrorKind::BreakOutsideOfLoop))
}
},
Expand Down
Loading