From 3c5d19f21cebce3e4f5b72a1f928cf09153e1496 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Sun, 28 Jul 2024 18:32:40 +0200 Subject: [PATCH] rune: Lower to anonymous variables --- crates/rune/src/ast/expr_closure.rs | 8 - crates/rune/src/compile/compile.rs | 229 +++++++++++--------- crates/rune/src/compile/error.rs | 16 +- crates/rune/src/compile/ir.rs | 16 +- crates/rune/src/compile/ir/compiler.rs | 6 +- crates/rune/src/compile/ir/eval.rs | 2 +- crates/rune/src/compile/ir/interpreter.rs | 21 +- crates/rune/src/compile/ir/scopes.rs | 12 +- crates/rune/src/compile/v1/assemble.rs | 43 ++-- crates/rune/src/compile/v1/scopes.rs | 12 +- crates/rune/src/hir/hir.rs | 107 ++++------ crates/rune/src/hir/lowering.rs | 244 +++++++--------------- crates/rune/src/hir/scopes.rs | 68 ++++-- crates/rune/src/indexing/mod.rs | 16 -- crates/rune/src/parse/id.rs | 9 +- crates/rune/src/query/mod.rs | 35 +++- crates/rune/src/query/query.rs | 22 -- 17 files changed, 387 insertions(+), 479 deletions(-) diff --git a/crates/rune/src/ast/expr_closure.rs b/crates/rune/src/ast/expr_closure.rs index e073f6128..0422faff1 100644 --- a/crates/rune/src/ast/expr_closure.rs +++ b/crates/rune/src/ast/expr_closure.rs @@ -75,14 +75,6 @@ pub enum ExprClosureArgs { } impl ExprClosureArgs { - /// The number of arguments the closure takes. - pub(crate) fn len(&self) -> usize { - match self { - Self::Empty { .. } => 0, - Self::List { args, .. } => args.len(), - } - } - /// Get a slice over all arguments. pub(crate) fn as_slice(&self) -> &[(ast::FnArg, Option)] { match self { diff --git a/crates/rune/src/compile/compile.rs b/crates/rune/src/compile/compile.rs index 86f5df3f0..799ee5cb4 100644 --- a/crates/rune/src/compile/compile.rs +++ b/crates/rune/src/compile/compile.rs @@ -1,6 +1,4 @@ -use crate::alloc; use crate::alloc::prelude::*; -use crate::ast; use crate::ast::{Span, Spanned}; use crate::compile::v1; use crate::compile::{ @@ -11,10 +9,11 @@ use crate::hir; use crate::indexing::FunctionAst; use crate::macros::Storage; use crate::parse::Resolve; -use crate::query::{Build, BuildEntry, GenericsParameters, Query, Used}; +use crate::query::{Build, BuildEntry, GenericsParameters, Query, SecondaryBuild, Used}; use crate::runtime::unit::UnitEncoder; use crate::shared::{Consts, Gen}; use crate::worker::{LoadFileKind, Task, Worker}; +use crate::{alloc, ast}; use crate::{Diagnostics, Sources}; /// Encode the given object into a collection of asm. @@ -149,12 +148,12 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { entry: BuildEntry, unit_storage: &mut dyn UnitEncoder, ) -> compile::Result<()> { + use self::v1::assemble; + let BuildEntry { item_meta, build } = entry; let location = item_meta.location; - let mut asm = self.q.unit.new_assembly(location); - match build { Build::Query => { tracing::trace!("query: {}", self.q.pool.item(item_meta.item)); @@ -179,9 +178,9 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { } } Build::Function(f) => { - tracing::trace!("function: {}", self.q.pool.item(item_meta.item)); + let mut asm = self.q.unit.new_assembly(location); - use self::v1::assemble; + tracing::trace!("function: {}", self.q.pool.item(item_meta.item)); // For instance functions, we are required to know the type hash // of the type it is associated with to perform the proper @@ -217,7 +216,7 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { let (debug_args, span): (_, &dyn Spanned) = match &f.ast { FunctionAst::Item(ast) => { - let debug_args = format_fn_args( + let debug_args = format_ast_args( self.q.sources, location, false, @@ -229,11 +228,13 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { }; let arena = hir::Arena::new(); + let mut secondary_builds = Vec::new(); let mut cx = hir::lowering::Ctxt::with_query( &arena, self.q.borrow(), item_meta.location.source_id, + &mut secondary_builds, )?; let hir = match &f.ast { @@ -276,101 +277,86 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { size, )?; } - } - Build::Closure(closure) => { - tracing::trace!("closure: {}", self.q.pool.item(item_meta.item)); - - use self::v1::assemble; - - let debug_args = format_fn_args( - self.q.sources, - location, - true, - 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, captures)?; - let mut scopes = self::v1::Scopes::new(location.source_id)?; - let mut c = self.compiler1(location, &closure.ast, &mut asm, &mut scopes)?; - assemble::expr_closure_secondary(&mut c, &hir, &closure.ast)?; - let size = c.scopes.size(); - - if !c.q.is_used(&item_meta) { - c.q.diagnostics - .not_used(location.source_id, &location.span, None)?; - } else { - let captures = - c.q.get_captures(captures) - .map(|c| c.len()) - .filter(|c| *c > 0); - - let args = closure - .ast - .args - .len() - .saturating_add(usize::from(captures.is_some())); - - self.q.unit.new_function( - location, - self.q.pool.item(item_meta.item), - None, - args, - captures, - asm, - closure.call, - debug_args, - unit_storage, - size, - )?; - } - } - Build::AsyncBlock(b) => { - tracing::trace!("async block: {}", self.q.pool.item(item_meta.item)); - - use self::v1::assemble; - - 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, captures)?; - let mut scopes = self::v1::Scopes::new(location.source_id)?; - let mut c = self.compiler1(location, &b.ast, &mut asm, &mut scopes)?; - assemble::async_block_secondary(&mut c, &hir)?; - let size = c.scopes.size(); - - if !self.q.is_used(&item_meta) { - self.q - .diagnostics - .not_used(location.source_id, &location.span, None)?; - } else { - let args = hir.captures.len(); - self.q.unit.new_function( - location, - self.q.pool.item(item_meta.item), - None, - args, - None, - asm, - b.call, - Default::default(), - unit_storage, - size, - )?; + for build in secondary_builds { + let item_meta = build.item_meta; + + let mut asm = self.q.unit.new_assembly(item_meta.location); + + match build.build { + SecondaryBuild::Closure(c) => { + tracing::trace!("closure: {}", self.q.pool.item(item_meta.item)); + + let debug_args = + format_hir_args(self.q.sources, location, true, c.hir.args.iter())?; + + let mut scopes = self::v1::Scopes::new(location.source_id)?; + let mut cx = self.compiler1(location, c.hir, &mut asm, &mut scopes)?; + assemble::expr_closure_secondary(&mut cx, c.hir)?; + let size = cx.scopes.size(); + + if !self.q.is_used(&item_meta) { + self.q.diagnostics.not_used( + location.source_id, + &location.span, + None, + )?; + } else { + let captures = + (!c.hir.captures.is_empty()).then_some(c.hir.captures.len()); + + let args = c + .hir + .args + .len() + .saturating_add(usize::from(captures.is_some())); + + self.q.unit.new_function( + location, + self.q.pool.item(item_meta.item), + None, + args, + captures, + asm, + c.call, + debug_args, + unit_storage, + size, + )?; + } + } + SecondaryBuild::AsyncBlock(b) => { + tracing::trace!("async block: {}", self.q.pool.item(item_meta.item)); + + let mut scopes = self::v1::Scopes::new(location.source_id)?; + let mut cx = self.compiler1(location, b.hir, &mut asm, &mut scopes)?; + assemble::async_block_secondary(&mut cx, b.hir)?; + let size = cx.scopes.size(); + + if !self.q.is_used(&item_meta) { + self.q.diagnostics.not_used( + location.source_id, + &location.span, + None, + )?; + } else { + let args = b.hir.captures.len(); + + self.q.unit.new_function( + location, + self.q.pool.item(item_meta.item), + None, + args, + None, + asm, + b.call, + Default::default(), + unit_storage, + size, + )?; + } + } + } } } Build::Unused => { @@ -457,21 +443,52 @@ impl<'arena> CompileBuildEntry<'_, 'arena> { } } -fn format_fn_args<'a, I>( +fn format_hir_args<'hir, I>( sources: &Sources, location: Location, environment: bool, arguments: I, ) -> compile::Result]>> where - I: IntoIterator, + I: IntoIterator>, { let mut args = Vec::new(); + for arg in arguments { + match arg { + hir::FnArg::SelfValue(..) => { + args.try_push(Box::try_from("self")?)?; + } + hir::FnArg::Pat(pat) => { + let span = pat.span(); + + if let Some(s) = sources.source(location.source_id, span) { + args.try_push(Box::try_from(s)?)?; + } else { + args.try_push(Box::try_from("*")?)?; + } + } + } + } + if environment { args.try_push(Box::try_from("environment")?)?; } + Ok(args.try_into_boxed_slice()?) +} + +fn format_ast_args<'a, I>( + sources: &Sources, + location: Location, + environment: bool, + arguments: I, +) -> compile::Result]>> +where + I: IntoIterator, +{ + let mut args = Vec::new(); + for arg in arguments { match arg { ast::FnArg::SelfValue(..) => { @@ -489,5 +506,9 @@ where } } + if environment { + args.try_push(Box::try_from("environment")?)?; + } + Ok(args.try_into_boxed_slice()?) } diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index 1511692e2..d9ba9a22e 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -512,6 +512,8 @@ pub(crate) enum ErrorKind { UnsupportedPatternRest, UnsupportedMut, UnsupportedSuffix, + ClosureInConst, + AsyncBlockInConst, } impl ErrorKind { @@ -1040,6 +1042,12 @@ impl fmt::Display for ErrorKind { "Unsupported suffix, expected one of `u8`, `i64`, or `f64`" )?; } + ErrorKind::ClosureInConst => { + write!(f, "Closures are not supported in constant contexts")?; + } + ErrorKind::AsyncBlockInConst => { + write!(f, "Async blocks are not supported in constant contexts")?; + } } Ok(()) @@ -1230,11 +1238,6 @@ pub(crate) enum IrErrorKind { /// The field that was missing. field: Box, }, - /// Missing const or local with the given name. - MissingConst { - /// Name of the missing thing. - name: Box, - }, /// Error raised when trying to use a break outside of a loop. BreakOutsideOfLoop, ArgumentCountMismatch { @@ -1271,9 +1274,6 @@ impl fmt::Display for IrErrorKind { IrErrorKind::MissingField { field } => { write!(f, "Missing field `{field}`",)?; } - IrErrorKind::MissingConst { name } => { - write!(f, "No constant or local matching `{name}`",)?; - } IrErrorKind::BreakOutsideOfLoop => { write!(f, "Break outside of supported loop")?; } diff --git a/crates/rune/src/compile/ir.rs b/crates/rune/src/compile/ir.rs index 30424d8ad..d189bbb21 100644 --- a/crates/rune/src/compile/ir.rs +++ b/crates/rune/src/compile/ir.rs @@ -121,7 +121,7 @@ pub(crate) struct IrTarget { #[derive(Debug, TryClone)] pub(crate) enum IrTargetKind { /// A variable. - Name(hir::OwnedName), + Name(hir::Variable), /// A field target. Field(Box, Box), /// An index target. @@ -145,7 +145,7 @@ decl_kind! { /// A template. Template(IrTemplate), /// A named value. - Name(hir::OwnedName), + Name(hir::Variable), /// A local name. Could either be a local variable or a reference to /// something else, like another const declaration. Target(IrTarget), @@ -175,7 +175,7 @@ pub(crate) struct IrFn { #[rune(span)] pub(crate) span: Span, /// The number of arguments the function takes and their names. - pub(crate) args: Vec, + pub(crate) args: Vec, /// The scope for the function. pub(crate) ir: Ir, } @@ -197,7 +197,7 @@ impl IrFn { .. }) = arg { - args.try_push(hir::Name::Str(name).into_owned()?)?; + args.try_push(name)?; continue; } @@ -247,7 +247,7 @@ pub(crate) struct IrDecl { #[rune(span)] pub(crate) span: Span, /// The name of the variable. - pub(crate) name: hir::OwnedName, + pub(crate) name: hir::Variable, /// The value of the variable. pub(crate) value: Box, } @@ -336,7 +336,7 @@ pub(crate) enum IrPat { /// An ignore pattern `_`. Ignore, /// A named binding. - Binding(hir::OwnedName), + Binding(hir::Variable), } impl IrPat { @@ -344,7 +344,7 @@ impl IrPat { match hir.kind { hir::PatKind::Ignore => return Ok(ir::IrPat::Ignore), hir::PatKind::Path(&hir::PatPathKind::Ident(name)) => { - return Ok(ir::IrPat::Binding(hir::Name::Str(name).into_owned()?)); + return Ok(ir::IrPat::Binding(name)); } _ => (), } @@ -364,7 +364,7 @@ impl IrPat { match self { IrPat::Ignore => Ok(true), IrPat::Binding(name) => { - interp.scopes.decl(name, value).with_span(spanned)?; + interp.scopes.decl(*name, value).with_span(spanned)?; Ok(true) } } diff --git a/crates/rune/src/compile/ir/compiler.rs b/crates/rune/src/compile/ir/compiler.rs index 8984ef8e7..b726d5834 100644 --- a/crates/rune/src/compile/ir/compiler.rs +++ b/crates/rune/src/compile/ir/compiler.rs @@ -54,7 +54,7 @@ pub(crate) fn expr(hir: &hir::Expr<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result ir::Ir::new(span, value) } hir::ExprKind::Variable(name) => { - return Ok(ir::Ir::new(span, name.into_owned()?)); + return Ok(ir::Ir::new(span, name)); } _ => { return Err(compile::Error::msg( @@ -71,7 +71,7 @@ fn ir_target(expr: &hir::Expr<'_>) -> compile::Result { hir::ExprKind::Variable(name) => { return Ok(ir::IrTarget { span: expr.span(), - kind: ir::IrTargetKind::Name(name.into_owned()?), + kind: ir::IrTargetKind::Name(name), }); } hir::ExprKind::FieldAccess(expr_field_access) => { @@ -370,7 +370,7 @@ fn local(hir: &hir::Local<'_>, c: &mut Ctxt<'_, '_>) -> compile::Result span, ir::IrDecl { span, - name: hir::Name::Str(name).into_owned()?, + name, value: Box::try_new(expr(&hir.expr, c)?)?, }, )) diff --git a/crates/rune/src/compile/ir/eval.rs b/crates/rune/src/compile/ir/eval.rs index 07510d8b1..aea2bfc0a 100644 --- a/crates/rune/src/compile/ir/eval.rs +++ b/crates/rune/src/compile/ir/eval.rs @@ -228,7 +228,7 @@ fn eval_ir_decl( ) -> Result { interp.budget.take(ir)?; let value = eval_ir(&ir.value, interp, used)?; - interp.scopes.decl(&ir.name, value).with_span(ir)?; + interp.scopes.decl(ir.name, value).with_span(ir)?; Ok(Value::unit()) } diff --git a/crates/rune/src/compile/ir/interpreter.rs b/crates/rune/src/compile/ir/interpreter.rs index 4a3b8967b..a7c8cece9 100644 --- a/crates/rune/src/compile/ir/interpreter.rs +++ b/crates/rune/src/compile/ir/interpreter.rs @@ -90,7 +90,7 @@ impl Interpreter<'_, '_> { pub(crate) fn resolve_var( &mut self, span: &dyn Spanned, - name: &hir::OwnedName, + name: &hir::Variable, used: Used, ) -> compile::Result { if let Some(ir_value) = self.scopes.try_get(name) { @@ -139,19 +139,10 @@ impl Interpreter<'_, '_> { base.pop()?; } - if name.as_ref().starts_with(char::is_lowercase) { - Err(compile::Error::new( - span, - MissingLocal(name.try_to_string()?.try_into_boxed_str()?), - )) - } else { - Err(compile::Error::new( - span, - IrErrorKind::MissingConst { - name: name.try_to_string()?.try_into()?, - }, - )) - } + Err(compile::Error::new( + span, + MissingLocal(name.try_to_string()?.try_into_boxed_str()?), + )) } pub(crate) fn call_const_fn( @@ -180,7 +171,7 @@ impl Interpreter<'_, '_> { let guard = self.scopes.isolate()?; for (name, value) in const_fn.ir_fn.args.iter().zip(args) { - self.scopes.decl(name, value).with_span(span)?; + self.scopes.decl(*name, value).with_span(span)?; } let value = self.eval_value(&const_fn.ir_fn.ir, used)?; diff --git a/crates/rune/src/compile/ir/scopes.rs b/crates/rune/src/compile/ir/scopes.rs index 9da2e8a01..2ca082729 100644 --- a/crates/rune/src/compile/ir/scopes.rs +++ b/crates/rune/src/compile/ir/scopes.rs @@ -33,16 +33,16 @@ impl Scopes { } /// Declare a value in the scope. - pub(crate) fn decl(&mut self, name: &hir::OwnedName, value: Value) -> Result<(), ErrorKind> { + pub(crate) fn decl(&mut self, name: hir::Variable, value: Value) -> Result<(), ErrorKind> { let last = self .last_mut() .ok_or_else(|| ErrorKind::msg("Expected at least one scope"))?; - last.locals.try_insert(name.try_clone()?, value)?; + last.locals.try_insert(name, value)?; Ok(()) } /// Try to get the value out from the scopes. - pub(crate) fn try_get(&self, name: &hir::OwnedName) -> Option<&Value> { + pub(crate) fn try_get(&self, name: &hir::Variable) -> Option<&Value> { for scope in self.scopes.iter().rev() { if let Some(current) = scope.locals.get(name) { return Some(current); @@ -60,7 +60,7 @@ impl Scopes { /// Get the given variable. pub(crate) fn get_name( &self, - name: &hir::OwnedName, + name: &hir::Variable, span: &dyn Spanned, ) -> compile::Result<&Value> { for scope in self.scopes.iter().rev() { @@ -83,7 +83,7 @@ impl Scopes { /// Get the given variable as mutable. pub(crate) fn get_name_mut( &mut self, - name: &hir::OwnedName, + name: &hir::Variable, span: &dyn Spanned, ) -> compile::Result<&mut Value> { for scope in self.scopes.iter_mut().rev() { @@ -153,7 +153,7 @@ enum ScopeKind { pub(crate) struct Scope { kind: ScopeKind, /// Locals in the current scope. - locals: HashMap, + locals: HashMap, } impl Default for Scope { diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index 39ee90384..fcfad9e4e 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -128,7 +128,7 @@ impl<'a, 'hir, 'arena> Ctxt<'a, 'hir, 'arena> { for (ir, name) in compiled { let value = interpreter.eval_value(&ir, Used::Used)?; - interpreter.scopes.decl(name, value).with_span(span)?; + interpreter.scopes.decl(*name, value).with_span(span)?; } interpreter.module = query_const_fn.item_meta.module; @@ -215,12 +215,12 @@ pub(crate) fn fn_from_item_fn<'a, 'hir>( for (arg, needs) in hir.args.iter().zip(&mut arguments) { match arg { - hir::FnArg::SelfValue(span) => { + hir::FnArg::SelfValue(span, name) => { if !instance_fn || !first { return Err(compile::Error::new(span, ErrorKind::UnsupportedSelf)); } - cx.scopes.define(span, hir::Name::SelfValue, needs)?; + cx.scopes.define(span, *name, needs)?; } hir::FnArg::Pat(pat) => { let asm = pattern_panic(cx, pat, move |cx, false_label| { @@ -261,7 +261,7 @@ pub(crate) fn async_block_secondary<'a, 'hir>( cx.scopes.define(&hir.block, name, needs)?; } - return_(cx, &hir.block, &hir.block, block_without_scope)?.ignore(); + return_(cx, &hir.block, hir.block, block_without_scope)?.ignore(); linear.free()?; cx.scopes.pop_last(&hir.block)?; @@ -269,14 +269,13 @@ pub(crate) fn async_block_secondary<'a, 'hir>( } /// Assemble the body of a closure function. -#[instrument(span = span)] +#[instrument(span = hir)] pub(crate) fn expr_closure_secondary<'a, 'hir>( cx: &mut Ctxt<'a, 'hir, '_>, hir: &'hir hir::ExprClosure<'hir>, - span: &'hir dyn Spanned, ) -> compile::Result<()> { - let mut arguments = cx.scopes.linear(span, hir.args.len())?; - let environment = cx.scopes.linear(span, hir.captures.len())?; + let mut arguments = cx.scopes.linear(hir, hir.args.len())?; + let environment = cx.scopes.linear(hir, hir.captures.len())?; if !hir.captures.is_empty() { cx.asm.push( @@ -285,17 +284,17 @@ pub(crate) fn expr_closure_secondary<'a, 'hir>( count: hir.captures.len(), out: environment.addr().output(), }, - span, + hir, )?; for (capture, needs) in hir.captures.iter().copied().zip(&environment) { - cx.scopes.define(span, capture, needs)?; + cx.scopes.define(hir, capture, needs)?; } } for (arg, needs) in hir.args.iter().zip(&mut arguments) { match arg { - hir::FnArg::SelfValue(span) => { + hir::FnArg::SelfValue(span, _) => { return Err(compile::Error::new(span, ErrorKind::UnsupportedSelf)) } hir::FnArg::Pat(pat) => { @@ -308,11 +307,11 @@ pub(crate) fn expr_closure_secondary<'a, 'hir>( } } - return_(cx, span, &hir.body, expr)?.ignore(); + return_(cx, hir, hir.body, expr)?.ignore(); environment.free()?; arguments.free()?; - cx.scopes.pop_last(span)?; + cx.scopes.pop_last(hir)?; Ok(()) } @@ -421,7 +420,7 @@ fn pat_binding_with<'a, 'hir>( cx: &mut Ctxt<'a, 'hir, '_>, span: &'hir dyn Spanned, pat: &'hir hir::Pat<'hir>, - names: &[hir::Name<'hir>], + names: &[hir::Variable], false_label: &Label, load: &mut dyn FnMut( &mut Ctxt<'a, 'hir, '_>, @@ -456,7 +455,7 @@ fn pat_binding_with_single<'a, 'hir>( cx: &mut Ctxt<'a, 'hir, '_>, span: &'hir dyn Spanned, pat: &'hir hir::Pat<'hir>, - name: hir::Name<'hir>, + name: hir::Variable, false_label: &Label, load: &mut dyn FnMut( &mut Ctxt<'a, 'hir, '_>, @@ -529,7 +528,7 @@ fn pat<'a, 'hir>( &mut Ctxt<'a, 'hir, '_>, &mut dyn Needs<'a, 'hir>, ) -> compile::Result>, - bindings: &mut dyn Bindings, &mut dyn Needs<'a, 'hir>>, + bindings: &mut dyn Bindings>, ) -> compile::Result> { let span = hir; @@ -555,8 +554,6 @@ fn pat<'a, 'hir>( Ok(Asm::new(span, Pattern::Refutable)) } hir::PatPathKind::Ident(name) => { - let name = hir::Name::Str(name); - let Some(binding) = bindings.remove(&name) else { return Err(compile::Error::msg(hir, format!("No binding for {name:?}"))); }; @@ -697,7 +694,7 @@ fn pat_sequence<'a, 'hir>( &mut Ctxt<'a, 'hir, '_>, &mut dyn Needs<'a, 'hir>, ) -> compile::Result>, - bindings: &mut dyn Bindings, &mut dyn Needs<'a, 'hir>>, + bindings: &mut dyn Bindings>, ) -> compile::Result> { let mut addr = cx.scopes.defer(span).with_name("loaded pattern sequence"); converge!(load(cx, &mut addr)?, free(addr)); @@ -796,7 +793,7 @@ fn pat_object<'a, 'hir>( &mut Ctxt<'a, 'hir, '_>, &mut dyn Needs<'a, 'hir>, ) -> compile::Result>, - bindings: &mut dyn Bindings, &mut dyn Needs<'a, 'hir>>, + bindings: &mut dyn Bindings>, ) -> compile::Result> { let mut needs = cx.scopes.defer(span); converge!(load(cx, &mut needs)?, free(needs)); @@ -873,10 +870,8 @@ fn pat_object<'a, 'hir>( free(needs) ); } - hir::Binding::Ident(span, name) => { - let name = hir::Name::Str(name); - - let Some(binding) = bindings.remove(&name) else { + hir::Binding::Ident(span, name, id) => { + let Some(binding) = bindings.remove(id) else { return Err(compile::Error::msg( binding, format!("No binding for {name:?}"), diff --git a/crates/rune/src/compile/v1/scopes.rs b/crates/rune/src/compile/v1/scopes.rs index a6dd1840b..e4d3a595c 100644 --- a/crates/rune/src/compile/v1/scopes.rs +++ b/crates/rune/src/compile/v1/scopes.rs @@ -22,7 +22,7 @@ pub(super) struct Scope<'hir> { /// Scope. id: ScopeId, /// Named variables. - names: HashMap, VarInner<'hir>>, + names: HashMap>, /// Slots owned by this scope. locals: Dangling, } @@ -189,7 +189,7 @@ impl<'hir> Scopes<'hir> { &self, q: &mut Query<'_, '_>, span: &dyn Spanned, - name: hir::Name<'hir>, + name: hir::Variable, ) -> compile::Result> { let scopes = self.scopes.borrow(); let mut current = Some(self.top.get()); @@ -241,7 +241,7 @@ impl<'hir> Scopes<'hir> { &self, q: &mut Query<'_, '_>, span: &'hir dyn Spanned, - name: hir::Name<'hir>, + name: hir::Variable, ) -> compile::Result> { let scopes = self.scopes.borrow(); let mut current = Some(self.top.get()); @@ -294,7 +294,7 @@ impl<'hir> Scopes<'hir> { pub(super) fn define( &self, span: &'hir dyn Spanned, - name: hir::Name<'hir>, + name: hir::Variable, addr: &Address<'_, 'hir>, ) -> compile::Result<()> { let mut scopes = self.scopes.borrow_mut(); @@ -585,7 +585,7 @@ pub(super) struct Var<'hir> { /// The span where the variable was declared. pub(super) span: &'hir dyn Spanned, /// The name of the variable. - name: hir::Name<'hir>, + name: hir::Variable, /// Address where the variable is currently live. pub(super) addr: InstAddress, } @@ -669,7 +669,7 @@ struct VarInner<'hir> { /// Token assocaited with the variable. span: &'hir dyn Spanned, /// The name of the variable. - name: hir::Name<'hir>, + name: hir::Variable, /// Offset from the current stack frame. addr: InstAddress, /// Variable has been taken at the given position. diff --git a/crates/rune/src/hir/hir.rs b/crates/rune/src/hir/hir.rs index f03f9d7e7..f7cb9b91d 100644 --- a/crates/rune/src/hir/hir.rs +++ b/crates/rune/src/hir/hir.rs @@ -3,39 +3,28 @@ use core::num::NonZeroUsize; use crate as rune; use crate::alloc::prelude::*; -use crate::alloc::{self, String}; use crate::ast::{self, Span, Spanned}; use crate::compile::{ItemId, ModId}; use crate::parse::NonZeroId; use crate::runtime::{format, Type, TypeCheck}; use crate::Hash; -/// An owned name. -#[derive(Debug, TryClone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) enum OwnedName { - SelfValue, - Str(String), - Id(usize), -} +#[derive(TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[try_clone(copy)] +#[repr(transparent)] +pub(crate) struct Variable(#[try_clone(copy)] pub(crate) NonZeroId); -impl OwnedName { - /// Get name as reference. - pub(crate) fn as_ref(&self) -> Name<'_> { - match self { - OwnedName::SelfValue => Name::SelfValue, - OwnedName::Str(name) => Name::Str(name), - OwnedName::Id(id) => Name::Id(*id), - } +impl fmt::Display for Variable { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) } } -impl fmt::Display for OwnedName { +impl fmt::Debug for Variable { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OwnedName::SelfValue => "self".fmt(f), - OwnedName::Str(name) => name.fmt(f), - OwnedName::Id(id) => id.fmt(f), - } + self.0.fmt(f) } } @@ -47,28 +36,6 @@ pub(crate) enum Name<'hir> { SelfValue, /// Capture of a named variable. Str(&'hir str), - /// Anonymous variable. - Id(usize), -} - -impl<'hir> Name<'hir> { - /// Coerce into an owned name. - pub(crate) fn into_owned(self) -> alloc::Result { - Ok(match self { - Name::SelfValue => OwnedName::SelfValue, - Name::Str(name) => OwnedName::Str(name.try_to_owned()?), - Name::Id(id) => OwnedName::Id(id), - }) - } - - /// Test if the name starts with the given test. - pub(crate) fn starts_with(&self, test: fn(char) -> bool) -> bool { - let Name::Str(name) = self else { - return false; - }; - - name.starts_with(test) - } } impl fmt::Display for Name<'_> { @@ -76,7 +43,6 @@ impl fmt::Display for Name<'_> { match self { Name::SelfValue => write!(f, "self"), Name::Str(name) => name.fmt(f), - Name::Id(id) => id.fmt(f), } } } @@ -86,7 +52,6 @@ impl fmt::Debug for Name<'_> { match self { Name::SelfValue => write!(f, "self"), Name::Str(name) => name.fmt(f), - Name::Id(id) => id.fmt(f), } } } @@ -112,14 +77,14 @@ pub(crate) struct PatBinding<'hir> { #[rune(span)] pub(crate) pat: Pat<'hir>, /// Names that will be defined by this pattern. - pub(crate) names: &'hir [Name<'hir>], + pub(crate) names: &'hir [Variable], } #[derive(Debug, TryClone, Clone, Copy)] #[try_clone(copy)] pub(crate) enum PatPathKind<'hir> { Kind(&'hir PatSequenceKind), - Ident(&'hir str), + Ident(Variable), } /// The kind of a [Pat]. @@ -186,14 +151,14 @@ pub(crate) struct PatObject<'hir> { #[non_exhaustive] pub(crate) enum Binding<'hir> { Binding(Span, &'hir str, &'hir Pat<'hir>), - Ident(Span, &'hir str), + Ident(Span, &'hir str, Variable), } impl<'hir> Spanned for Binding<'hir> { fn span(&self) -> Span { match self { Binding::Binding(span, _, _) => *span, - Binding::Ident(span, _) => *span, + Binding::Ident(span, _, _) => *span, } } } @@ -202,7 +167,7 @@ impl<'hir> Binding<'hir> { pub(crate) fn key(&self) -> &'hir str { match *self { Self::Binding(_, key, _) => key, - Self::Ident(_, key) => key, + Self::Ident(_, key, _) => key, } } } @@ -238,7 +203,7 @@ pub(crate) enum Lit<'hir> { #[try_clone(copy)] #[non_exhaustive] pub(crate) enum ExprKind<'hir> { - Variable(Name<'hir>), + Variable(Variable), Type(Type), Fn(Hash), Path, @@ -333,7 +298,7 @@ pub(crate) struct ExprLoop<'hir> { pub(crate) body: Block<'hir>, /// Variables that have been defined by the loop header. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } /// A `for` loop over an iterator: `for i in [1, 2, 3] {}`. @@ -352,7 +317,7 @@ pub(crate) struct ExprFor<'hir> { pub(crate) body: Block<'hir>, /// Variables that have been defined by the loop header. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } /// A let expression `let = ` @@ -398,7 +363,7 @@ pub(crate) struct ConditionalBranch<'hir> { pub(crate) block: Block<'hir>, /// Variables that have been defined by the conditional header. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } /// A match expression. @@ -429,7 +394,7 @@ pub(crate) struct ExprMatchBranch<'hir> { /// Variables that have been defined by this match branch, which needs to be /// dropped. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } #[derive(Debug, TryClone, Clone, Copy)] @@ -437,7 +402,7 @@ pub(crate) struct ExprMatchBranch<'hir> { pub(crate) enum Call<'hir> { Var { /// The name of the variable being called. - name: Name<'hir>, + name: Variable, }, Associated { /// The target expression being called. @@ -551,7 +516,7 @@ pub(crate) struct ExprIndex<'hir> { pub(crate) struct ExprAsyncBlock<'hir> { pub(crate) hash: Hash, pub(crate) do_move: bool, - pub(crate) captures: &'hir [Name<'hir>], + pub(crate) captures: &'hir [Variable], } #[derive(Debug, TryClone, Clone, Copy)] @@ -563,7 +528,7 @@ pub(crate) struct ExprBreak<'hir> { pub(crate) expr: Option<&'hir Expr<'hir>>, /// Variables that goes out of scope. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } #[derive(Debug, TryClone, Clone, Copy)] @@ -573,7 +538,7 @@ pub(crate) struct ExprContinue<'hir> { pub(crate) label: Option<&'hir str>, /// Variables that goes out of scope. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } /// A `select` expression that selects over a collection of futures. @@ -600,7 +565,7 @@ pub(crate) struct ExprSelectBranch<'hir> { pub(crate) body: Expr<'hir>, /// Variables that need to be dropped by the end of this block. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } /// Calling a closure. @@ -610,20 +575,21 @@ pub(crate) struct ExprSelectBranch<'hir> { pub(crate) struct ExprCallClosure<'hir> { pub(crate) do_move: bool, pub(crate) hash: Hash, - pub(crate) captures: &'hir [Name<'hir>], + pub(crate) captures: &'hir [Variable], } /// A closure expression. -#[derive(Debug, TryClone, Clone, Copy)] +#[derive(Debug, TryClone, Clone, Copy, Spanned)] #[try_clone(copy)] #[non_exhaustive] pub(crate) struct ExprClosure<'hir> { /// Arguments to the closure. pub(crate) args: &'hir [FnArg<'hir>], /// The body of the closure. - pub(crate) body: Expr<'hir>, + #[rune(span)] + pub(crate) body: &'hir Expr<'hir>, /// Captures in the closure. - pub(crate) captures: &'hir [Name<'hir>], + pub(crate) captures: &'hir [Variable], } #[derive(Debug, TryClone, Clone, Copy)] @@ -728,7 +694,7 @@ pub(crate) struct ItemFn<'hir> { #[non_exhaustive] pub(crate) enum FnArg<'hir> { /// The `self` parameter. - SelfValue(Span), + SelfValue(#[rune(span)] Span, Variable), /// Function argument is a pattern binding. Pat(&'hir PatBinding<'hir>), } @@ -749,14 +715,15 @@ pub(crate) struct Block<'hir> { pub(crate) value: Option<&'hir Expr<'hir>>, /// Variables that need to be dropped by the end of this block. #[allow(unused)] - pub(crate) drop: &'hir [Name<'hir>], + pub(crate) drop: &'hir [Variable], } -#[derive(Debug, TryClone, Clone, Copy)] +#[derive(Debug, TryClone, Clone, Copy, Spanned)] #[try_clone(copy)] pub(crate) struct AsyncBlock<'hir> { - pub(crate) block: Block<'hir>, - pub(crate) captures: &'hir [Name<'hir>], + #[rune(span)] + pub(crate) block: &'hir Block<'hir>, + pub(crate) captures: &'hir [Variable], } /// A statement within a block. diff --git a/crates/rune/src/hir/lowering.rs b/crates/rune/src/hir/lowering.rs index 0eef1adf4..c0d6c34ea 100644 --- a/crates/rune/src/hir/lowering.rs +++ b/crates/rune/src/hir/lowering.rs @@ -11,9 +11,11 @@ use crate::compile::meta; use crate::compile::{self, DynLocation, ErrorKind, ItemId, WithSpan}; use crate::hash::ParametersBuilder; use crate::hir; -use crate::indexing; use crate::parse::Resolve; -use crate::query::{self, Build, BuildEntry, GenericsParameters, Named, Query}; +use crate::query::AsyncBlock; +use crate::query::Closure; +use crate::query::SecondaryBuildEntry; +use crate::query::{self, GenericsParameters, Named, Query, SecondaryBuild}; use crate::runtime::ConstValue; use crate::runtime::{Type, TypeCheck}; use crate::{Hash, Item, SourceId}; @@ -35,10 +37,11 @@ pub(crate) struct Ctxt<'hir, 'a, 'arena> { in_template: Cell, in_path: Cell, needs: Cell, - scopes: hir::Scopes<'hir>, + scopes: hir::Scopes<'hir, 'a>, const_eval: bool, statements: Vec>, - pattern_bindings: Vec>, + pattern_bindings: Vec, + secondary_builds: Option<&'a mut Vec>>, } impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { @@ -59,8 +62,9 @@ impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { arena: &'hir hir::arena::Arena, q: Query<'a, 'arena>, source_id: SourceId, + secondary_builds: &'a mut Vec>, ) -> alloc::Result { - Self::inner(arena, q, source_id, false) + Self::inner(arena, q, source_id, false, Some(secondary_builds)) } /// Construct a new context used in a constant context where the resulting @@ -70,7 +74,7 @@ impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { q: Query<'a, 'arena>, source_id: SourceId, ) -> alloc::Result { - Self::inner(arena, q, source_id, true) + Self::inner(arena, q, source_id, true, None) } fn inner( @@ -78,7 +82,10 @@ impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { q: Query<'a, 'arena>, source_id: SourceId, const_eval: bool, + secondary_builds: Option<&'a mut Vec>>, ) -> alloc::Result { + let scopes = hir::Scopes::new(q.gen)?; + Ok(Self { arena, q, @@ -86,16 +93,17 @@ impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { in_template: Cell::new(false), in_path: Cell::new(false), needs: Cell::new(Needs::default()), - scopes: hir::Scopes::new()?, + scopes, const_eval, statements: Vec::new(), pattern_bindings: Vec::new(), + secondary_builds, }) } #[allow(unused)] #[instrument(span = ast)] - pub(crate) fn try_lookup_meta( + fn try_lookup_meta( &mut self, span: &dyn Spanned, item: ItemId, @@ -106,7 +114,7 @@ impl<'hir, 'a, 'arena> Ctxt<'hir, 'a, 'arena> { } #[instrument(span = ast)] - pub(crate) fn lookup_meta( + fn lookup_meta( &mut self, span: &dyn Spanned, item: ItemId, @@ -146,82 +154,6 @@ pub(crate) fn item_fn<'hir>( }) } -/// Lower the body of an async block. -/// -/// This happens *after* it's been lowered as part of a closure expression. -#[instrument(span = ast)] -pub(crate) fn async_block_secondary<'hir>( - cx: &mut Ctxt<'hir, '_, '_>, - ast: &ast::Block, - captures: Hash, -) -> compile::Result> { - alloc_with!(cx, ast); - - let Some(captures) = cx.q.get_captures(captures) else { - return Err(compile::Error::msg( - ast, - try_format!("Missing captures for hash {captures}"), - )); - }; - - let captures = &*iter!(captures, |capture| { - match capture { - hir::OwnedName::SelfValue => cx.scopes.define(hir::Name::SelfValue, ast)?, - hir::OwnedName::Str(name) => { - let name = alloc_str!(name.as_str()); - cx.scopes.define(hir::Name::Str(name), ast)? - } - hir::OwnedName::Id(id) => cx.scopes.define(hir::Name::Id(*id), ast)?, - } - }); - - Ok(hir::AsyncBlock { - block: block(cx, None, ast)?, - captures, - }) -} - -/// Lower the body of a closure. -/// -/// This happens *after* it's been lowered as part of a closure expression. -#[instrument(span = ast)] -pub(crate) fn expr_closure_secondary<'hir>( - cx: &mut Ctxt<'hir, '_, '_>, - ast: &ast::ExprClosure, - captures: Hash, -) -> compile::Result> { - alloc_with!(cx, ast); - - let Some(captures) = cx.q.get_captures(captures) else { - return Err(compile::Error::msg( - ast, - try_format!("Missing captures for hash {captures}"), - )); - }; - - let captures = &*iter!(captures, |capture| match capture { - hir::OwnedName::SelfValue => { - cx.scopes.define(hir::Name::SelfValue, ast)? - } - hir::OwnedName::Str(name) => { - let name = hir::Name::Str(alloc_str!(name.as_str())); - cx.scopes.define(name, ast)? - } - hir::OwnedName::Id(id) => { - cx.scopes.define(hir::Name::Id(*id), ast)? - } - }); - - let args = iter!(ast.args.as_slice(), |(ast, _)| fn_arg(cx, ast)?); - let body = expr(cx, &ast.body)?; - - Ok(hir::ExprClosure { - args, - body, - captures, - }) -} - /// Assemble a closure expression. #[instrument(span = ast)] fn expr_call_closure<'hir>( @@ -249,40 +181,35 @@ fn expr_call_closure<'hir>( )); }; - let captures = match cx.q.get_captures(meta.hash) { - None => { - tracing::trace!("queuing closure build entry"); + tracing::trace!("queuing closure build entry"); - cx.scopes.push_captures()?; + cx.scopes.push_captures()?; - for (arg, _) in ast.args.as_slice() { - fn_arg(cx, arg)?; - } + let args = iter!(ast.args.as_slice(), |(arg, _)| fn_arg(cx, arg)?); + let body = alloc!(expr(cx, &ast.body)?); - expr(cx, &ast.body)?; - let layer = cx.scopes.pop().with_span(&ast.body)?; + let layer = cx.scopes.pop().with_span(&ast.body)?; - cx.q.set_used(&meta.item_meta)?; - cx.q.inner.queue.try_push_back(BuildEntry { - item_meta: meta.item_meta, - build: Build::Closure(indexing::Closure { - ast: Box::try_new(ast.try_clone()?)?, - call, - }), - })?; + cx.q.set_used(&meta.item_meta)?; - cx.q.insert_captures(meta.hash, layer.captures())?; - iter!(layer.captures()) - } - Some(captures) => { - iter!(captures, |capture| match capture { - hir::OwnedName::SelfValue => hir::Name::SelfValue, - hir::OwnedName::Str(name) => hir::Name::Str(alloc_str!(name.as_str())), - hir::OwnedName::Id(id) => hir::Name::Id(*id), - }) - } + let captures = &*iter!(layer.captures().map(|(_, id)| id)); + + let Some(queue) = cx.secondary_builds.as_mut() else { + return Err(compile::Error::new(ast, ErrorKind::ClosureInConst)); }; + queue.try_push(SecondaryBuildEntry { + item_meta: meta.item_meta, + build: SecondaryBuild::Closure(Closure { + hir: alloc!(hir::ExprClosure { + args, + body, + captures, + }), + call, + }), + })?; + if captures.is_empty() { return Ok(hir::ExprKind::Fn(meta.hash)); } @@ -304,7 +231,7 @@ pub(crate) fn block<'hir>( } #[instrument(span = span)] -pub(crate) fn statements<'hir>( +fn statements<'hir>( cx: &mut Ctxt<'hir, '_, '_>, label: Option<&(ast::Label, T![:])>, statements: &[ast::Stmt], @@ -365,7 +292,7 @@ pub(crate) fn statements<'hir>( } #[instrument(span = ast)] -pub(crate) fn expr_range<'hir>( +fn expr_range<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprRange, ) -> compile::Result> { @@ -400,7 +327,7 @@ pub(crate) fn expr_range<'hir>( } #[instrument(span = ast)] -pub(crate) fn expr_object<'hir>( +fn expr_object<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprObject, ) -> compile::Result> { @@ -770,7 +697,7 @@ pub(crate) fn expr<'hir>( /// Construct a pattern from a constant value. #[instrument(span = span)] -pub(crate) fn pat_const_value<'hir>( +fn pat_const_value<'hir>( cx: &mut Ctxt<'hir, '_, '_>, const_value: &ConstValue, span: &dyn Spanned, @@ -860,7 +787,7 @@ pub(crate) fn pat_const_value<'hir>( } #[instrument(span = ast)] -pub(crate) fn expr_if<'hir>( +fn expr_if<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprIf, ) -> compile::Result> { @@ -908,10 +835,7 @@ pub(crate) fn expr_if<'hir>( } #[instrument(span = ast)] -pub(crate) fn lit<'hir>( - cx: &mut Ctxt<'hir, '_, '_>, - ast: &ast::Lit, -) -> compile::Result> { +fn lit<'hir>(cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::Lit) -> compile::Result> { alloc_with!(cx, ast); match ast { @@ -962,7 +886,7 @@ pub(crate) fn lit<'hir>( } #[instrument(span = ast)] -pub(crate) fn expr_unary<'hir>( +fn expr_unary<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprUnary, ) -> compile::Result> { @@ -1006,7 +930,7 @@ pub(crate) fn expr_unary<'hir>( /// Lower a block expression. #[instrument(span = ast)] -pub(crate) fn expr_block<'hir>( +fn expr_block<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprBlock, ) -> compile::Result> { @@ -1061,43 +985,35 @@ pub(crate) fn expr_block<'hir>( match (kind, &meta.kind) { (ExprBlockKind::Async, &meta::Kind::AsyncBlock { call, do_move, .. }) => { - let captures = match cx.q.get_captures(meta.hash) { - None => { - tracing::trace!("queuing async block build entry"); + tracing::trace!("queuing async block build entry"); - if let Some(label) = &ast.label { - return Err(compile::Error::msg( - label, - "Async blocks cannot be labelled", - )); - }; + if let Some(label) = &ast.label { + return Err(compile::Error::msg( + label, + "Async blocks cannot be labelled", + )); + }; - cx.scopes.push_captures()?; - block(cx, None, &ast.block)?; - let layer = cx.scopes.pop().with_span(&ast.block)?; + cx.scopes.push_captures()?; + let block = alloc!(block(cx, None, &ast.block)?); + let layer = cx.scopes.pop().with_span(&ast.block)?; - cx.q.insert_captures(meta.hash, layer.captures())?; + cx.q.set_used(&meta.item_meta)?; - cx.q.set_used(&meta.item_meta)?; - cx.q.inner.queue.try_push_back(BuildEntry { - item_meta: meta.item_meta, - build: Build::AsyncBlock(indexing::AsyncBlock { - ast: ast.block.try_clone()?, - call, - }), - })?; + let captures = &*iter!(layer.captures().map(|(_, id)| id)); - iter!(layer.captures()) - } - Some(captures) => { - iter!(captures, |capture| match capture { - hir::OwnedName::SelfValue => hir::Name::SelfValue, - hir::OwnedName::Str(name) => hir::Name::Str(alloc_str!(name.as_str())), - hir::OwnedName::Id(id) => hir::Name::Id(*id), - }) - } + let Some(queue) = cx.secondary_builds.as_mut() else { + return Err(compile::Error::new(ast, ErrorKind::AsyncBlockInConst)); }; + queue.try_push(SecondaryBuildEntry { + item_meta: meta.item_meta, + build: SecondaryBuild::AsyncBlock(AsyncBlock { + hir: alloc!(hir::AsyncBlock { block, captures }), + call, + }), + })?; + Ok(hir::ExprKind::AsyncBlock(alloc!(hir::ExprAsyncBlock { hash: meta.hash, do_move, @@ -1196,8 +1112,8 @@ fn fn_arg<'hir>( Ok(match ast { ast::FnArg::SelfValue(ast) => { - cx.scopes.define(hir::Name::SelfValue, ast)?; - hir::FnArg::SelfValue(ast.span()) + let id = cx.scopes.define(hir::Name::SelfValue, ast)?; + hir::FnArg::SelfValue(ast.span(), id) } ast::FnArg::Pat(ast) => hir::FnArg::Pat(alloc!(pat_binding(cx, ast)?)), }) @@ -1271,8 +1187,8 @@ fn pat<'hir>(cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::Pat) -> compile::Result(cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::Pat) -> compile::Result { return Err(compile::Error::new( @@ -1487,7 +1403,7 @@ fn object_key<'hir, 'ast>( /// Lower the given path. #[instrument(span = ast)] -pub(crate) fn expr_path<'hir>( +fn expr_path<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::Path, in_path: bool, @@ -1495,11 +1411,11 @@ pub(crate) fn expr_path<'hir>( alloc_with!(cx, ast); if let Some(ast::PathKind::SelfValue) = ast.as_kind() { - let Some(..) = cx.scopes.get(hir::Name::SelfValue)? else { + let Some((id, _)) = cx.scopes.get(hir::Name::SelfValue)? else { return Err(compile::Error::new(ast, ErrorKind::MissingSelf)); }; - return Ok(hir::ExprKind::Variable(hir::Name::SelfValue)); + return Ok(hir::ExprKind::Variable(id)); } if let Needs::Value = cx.needs.get() { @@ -1786,7 +1702,7 @@ fn expr_call<'hir>( cx: &mut Ctxt<'hir, '_, '_>, ast: &ast::ExprCall, ) -> compile::Result> { - pub(crate) fn find_path(ast: &ast::Expr) -> Option<&ast::Path> { + fn find_path(ast: &ast::Expr) -> Option<&ast::Path> { let mut current = ast; loop { diff --git a/crates/rune/src/hir/scopes.rs b/crates/rune/src/hir/scopes.rs index e3222b529..1390556dd 100644 --- a/crates/rune/src/hir/scopes.rs +++ b/crates/rune/src/hir/scopes.rs @@ -5,11 +5,13 @@ use core::fmt; use core::num::NonZeroUsize; use crate::alloc::prelude::*; -use crate::alloc::{self, BTreeSet, HashSet, Vec}; +use crate::alloc::{self, BTreeSet, HashMap, Vec}; use crate::ast::Spanned; use crate::compile::error::{MissingScope, PopError}; use crate::compile::{self, HasSpan}; use crate::hir; +use crate::parse::NonZeroId; +use crate::shared::Gen; use rune_macros::instrument; @@ -35,11 +37,11 @@ pub(crate) struct Layer<'hir> { /// The kind of the layer. kind: LayerKind, /// Variables defined in this layer. - variables: HashSet>, + variables: HashMap, hir::Variable>, /// Order of variable definitions. - order: Vec>, + order: Vec, /// Captures inside of this layer. - captures: BTreeSet>, + captures: BTreeSet<(hir::Name<'hir>, hir::Variable)>, /// An optional layer label. label: Option<&'hir str>, } @@ -51,33 +53,37 @@ impl<'hir> Layer<'hir> { /// Convert layer into variable drop order. #[inline(always)] - pub(crate) fn into_drop_order(self) -> impl ExactSizeIterator> { + pub(crate) fn into_drop_order(self) -> impl ExactSizeIterator { self.order.into_iter().rev() } /// Variables captured by the layer. - pub(crate) fn captures(&self) -> impl ExactSizeIterator> + '_ { + pub(crate) fn captures( + &self, + ) -> impl ExactSizeIterator, hir::Variable)> + '_ { self.captures.iter().copied() } } -pub(crate) struct Scopes<'hir> { +pub(crate) struct Scopes<'hir, 'a> { scope: Scope, scopes: Vec>, + gen: &'a Gen, } -impl<'hir> Scopes<'hir> { +impl<'hir, 'a> Scopes<'hir, 'a> { /// Root scope. pub const ROOT: Scope = Scope(0); #[inline] - pub(crate) fn new() -> alloc::Result { + pub(crate) fn new(gen: &'a Gen) -> alloc::Result { let mut scopes = Vec::new(); scopes.try_push(Layer::default())?; Ok(Self { scope: Scopes::ROOT, scopes, + gen, }) } @@ -102,7 +108,7 @@ impl<'hir> Scopes<'hir> { let layer = Layer { scope, parent: Some(NonZeroUsize::new(self.scope.0.wrapping_add(1)).expect("ran out of ids")), - variables: HashSet::new(), + variables: HashMap::new(), order: Vec::new(), kind, captures: BTreeSet::new(), @@ -135,22 +141,42 @@ impl<'hir> Scopes<'hir> { Ok(layer) } + /// Insert a variable with the specified id. + #[tracing::instrument(skip_all, fields(?self.scope, ?name))] + pub(crate) fn insert( + &mut self, + name: hir::Name<'hir>, + id: hir::Variable, + span: &dyn Spanned, + ) -> compile::Result { + tracing::trace!(?self.scope, ?name, "define"); + + let Some(layer) = self.scopes.get_mut(self.scope.0) else { + return Err(HasSpan::new(span, MissingScope(self.scope.0)).into()); + }; + + layer.variables.try_insert(name, id)?; + layer.order.try_push(id)?; + Ok(id) + } + /// Define the given variable. #[tracing::instrument(skip_all, fields(?self.scope, ?name))] pub(crate) fn define( &mut self, name: hir::Name<'hir>, span: &dyn Spanned, - ) -> compile::Result> { + ) -> compile::Result { tracing::trace!(?self.scope, ?name, "define"); let Some(layer) = self.scopes.get_mut(self.scope.0) else { return Err(HasSpan::new(span, MissingScope(self.scope.0)).into()); }; - layer.variables.try_insert(name)?; - layer.order.try_push(name)?; - Ok(name) + let id = hir::Variable(self.gen.next()); + layer.variables.try_insert(name, id)?; + layer.order.try_push(id)?; + Ok(id) } /// Try to lookup the given variable. @@ -158,20 +184,20 @@ impl<'hir> Scopes<'hir> { pub(crate) fn get( &mut self, name: hir::Name<'hir>, - ) -> alloc::Result, Scope)>> { + ) -> alloc::Result> { tracing::trace!("get"); let mut blocks = Vec::new(); let mut scope = self.scopes.get(self.scope.0); - let scope = 'ok: { + let (scope, id) = 'ok: { loop { let Some(layer) = scope.take() else { return Ok(None); }; - if layer.variables.contains(&name) { - break 'ok layer.scope; + if let Some(id) = layer.variables.get(&name) { + break 'ok (layer.scope, *id); } if let LayerKind::Captures { .. } = layer.kind { @@ -193,10 +219,10 @@ impl<'hir> Scopes<'hir> { continue; }; - layer.captures.try_insert(name)?; + layer.captures.try_insert((name, id))?; } - Ok(Some((name, scope))) + Ok(Some((id, scope))) } /// Walk the loop and construct captures for it. @@ -204,7 +230,7 @@ impl<'hir> Scopes<'hir> { pub(crate) fn loop_drop( &self, label: Option<&str>, - ) -> alloc::Result>>> { + ) -> alloc::Result>> { let mut captures = Vec::new(); let mut scope = self.scopes.get(self.scope.0); diff --git a/crates/rune/src/indexing/mod.rs b/crates/rune/src/indexing/mod.rs index 048f8d0b2..294dc794c 100644 --- a/crates/rune/src/indexing/mod.rs +++ b/crates/rune/src/indexing/mod.rs @@ -121,22 +121,6 @@ pub(crate) struct Variant { pub(crate) index: usize, } -#[derive(Debug, TryClone)] -pub(crate) struct Closure { - /// Ast for closure. - pub(crate) ast: Box, - /// Calling convention used for closure. - pub(crate) call: Call, -} - -#[derive(Debug, TryClone)] -pub(crate) struct AsyncBlock { - /// Ast for block. - pub(crate) ast: ast::Block, - /// Calling convention used for async block. - pub(crate) call: Call, -} - #[derive(Debug, TryClone)] pub(crate) struct ConstExpr { pub(crate) ast: Box, diff --git a/crates/rune/src/parse/id.rs b/crates/rune/src/parse/id.rs index 8d13f38c3..28b7c11ee 100644 --- a/crates/rune/src/parse/id.rs +++ b/crates/rune/src/parse/id.rs @@ -7,7 +7,7 @@ use crate::alloc::prelude::*; /// A non-zero [Id] which definitely contains a value. We keep this distinct /// from `Id` to allow for safely using this as a key in a hashmap, preventing /// us from inadvertently storing an empty identifier. -#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[try_clone(copy)] #[repr(transparent)] pub struct NonZeroId(#[try_clone(copy)] NonZeroU32); @@ -19,6 +19,13 @@ impl fmt::Display for NonZeroId { } } +impl fmt::Debug for NonZeroId { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + impl From for NonZeroId { fn from(value: NonZeroU32) -> Self { Self(value) diff --git a/crates/rune/src/query/mod.rs b/crates/rune/src/query/mod.rs index cf5e57716..05ba50437 100644 --- a/crates/rune/src/query/mod.rs +++ b/crates/rune/src/query/mod.rs @@ -20,6 +20,7 @@ use crate::hir; use crate::indexing; use crate::parse::NonZeroId; use crate::runtime::format; +use crate::runtime::Call; /// Indication whether a value is being evaluated because it's being used or not. #[derive(Debug, TryClone, Clone, Copy)] @@ -123,12 +124,42 @@ pub(crate) struct BuiltInLine { pub(crate) value: ast::Lit, } +#[derive(Debug, TryClone)] +pub(crate) struct Closure<'hir> { + /// Ast for closure. + pub(crate) hir: &'hir hir::ExprClosure<'hir>, + /// Calling convention used for closure. + pub(crate) call: Call, +} + +#[derive(Debug, TryClone)] +pub(crate) struct AsyncBlock<'hir> { + /// Ast for block. + pub(crate) hir: &'hir hir::AsyncBlock<'hir>, + /// Calling convention used for async block. + pub(crate) call: Call, +} + +/// An entry in the build queue. +#[derive(Debug, TryClone)] +pub(crate) enum SecondaryBuild<'hir> { + Closure(Closure<'hir>), + AsyncBlock(AsyncBlock<'hir>), +} + +/// An entry in the build queue. +#[derive(Debug, TryClone)] +pub(crate) struct SecondaryBuildEntry<'hir> { + /// The item of the build entry. + pub(crate) item_meta: ItemMeta, + /// The build entry. + pub(crate) build: SecondaryBuild<'hir>, +} + /// An entry in the build queue. #[derive(Debug, TryClone)] pub(crate) enum Build { Function(indexing::Function), - Closure(indexing::Closure), - AsyncBlock(indexing::AsyncBlock), Unused, Import(indexing::Import), /// A public re-export. diff --git a/crates/rune/src/query/query.rs b/crates/rune/src/query/query.rs index 8d21df25f..81850c6be 100644 --- a/crates/rune/src/query/query.rs +++ b/crates/rune/src/query/query.rs @@ -90,8 +90,6 @@ pub(crate) struct QueryInner<'arena> { pub(crate) items: HashMap, /// All available names. names: Names, - /// Recorded captures. - captures: HashMap>, } impl QueryInner<'_> { @@ -1858,26 +1856,6 @@ impl<'a, 'arena> Query<'a, 'arena> { self.context.get_const_value(hash) } - - /// Insert captures. - pub(crate) fn insert_captures<'hir, C>(&mut self, hash: Hash, captures: C) -> alloc::Result<()> - where - C: IntoIterator>, - { - let captures = captures - .into_iter() - .map(hir::Name::into_owned) - .try_collect::>()??; - - self.inner.captures.try_insert(hash, captures)?; - - Ok(()) - } - - /// Get captures for the given hash. - pub(crate) fn get_captures(&self, hash: Hash) -> Option<&[hir::OwnedName]> { - Some(self.inner.captures.get(&hash)?) - } } struct FoundImportStep {