From 6b31eda03d468648de84de4511ce724985dece27 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Sun, 3 Nov 2024 19:26:42 +0100 Subject: [PATCH] Simplify how variants are handled (#877) --- crates/rune-alloc/src/error.rs | 8 + crates/rune-core/src/item/component_ref.rs | 4 +- crates/rune-core/src/item/item.rs | 8 + crates/rune-macros/src/context.rs | 2 - crates/rune-macros/src/from_value.rs | 43 +- crates/rune/benches/bf.rn | 116 +++ crates/rune/benches/fib.rn | 17 + crates/rune/benches/primes.rn | 31 + crates/rune/src/cli/benches.rs | 92 +- crates/rune/src/cli/out.rs | 34 +- crates/rune/src/compile/context.rs | 15 +- crates/rune/src/compile/error.rs | 4 +- crates/rune/src/compile/ir/eval.rs | 2 +- crates/rune/src/compile/ir/interpreter.rs | 24 +- crates/rune/src/compile/unit_builder.rs | 60 +- crates/rune/src/compile/v1/assemble.rs | 12 +- crates/rune/src/lib.rs | 1 - crates/rune/src/modules/capture_io.rs | 5 + crates/rune/src/modules/future.rs | 8 +- crates/rune/src/modules/iter.rs | 6 +- crates/rune/src/modules/string.rs | 8 +- crates/rune/src/runtime/const_value.rs | 4 +- crates/rune/src/runtime/dynamic.rs | 123 +++ crates/rune/src/runtime/format.rs | 4 +- crates/rune/src/runtime/function.rs | 99 +-- crates/rune/src/runtime/hasher.rs | 10 - crates/rune/src/runtime/inst.rs | 21 - crates/rune/src/runtime/mod.rs | 13 +- crates/rune/src/runtime/range.rs | 4 +- crates/rune/src/runtime/range_from.rs | 2 +- crates/rune/src/runtime/range_inclusive.rs | 4 +- crates/rune/src/runtime/shared.rs | 332 -------- crates/rune/src/runtime/stack.rs | 1 + crates/rune/src/runtime/tests.rs | 203 +---- crates/rune/src/runtime/tuple.rs | 13 + crates/rune/src/runtime/type_info.rs | 23 +- crates/rune/src/runtime/type_of.rs | 29 +- crates/rune/src/runtime/unit.rs | 51 +- crates/rune/src/runtime/value.rs | 800 ++++++------------ crates/rune/src/runtime/value/data.rs | 144 +--- crates/rune/src/runtime/value/dynamic.rs | 455 ++++++++++ crates/rune/src/runtime/value/inline.rs | 93 +- crates/rune/src/runtime/value/macros.rs | 203 +---- crates/rune/src/runtime/value/rtti.rs | 131 ++- crates/rune/src/runtime/value/serde.rs | 24 +- crates/rune/src/runtime/value/tests.rs | 138 +++ crates/rune/src/runtime/variant.rs | 217 ----- crates/rune/src/runtime/vec.rs | 16 + crates/rune/src/runtime/vm.rs | 383 ++------- crates/rune/src/runtime/vm_error.rs | 99 ++- crates/rune/src/tests.rs | 8 +- crates/rune/src/tests/function_guardedargs.rs | 21 +- crates/rune/src/tests/variants.rs | 33 - crates/rune/src/tests/vm_function.rs | 10 +- crates/rune/tests/variants.rn | 30 + 55 files changed, 1760 insertions(+), 2481 deletions(-) create mode 100644 crates/rune/benches/bf.rn create mode 100644 crates/rune/benches/fib.rn create mode 100644 crates/rune/benches/primes.rn create mode 100644 crates/rune/src/runtime/dynamic.rs delete mode 100644 crates/rune/src/runtime/shared.rs create mode 100644 crates/rune/src/runtime/value/dynamic.rs create mode 100644 crates/rune/src/runtime/value/tests.rs delete mode 100644 crates/rune/src/runtime/variant.rs delete mode 100644 crates/rune/src/tests/variants.rs create mode 100644 crates/rune/tests/variants.rn diff --git a/crates/rune-alloc/src/error.rs b/crates/rune-alloc/src/error.rs index 59042a9e2..6f6d0ba60 100644 --- a/crates/rune-alloc/src/error.rs +++ b/crates/rune-alloc/src/error.rs @@ -1,5 +1,6 @@ //! Error types used by rune alloc. +use core::alloc::LayoutError; use core::convert::Infallible; use core::fmt; @@ -65,6 +66,13 @@ impl From for Error { } } +impl From for Error { + #[inline] + fn from(_: LayoutError) -> Self { + Error::LayoutError + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/crates/rune-core/src/item/component_ref.rs b/crates/rune-core/src/item/component_ref.rs index 93cddc747..bd57cd38d 100644 --- a/crates/rune-core/src/item/component_ref.rs +++ b/crates/rune-core/src/item/component_ref.rs @@ -22,9 +22,7 @@ pub enum ComponentRef<'a> { } impl<'a> ComponentRef<'a> { - /// Get the component as a string. - #[cfg(feature = "doc")] - #[doc(hidden)] + /// Get the component as a `&str` reference. pub fn as_str(&self) -> Option<&'a str> { match self { ComponentRef::Str(string) => Some(string), diff --git a/crates/rune-core/src/item/item.rs b/crates/rune-core/src/item/item.rs index aa55b6624..89b58d86d 100644 --- a/crates/rune-core/src/item/item.rs +++ b/crates/rune-core/src/item/item.rs @@ -211,6 +211,14 @@ impl Item { self.iter().next_back() } + /// Access the base name of the item if available. + /// + /// The base name is the last string component of the item. + #[inline] + pub fn base_name(&self) -> Option<&str> { + self.iter().next_back()?.as_str() + } + /// An iterator over the [Component]s that constitute this item. /// /// # Examples diff --git a/crates/rune-macros/src/context.rs b/crates/rune-macros/src/context.rs index cb9f2f6e5..ba99eee3a 100644 --- a/crates/rune-macros/src/context.rs +++ b/crates/rune-macros/src/context.rs @@ -775,7 +775,6 @@ impl Context { value_mut_guard: path(m, ["runtime", "ValueMutGuard"]), value_ref_guard: path(m, ["runtime", "ValueRefGuard"]), value: path(m, ["runtime", "Value"]), - variant_data: path(m, ["runtime", "VariantData"]), vec: path(m, ["alloc", "Vec"]), vm_result: path(m, ["runtime", "VmResult"]), vm_try: path(m, ["vm_try"]), @@ -870,7 +869,6 @@ pub(crate) struct Tokens { pub(crate) value_mut_guard: syn::Path, pub(crate) value_ref_guard: syn::Path, pub(crate) value: syn::Path, - pub(crate) variant_data: syn::Path, pub(crate) vec: syn::Path, pub(crate) vm_result: syn::Path, pub(crate) vm_try: syn::Path, diff --git a/crates/rune-macros/src/from_value.rs b/crates/rune-macros/src/from_value.rs index 007d2f109..7d9cdb265 100644 --- a/crates/rune-macros/src/from_value.rs +++ b/crates/rune-macros/src/from_value.rs @@ -78,7 +78,7 @@ impl Expander<'_> { #[automatically_derived] impl #from_value for #ident { fn from_value(value: #value) -> #result { - match #value::into_type_value(value)? { + match #value::as_type_value(&value)? { #expanded actual => { #result::Err(#runtime_error::expected::<#expected>(#type_value::type_info(&actual))) @@ -104,7 +104,6 @@ impl Expander<'_> { let Tokens { type_value, from_value, - variant_data, value, result, runtime_error, @@ -145,27 +144,31 @@ impl Expander<'_> { }; let variant = quote! { - #type_value::Variant(variant) => { - let mut it = variant.rtti().item.iter(); + #type_value::EmptyStruct(data) => { + let Some(name) = data.rtti().item().base_name() else { + return #result::Err(#runtime_error::__rune_macros__missing_variant_name()); + }; - let Some(name) = it.next_back_str() else { + match name { + #(#unit_matches,)* #missing, + } + } + #type_value::TupleStruct(tuple) => { + let Some(name) = tuple.rtti().item().base_name() else { return #result::Err(#runtime_error::__rune_macros__missing_variant_name()); }; - match variant.data() { - #variant_data::Empty => match name { - #(#unit_matches,)* #missing, - }, - #variant_data::Tuple(tuple) => match name { - #(#unnamed_matches,)* #missing, - }, - #variant_data::Struct(data) => { - let object = variant.accessor(data); - - match name { - #(#named_matches,)* #missing, - } - } + match name { + #(#unnamed_matches,)* #missing, + } + } + #type_value::Struct(object) => { + let Some(name) = object.rtti().item().base_name() else { + return #result::Err(#runtime_error::__rune_macros__missing_variant_name()); + }; + + match name { + #(#named_matches,)* #missing, } } }; @@ -174,7 +177,7 @@ impl Expander<'_> { #[automatically_derived] impl #from_value for #ident { fn from_value(value: #value) -> #result { - match #value::into_type_value(value)? { + match #value::as_type_value(&value)? { #variant, actual => { #result::Err(#runtime_error::__rune_macros__expected_variant(#type_value::type_info(&actual))) diff --git a/crates/rune/benches/bf.rn b/crates/rune/benches/bf.rn new file mode 100644 index 000000000..a1a6ad1dc --- /dev/null +++ b/crates/rune/benches/bf.rn @@ -0,0 +1,116 @@ +enum Op { + Inc(v), + Move(v), + Loop(ops), + Input, + Print, +} + +struct Tape { + pos, + tape, +} + +impl Tape { + fn new() { + Tape { pos: 0, tape: [0] } + } + + fn get(self) { + self.tape[self.pos] + } + + fn inc(self, x) { + self.tape[self.pos] = (self.tape[self.pos] + x) % 256; + + if self.tape[self.pos] < 0 { + self.tape[self.pos] = self.tape[self.pos] + 256; + } + } + + fn mov(self, x) { + self.pos += x; + + while self.pos >= self.tape.len() { + self.tape.push(0); + } + } + + fn set(self, v) { + self.tape[self.pos] = v; + } +} + +fn run(program, tape, inputs) { + for op in program { + match op { + Op::Inc(x) => tape.inc(x), + Op::Move(x) => tape.mov(x), + Op::Loop(program) => while tape.get() != 0 { + run(program, tape, inputs); + }, + Op::Print => { + let c = char::from_i64(tape.get()).expect("A valid char"); + print!("{c}"); + } + Op::Input => { + tape.set(0) + } + } + } +} + +fn parse(it) { + let buf = Vec::new(); + + while let Some(c) = it.next() { + let op = match c { + '+' => Op::Inc(1), + '-' => Op::Inc(-1), + '>' => Op::Move(1), + '<' => Op::Move(-1), + '.' => Op::Print, + '[' => Op::Loop(parse(it)), + ',' => Op::Input, + ']' => break, + _ => continue, + }; + + buf.push(op); + } + + buf +} + +struct Program { + ops, + inputs, +} + +impl Program { + fn new(code, inputs) { + Program { ops: parse(code), inputs } + } + + fn run(self) { + let tape = Tape::new(); + run(self.ops, tape, self.inputs); + } +} + +fn bf(s, i) { + let program = Program::new(s.chars(), i); + program.run(); +} + +#[bench] +pub fn bf_hello_world(b) { + b.iter( + || { + bf( + "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.", + 0, + ) + }, + ) +} diff --git a/crates/rune/benches/fib.rn b/crates/rune/benches/fib.rn new file mode 100644 index 000000000..b6779d855 --- /dev/null +++ b/crates/rune/benches/fib.rn @@ -0,0 +1,17 @@ +fn fib(n) { + if n <= 1 { + n + } else { + fib(n - 2) + fib(n - 1) + } +} + +#[bench] +pub fn fib15(b) { + b.iter(|| fib(15)); +} + +#[bench] +pub fn fib20(b) { + b.iter(|| fib(20)); +} diff --git a/crates/rune/benches/primes.rn b/crates/rune/benches/primes.rn new file mode 100644 index 000000000..69b0889db --- /dev/null +++ b/crates/rune/benches/primes.rn @@ -0,0 +1,31 @@ +const MAX_NUMBER_TO_CHECK = 10_000; + +#[bench] +pub fn primes(b) { + b.iter( + || { + let prime_mask = []; + + prime_mask.resize(MAX_NUMBER_TO_CHECK, true); + + prime_mask[0] = false; + prime_mask[1] = false; + + let total_primes_found = 0; + + for p in 2..MAX_NUMBER_TO_CHECK { + if prime_mask[p] { + total_primes_found += 1; + let i = 2 * p; + + while i < MAX_NUMBER_TO_CHECK { + prime_mask[i] = false; + i += p; + } + } + } + + total_primes_found + }, + ); +} diff --git a/crates/rune/src/cli/benches.rs b/crates/rune/src/cli/benches.rs index 339bbd509..131ba4556 100644 --- a/crates/rune/src/cli/benches.rs +++ b/crates/rune/src/cli/benches.rs @@ -11,7 +11,9 @@ use crate::modules::capture_io::CaptureIo; use crate::modules::test::Bencher; use crate::runtime::{Function, Unit, Value}; use crate::support::Result; -use crate::{Context, Hash, Item, ItemBuf, Sources, Vm}; +use crate::{Context, Hash, ItemBuf, Sources, Vm}; + +use super::{Color, Stream}; mod cli { use std::path::PathBuf; @@ -23,7 +25,7 @@ mod cli { #[command(rename_all = "kebab-case")] pub(crate) struct Flags { /// Rounds of warmup to perform - #[arg(long, default_value = "100")] + #[arg(long, default_value = "10")] pub(super) warmup: u32, /// Iterations to run of the benchmark #[arg(long, default_value = "100")] @@ -74,7 +76,9 @@ pub(super) async fn run( return Ok(ExitCode::Success); } - writeln!(io.stdout, "Found {} benches...", fns.len())?; + io.section("Benching", Stream::Stdout, Color::Highlight)? + .append(format_args!(" Found {} benches", fns.len()))? + .close()?; let mut any_error = false; @@ -87,9 +91,11 @@ pub(super) async fn run( any_error = true; if let Some(capture_io) = capture_io { - writeln!(io.stdout, "-- output --")?; - capture_io.drain_into(&mut *io.stdout)?; - writeln!(io.stdout, "-- end output --")?; + if !capture_io.is_empty() { + writeln!(io.stdout, "-- output --")?; + capture_io.drain_into(&mut *io.stdout)?; + writeln!(io.stdout, "-- end output --")?; + } } continue; @@ -100,13 +106,24 @@ pub(super) async fn run( let multiple = fns.len() > 1; for (i, f) in fns.iter().enumerate() { - if let Err(e) = bench_fn(io, i, item, args, f, multiple) { + let out; + + let item: &dyn fmt::Display = if multiple { + out = DisplayHash(item, i); + &out + } else { + &item + }; + + if let Err(e) = bench_fn(io, item, args, f) { writeln!(io.stdout, "{}: Error in bench iteration: {}", item, e)?; if let Some(capture_io) = capture_io { - writeln!(io.stdout, "-- output --")?; - capture_io.drain_into(&mut *io.stdout)?; - writeln!(io.stdout, "-- end output --")?; + if !capture_io.is_empty() { + writeln!(io.stdout, "-- output --")?; + capture_io.drain_into(&mut *io.stdout)?; + writeln!(io.stdout, "-- end output --")?; + } } any_error = true; @@ -121,23 +138,51 @@ pub(super) async fn run( } } -fn bench_fn( - io: &mut Io<'_>, - i: usize, - item: &Item, - args: &Flags, - f: &Function, - multiple: bool, -) -> Result<()> { - for _ in 0..args.warmup { +struct DisplayHash(A, B); + +impl fmt::Display for DisplayHash +where + A: fmt::Display, + B: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(a, b) = self; + write!(f, "{a}#{b}") + } +} + +fn bench_fn(io: &mut Io<'_>, item: &dyn fmt::Display, args: &Flags, f: &Function) -> Result<()> { + let mut section = io.section("Warming up", Stream::Stdout, Color::Ignore)?; + section.append(format_args!(" {item} ({} iters): ", args.warmup))?; + + let step = (args.warmup / 10).max(1); + + for n in 0..args.warmup { + if n % step == 0 { + section.append(".")?; + section.flush()?; + } + let value = f.call::(()).into_result()?; drop(hint::black_box(value)); } + section.close()?; + let iterations = usize::try_from(args.iter).expect("iterations out of bounds"); let mut collected = Vec::try_with_capacity(iterations)?; - for _ in 0..args.iter { + let step = (args.iter / 10).max(1); + + let mut section = io.section("Running", Stream::Stdout, Color::Highlight)?; + section.append(format_args!(" {item} ({} iters): ", args.iter))?; + + for n in 0..args.iter { + if n % step == 0 { + section.append(".")?; + section.flush()?; + } + let start = Instant::now(); let value = f.call::(()).into_result()?; let duration = Instant::now().duration_since(start); @@ -163,12 +208,7 @@ fn bench_fn( iterations, }; - if multiple { - writeln!(io.stdout, "bench {}#{}: {}", item, i, format)?; - } else { - writeln!(io.stdout, "bench {}: {}", item, format)?; - } - + section.passed(format_args!(" {format}"))?.close()?; Ok(()) } diff --git a/crates/rune/src/cli/out.rs b/crates/rune/src/cli/out.rs index 3e0a50fea..6b5b57aab 100644 --- a/crates/rune/src/cli/out.rs +++ b/crates/rune/src/cli/out.rs @@ -130,27 +130,41 @@ pub(super) struct Section<'a> { } impl Section<'_> { - pub(super) fn append(&mut self, text: impl fmt::Display) -> io::Result<()> { - write!(self.io, "{text}") + pub(super) fn append(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> { + write!(self.io, "{text}")?; + Ok(self) + } + + /// Flush the current section. + pub(super) fn flush(&mut self) -> io::Result<&mut Self> { + self.io.flush()?; + Ok(self) } - pub(super) fn append_with(&mut self, text: impl fmt::Display, color: Color) -> io::Result<()> { + pub(super) fn append_with( + &mut self, + text: impl fmt::Display, + color: Color, + ) -> io::Result<&mut Self> { self.io.set_color(color.find(self.colors))?; write!(self.io, "{text}")?; self.io.reset()?; - Ok(()) + Ok(self) } - pub(super) fn error(&mut self, text: impl fmt::Display) -> io::Result<()> { - self.append_with(text, Color::Error) + pub(super) fn error(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> { + self.append_with(text, Color::Error)?; + Ok(self) } - pub(super) fn passed(&mut self, text: impl fmt::Display) -> io::Result<()> { - self.append_with(text, Color::Passed) + pub(super) fn passed(&mut self, text: impl fmt::Display) -> io::Result<&mut Self> { + self.append_with(text, Color::Passed)?; + Ok(self) } - pub(super) fn close(self) -> io::Result<()> { - writeln!(self.io) + pub(super) fn close(&mut self) -> io::Result<()> { + writeln!(self.io)?; + Ok(()) } } diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index c3427606c..afd0a6d8e 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -21,7 +21,7 @@ use crate::module::{ }; use crate::runtime::{ AnyTypeInfo, ConstConstruct, ConstContext, ConstValue, FunctionHandler, InstAddress, Memory, - Output, Protocol, RuntimeContext, TypeCheck, TypeInfo, VariantRtti, VmResult, + Output, Protocol, Rtti, RttiKind, RuntimeContext, TypeCheck, TypeInfo, VmResult, }; use crate::{Hash, Item, ItemBuf}; @@ -764,6 +764,12 @@ impl Context { continue; }; + let kind = match fields { + Fields::Empty => RttiKind::Empty, + Fields::Unnamed(..) => RttiKind::Tuple, + Fields::Named(..) => RttiKind::Struct, + }; + let item = ty.item.extended(variant.name)?; let hash = Hash::type_hash(&item); @@ -771,9 +777,10 @@ impl Context { item: item.try_clone()?, hash, type_check: None, - type_info: TypeInfo::variant(Arc::new(VariantRtti { - enum_hash: ty.hash, - hash, + type_info: TypeInfo::rtti(Arc::new(Rtti { + kind, + hash: ty.hash, + variant_hash: hash, item: item.try_clone()?, fields: fields.to_fields()?, })), diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index a4164ff1c..b6690f894 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -545,7 +545,7 @@ pub(crate) enum ErrorKind { path: Vec, }, LastUseComponent, - VariantRttiConflict { + RttiConflict { hash: Hash, }, TypeRttiConflict { @@ -1113,7 +1113,7 @@ impl fmt::Display for ErrorKind { ErrorKind::LastUseComponent => { write!(f, "Missing last use component")?; } - ErrorKind::VariantRttiConflict { hash } => { + ErrorKind::RttiConflict { hash } => { write!(f,"Tried to insert variant runtime type information, but conflicted with hash `{hash}`")?; } ErrorKind::TypeRttiConflict { hash } => { diff --git a/crates/rune/src/compile/ir/eval.rs b/crates/rune/src/compile/ir/eval.rs index 30664b073..159eb6a53 100644 --- a/crates/rune/src/compile/ir/eval.rs +++ b/crates/rune/src/compile/ir/eval.rs @@ -380,7 +380,7 @@ fn eval_ir_template( return Err(EvalOutcome::not_const(ir)); } }, - ReprRef::Mutable(..) => { + ReprRef::Dynamic(..) => { return Err(EvalOutcome::not_const(ir)); } } diff --git a/crates/rune/src/compile/ir/interpreter.rs b/crates/rune/src/compile/ir/interpreter.rs index c9a67c8e1..cbaf30b87 100644 --- a/crates/rune/src/compile/ir/interpreter.rs +++ b/crates/rune/src/compile/ir/interpreter.rs @@ -209,10 +209,10 @@ impl ir::Scopes { value.type_info(), )); } - ReprRef::Mutable(value) => { + ReprRef::Dynamic(value) => { return Err(compile::Error::expected_type::( ir_target, - value.borrow_ref().with_span(ir_target)?.type_info(), + value.type_info(), )); } ReprRef::Any(value) => match value.type_hash() { @@ -277,12 +277,10 @@ impl ir::Scopes { value.type_info(), )); } - ReprRef::Mutable(current) => { - let mutable = current.borrow_mut().with_span(ir_target)?; - + ReprRef::Dynamic(value) => { return Err(compile::Error::expected_type::( ir_target, - mutable.type_info(), + value.type_info(), )); } ReprRef::Any(any) => match any.type_hash() { @@ -348,14 +346,10 @@ impl ir::Scopes { let current = self.get_target(target)?; match current.as_ref().with_span(ir_target)? { - ReprRef::Mutable(value) => { - let value = value.borrow_ref().with_span(ir_target)?; - - Err(compile::Error::expected_type::( - ir_target, - value.type_info(), - )) - } + ReprRef::Dynamic(value) => Err(compile::Error::expected_type::( + ir_target, + value.type_info(), + )), ReprRef::Any(value) => match value.type_hash() { runtime::Vec::HASH => { let mut vec = @@ -391,7 +385,7 @@ impl ir::Scopes { }, actual => Err(compile::Error::expected_type::( ir_target, - actual.type_info().with_span(ir_target)?, + actual.type_info(), )), } } diff --git a/crates/rune/src/compile/unit_builder.rs b/crates/rune/src/compile/unit_builder.rs index 0fd58c64d..5cf95ef7f 100644 --- a/crates/rune/src/compile/unit_builder.rs +++ b/crates/rune/src/compile/unit_builder.rs @@ -18,8 +18,8 @@ use crate::query::QueryInner; use crate::runtime::debug::{DebugArgs, DebugSignature}; use crate::runtime::unit::UnitEncoder; use crate::runtime::{ - Call, ConstValue, DebugInfo, DebugInst, Inst, Label, Protocol, Rtti, StaticString, Unit, - UnitFn, VariantRtti, + Call, ConstValue, DebugInfo, DebugInst, Inst, Label, Protocol, Rtti, RttiKind, StaticString, + Unit, UnitFn, }; use crate::{Context, Diagnostics, Hash, Item, SourceId}; @@ -74,8 +74,6 @@ pub(crate) struct UnitBuilder { static_object_keys_rev: HashMap, /// Runtime type information for types. rtti: hash::Map>, - /// Runtime type information for variants. - variant_rtti: hash::Map>, /// The current label count. label_count: usize, /// A collection of required function hashes. @@ -153,7 +151,6 @@ impl UnitBuilder { self.static_bytes, self.static_object_keys, self.rtti, - self.variant_rtti, self.debug, self.constants, )) @@ -316,7 +313,9 @@ impl UnitBuilder { let hash = pool.item_type_hash(meta.item_meta.item); let rtti = Arc::new(Rtti { + kind: RttiKind::Empty, hash, + variant_hash: Hash::EMPTY, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: HashMap::new(), }); @@ -347,7 +346,9 @@ impl UnitBuilder { ); let rtti = Arc::new(Rtti { + kind: RttiKind::Empty, hash: meta.hash, + variant_hash: Hash::EMPTY, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: HashMap::new(), }); @@ -404,7 +405,9 @@ impl UnitBuilder { ); let rtti = Arc::new(Rtti { + kind: RttiKind::Tuple, hash: meta.hash, + variant_hash: Hash::EMPTY, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: HashMap::new(), }); @@ -453,7 +456,9 @@ impl UnitBuilder { let hash = pool.item_type_hash(meta.item_meta.item); let rtti = Arc::new(Rtti { + kind: RttiKind::Struct, hash, + variant_hash: Hash::EMPTY, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: named.to_fields()?, }); @@ -477,26 +482,27 @@ impl UnitBuilder { fields: meta::Fields::Empty, .. } => { - let rtti = Arc::new(VariantRtti { - enum_hash, - hash: meta.hash, + let rtti = Arc::new(Rtti { + kind: RttiKind::Empty, + hash: enum_hash, + variant_hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: HashMap::new(), }); if self - .variant_rtti + .rtti .try_insert(meta.hash, rtti) .with_span(span)? .is_some() { return Err(compile::Error::new( span, - ErrorKind::VariantRttiConflict { hash: meta.hash }, + ErrorKind::RttiConflict { hash: meta.hash }, )); } - let info = UnitFn::UnitVariant { hash: meta.hash }; + let info = UnitFn::EmptyStruct { hash: meta.hash }; let signature = DebugSignature::new( pool.item(meta.item_meta.item).try_to_owned()?, @@ -526,26 +532,27 @@ impl UnitBuilder { fields: meta::Fields::Unnamed(args), .. } => { - let rtti = Arc::new(VariantRtti { - enum_hash, - hash: meta.hash, + let rtti = Arc::new(Rtti { + kind: RttiKind::Tuple, + hash: enum_hash, + variant_hash: meta.hash, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: HashMap::new(), }); if self - .variant_rtti + .rtti .try_insert(meta.hash, rtti) .with_span(span)? .is_some() { return Err(compile::Error::new( span, - ErrorKind::VariantRttiConflict { hash: meta.hash }, + ErrorKind::RttiConflict { hash: meta.hash }, )); } - let info = UnitFn::TupleVariant { + let info = UnitFn::TupleStruct { hash: meta.hash, args, }; @@ -580,23 +587,16 @@ impl UnitBuilder { } => { let hash = pool.item_type_hash(meta.item_meta.item); - let rtti = Arc::new(VariantRtti { - enum_hash, - hash, + let rtti = Arc::new(Rtti { + kind: RttiKind::Struct, + hash: enum_hash, + variant_hash: hash, item: pool.item(meta.item_meta.item).try_to_owned()?, fields: named.to_fields()?, }); - if self - .variant_rtti - .try_insert(hash, rtti) - .with_span(span)? - .is_some() - { - return Err(compile::Error::new( - span, - ErrorKind::VariantRttiConflict { hash }, - )); + if self.rtti.try_insert(hash, rtti).with_span(span)?.is_some() { + return Err(compile::Error::new(span, ErrorKind::RttiConflict { hash })); } } meta::Kind::Enum { .. } => { diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index e1e13c6a1..733865f3e 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -2527,7 +2527,7 @@ fn expr_object<'a, 'hir>( span, )?; } - hir::ExprObjectKind::Struct { hash } => { + hir::ExprObjectKind::Struct { hash } | hir::ExprObjectKind::StructVariant { hash } => { cx.asm.push( Inst::Struct { addr: linear.addr(), @@ -2537,16 +2537,6 @@ fn expr_object<'a, 'hir>( span, )?; } - hir::ExprObjectKind::StructVariant { hash } => { - cx.asm.push( - Inst::StructVariant { - addr: linear.addr(), - hash, - out: needs.alloc_output()?, - }, - span, - )?; - } hir::ExprObjectKind::ExternalType { hash, args } => { reorder_field_assignments(cx, hir, linear.addr(), span)?; diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index c399617c0..fd1c34327 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -748,7 +748,6 @@ impl_builtin_type_of! { impl ::std::tuple::Tuple, crate::runtime::Tuple; - impl ::std::object::Object, crate::runtime::Struct; impl ::std::object::Object, crate::alloc::HashMap<::rust_alloc::string::String, T>; impl ::std::object::Object, crate::alloc::HashMap; diff --git a/crates/rune/src/modules/capture_io.rs b/crates/rune/src/modules/capture_io.rs index 48e710fed..11bc2e342 100644 --- a/crates/rune/src/modules/capture_io.rs +++ b/crates/rune/src/modules/capture_io.rs @@ -77,6 +77,11 @@ impl CaptureIo { Self::default() } + /// Test if capture is empty. + pub fn is_empty(&self) -> bool { + self.inner.lock().is_empty() + } + /// Drain all captured I/O that has been written to output functions. pub fn drain(&self) -> Vec { let mut o = self.inner.lock(); diff --git a/crates/rune/src/modules/future.rs b/crates/rune/src/modules/future.rs index ae2ab0495..07914b23e 100644 --- a/crates/rune/src/modules/future.rs +++ b/crates/rune/src/modules/future.rs @@ -32,9 +32,9 @@ where VmErrorKind::bad_argument(index), ]); } - ReprRef::Mutable(value) => { + ReprRef::Dynamic(value) => { return VmResult::err([ - VmErrorKind::expected::(vm_try!(value.borrow_ref()).type_info()), + VmErrorKind::expected::(value.type_info()), VmErrorKind::bad_argument(index), ]); } @@ -106,9 +106,9 @@ async fn join(value: Value) -> VmResult { VmErrorKind::expected::(value.type_info()), ]), }, - ReprRef::Mutable(value) => VmResult::err([ + ReprRef::Dynamic(value) => VmResult::err([ VmErrorKind::bad_argument(0), - VmErrorKind::expected::(vm_try!(value.borrow_ref()).type_info()), + VmErrorKind::expected::(value.type_info()), ]), ReprRef::Any(value) => match value.type_hash() { runtime::Vec::HASH => { diff --git a/crates/rune/src/modules/iter.rs b/crates/rune/src/modules/iter.rs index d164ef4a9..b673d5e06 100644 --- a/crates/rune/src/modules/iter.rs +++ b/crates/rune/src/modules/iter.rs @@ -480,10 +480,8 @@ pub fn module() -> Result { ReprRef::Inline(value) => { return VmResult::expected::(value.type_info()); } - ReprRef::Mutable(value) => { - return VmResult::expected::( - vm_try!(value.borrow_ref()).type_info(), - ); + ReprRef::Dynamic(value) => { + return VmResult::expected::(value.type_info()); } ReprRef::Any(value) => match value.type_hash() { String::HASH => { diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index 953620ba0..3cb9eadaa 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -896,8 +896,8 @@ fn split(this: Ref, value: Value) -> VmResult { VmErrorKind::expected::(value.type_info()), VmErrorKind::bad_argument(0), ]), - ReprRef::Mutable(value) => VmResult::err([ - VmErrorKind::expected::(vm_try!(value.borrow_ref()).type_info()), + ReprRef::Dynamic(value) => VmResult::err([ + VmErrorKind::expected::(value.type_info()), VmErrorKind::bad_argument(0), ]), ReprRef::Any(value) => match value.type_hash() { @@ -945,9 +945,9 @@ fn split_once(this: &str, value: Value) -> VmResult> { VmErrorKind::bad_argument(0), ]); } - ReprRef::Mutable(value) => { + ReprRef::Dynamic(value) => { return VmResult::err([ - VmErrorKind::expected::(vm_try!(value.borrow_ref()).type_info()), + VmErrorKind::expected::(value.type_info()), VmErrorKind::bad_argument(0), ]); } diff --git a/crates/rune/src/runtime/const_value.rs b/crates/rune/src/runtime/const_value.rs index 26e9d5c14..0fec86315 100644 --- a/crates/rune/src/runtime/const_value.rs +++ b/crates/rune/src/runtime/const_value.rs @@ -236,9 +236,9 @@ impl ConstValue { pub(crate) fn from_value_ref(value: &Value) -> Result { let inner = match value.as_ref()? { ReprRef::Inline(value) => ConstValueKind::Inline(*value), - ReprRef::Mutable(value) => { + ReprRef::Dynamic(value) => { return Err(RuntimeError::from(VmErrorKind::ConstNotSupported { - actual: value.borrow_ref()?.type_info(), + actual: value.type_info(), })); } ReprRef::Any(value) => match value.type_hash() { diff --git a/crates/rune/src/runtime/dynamic.rs b/crates/rune/src/runtime/dynamic.rs new file mode 100644 index 000000000..866057901 --- /dev/null +++ b/crates/rune/src/runtime/dynamic.rs @@ -0,0 +1,123 @@ +use core::borrow::Borrow; +use core::hash::Hash; +use core::ops::Deref; + +use crate::alloc::{Box, Vec}; + +use super::{FromValue, ReprOwned, Rtti, RttiKind, RuntimeError, Tuple, TypeInfo, Value}; + +use rust_alloc::sync::Arc; + +/// A reference to a dynamically defined empty type. +pub struct DynamicEmpty { + rtti: Arc, +} + +impl FromValue for DynamicEmpty { + #[inline] + fn from_value(value: Value) -> Result { + match value.take_repr()? { + ReprOwned::Dynamic(value) if matches!(value.rtti().kind, RttiKind::Empty) => Ok(Self { + rtti: value.rtti().clone(), + }), + value => Err(RuntimeError::expected_empty(value.type_info())), + } + } +} + +impl DynamicEmpty { + /// Get human readable type information for the dynamic tuple. + pub fn type_info(&self) -> TypeInfo { + self.rtti.clone().type_info() + } +} + +/// A reference to a dynamically defined tuple. +/// +/// This derefs into a [`Tuple`], which can be used to access the individual +/// fields. +pub struct DynamicTuple { + rtti: Arc, + values: Vec, +} + +impl FromValue for DynamicTuple { + #[inline] + fn from_value(value: Value) -> Result { + match value.take_repr()? { + ReprOwned::Dynamic(value) if matches!(value.rtti().kind, RttiKind::Tuple) => { + let mut values = Vec::try_with_capacity(value.len())?; + + for value in value.borrow_ref()?.iter() { + values.try_push(value.clone())?; + } + + Ok(Self { + rtti: value.rtti().clone(), + values, + }) + } + value => Err(RuntimeError::expected_tuple(value.type_info())), + } + } +} + +impl DynamicTuple { + /// Get human readable type information for the dynamic tuple. + pub fn type_info(&self) -> TypeInfo { + self.rtti.clone().type_info() + } +} + +impl Deref for DynamicTuple { + type Target = Tuple; + + #[inline] + fn deref(&self) -> &Self::Target { + Tuple::new(&self.values) + } +} + +/// A reference to a dynamically defined struct. +pub struct DynamicStruct { + rtti: Arc, + values: Vec, +} + +impl FromValue for DynamicStruct { + #[inline] + fn from_value(value: Value) -> Result { + match value.take_repr()? { + ReprOwned::Dynamic(value) if matches!(value.rtti().kind, RttiKind::Struct) => { + let mut values = Vec::try_with_capacity(value.len())?; + + for value in value.borrow_ref()?.iter() { + values.try_push(value.clone())?; + } + + Ok(Self { + rtti: value.rtti().clone(), + values, + }) + } + value => Err(RuntimeError::expected_struct(value.type_info())), + } + } +} + +impl DynamicStruct { + /// Get a value from the dynamic struct. + pub fn get(&self, key: &Q) -> Option<&Value> + where + Box: Borrow, + Q: Hash + Eq + ?Sized, + { + let index = *self.rtti.fields.get(key)?; + self.values.get(index) + } + + /// Get human readable type information for the dynamic struct. + pub fn type_info(&self) -> TypeInfo { + self.rtti.clone().type_info() + } +} diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index a71839da2..7c9730c00 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -228,7 +228,7 @@ impl FormatSpec { break 'fallback; } }, - ReprRef::Mutable(..) => { + ReprRef::Dynamic(..) => { break 'fallback; } ReprRef::Any(value) => match value.type_hash() { @@ -272,7 +272,7 @@ impl FormatSpec { break 'fallback; } }, - ReprRef::Mutable(..) => { + ReprRef::Dynamic(..) => { break 'fallback; } ReprRef::Any(value) => match value.type_hash() { diff --git a/crates/rune/src/runtime/function.rs b/crates/rune/src/runtime/function.rs index ded84a248..2891292c7 100644 --- a/crates/rune/src/runtime/function.rs +++ b/crates/rune/src/runtime/function.rs @@ -15,8 +15,8 @@ use crate::Any; use crate::Hash; use super::{ - Args, Call, ConstValue, Formatter, FromValue, FunctionHandler, GuardedArgs, InstAddress, - Output, OwnedTuple, Rtti, RuntimeContext, RuntimeError, Stack, Unit, Value, VariantRtti, Vm, + Args, Call, ConstValue, Dynamic, Formatter, FromValue, FunctionHandler, GuardedArgs, + InstAddress, Output, OwnedTuple, Rtti, RuntimeContext, RuntimeError, Stack, Unit, Value, Vm, VmCall, VmErrorKind, VmHalt, VmResult, }; @@ -255,16 +255,6 @@ impl Function { Self(FunctionImpl::from_tuple_struct(rtti, args)) } - /// Create a function pointer that constructs a empty variant. - pub(crate) fn from_unit_variant(rtti: Arc) -> Self { - Self(FunctionImpl::from_unit_variant(rtti)) - } - - /// Create a function pointer that constructs a tuple variant. - pub(crate) fn from_tuple_variant(rtti: Arc, args: usize) -> Self { - Self(FunctionImpl::from_tuple_variant(rtti, args)) - } - /// Type [Hash][struct@Hash] of the underlying function. /// /// # Examples @@ -581,16 +571,6 @@ where let (args, _guard) = vm_try!(unsafe { args.guarded_into_vec() }); vm_try!(Value::tuple_struct(tuple.rtti.clone(), args)) } - Inner::FnUnitVariant(unit) => { - vm_try!(check_args(args.count(), 0)); - vm_try!(Value::unit_variant(unit.rtti.clone())) - } - Inner::FnTupleVariant(tuple) => { - vm_try!(check_args(args.count(), tuple.args)); - // SAFETY: We don't let the guard outlive the value. - let (args, _guard) = vm_try!(unsafe { args.guarded_into_vec() }); - vm_try!(Value::tuple_variant(tuple.rtti.clone(), args)) - } }; VmResult::Ok(vm_try!(T::from_value(value))) @@ -667,30 +647,9 @@ where vm_try!(check_args(args, tuple.args)); let seq = vm_try!(vm.stack().slice_at(addr, args)); - let seq = vm_try!(seq.iter().cloned().try_collect()); - - vm_try!(out.store(vm.stack_mut(), || { - Value::tuple_struct(tuple.rtti.clone(), seq) - })); - - None - } - Inner::FnUnitVariant(tuple) => { - vm_try!(check_args(args, 0)); - vm_try!(out.store(vm.stack_mut(), || Value::unit_variant(tuple.rtti.clone()))); - None - } - Inner::FnTupleVariant(tuple) => { - vm_try!(check_args(args, tuple.args)); - - let seq = vm_try!(vm.stack().slice_at(addr, args)); - let seq = vm_try!(seq.iter().cloned().try_collect()); - - vm_try!(out.store(vm.stack_mut(), || Value::tuple_variant( - tuple.rtti.clone(), - seq - ))); - + let data = seq.iter().cloned(); + let value = vm_try!(Dynamic::new(tuple.rtti.clone(), data)); + vm_try!(out.store(vm.stack_mut(), value)); None } }; @@ -765,20 +724,6 @@ where } } - /// Create a function pointer that constructs a empty variant. - pub(crate) fn from_unit_variant(rtti: Arc) -> Self { - Self { - inner: Inner::FnUnitVariant(FnUnitVariant { rtti }), - } - } - - /// Create a function pointer that constructs a tuple variant. - pub(crate) fn from_tuple_variant(rtti: Arc, args: usize) -> Self { - Self { - inner: Inner::FnTupleVariant(FnTupleVariant { rtti, args }), - } - } - #[inline] fn type_hash(&self) -> Hash { match &self.inner { @@ -786,10 +731,8 @@ where *hash } Inner::FnClosureOffset(fco) => fco.fn_offset.hash, - Inner::FnUnitStruct(func) => func.rtti.hash, - Inner::FnTupleStruct(func) => func.rtti.hash, - Inner::FnUnitVariant(func) => func.rtti.hash, - Inner::FnTupleVariant(func) => func.rtti.hash, + Inner::FnUnitStruct(func) => func.rtti.type_hash(), + Inner::FnTupleStruct(func) => func.rtti.type_hash(), } } } @@ -814,8 +757,6 @@ impl FunctionImpl { Inner::FnOffset(inner) => Inner::FnOffset(inner), Inner::FnUnitStruct(inner) => Inner::FnUnitStruct(inner), Inner::FnTupleStruct(inner) => Inner::FnTupleStruct(inner), - Inner::FnUnitVariant(inner) => Inner::FnUnitVariant(inner), - Inner::FnTupleVariant(inner) => Inner::FnTupleVariant(inner), }; Ok(FunctionImpl { inner }) @@ -844,12 +785,6 @@ impl fmt::Debug for Function { Inner::FnTupleStruct(tuple) => { write!(f, "tuple {}", tuple.rtti.item)?; } - Inner::FnUnitVariant(empty) => { - write!(f, "variant empty {}", empty.rtti.item)?; - } - Inner::FnTupleVariant(tuple) => { - write!(f, "variant tuple {}", tuple.rtti.item)?; - } } Ok(()) @@ -875,10 +810,6 @@ enum Inner { FnUnitStruct(FnUnitStruct), /// Constructor for a tuple. FnTupleStruct(FnTupleStruct), - /// Constructor for an empty variant. - FnUnitVariant(FnUnitVariant), - /// Constructor for a tuple variant. - FnTupleVariant(FnTupleVariant), } impl TryClone for Inner @@ -892,8 +823,6 @@ where Inner::FnClosureOffset(inner) => Inner::FnClosureOffset(inner.try_clone()?), Inner::FnUnitStruct(inner) => Inner::FnUnitStruct(inner.clone()), Inner::FnTupleStruct(inner) => Inner::FnTupleStruct(inner.clone()), - Inner::FnUnitVariant(inner) => Inner::FnUnitVariant(inner.clone()), - Inner::FnTupleVariant(inner) => Inner::FnTupleVariant(inner.clone()), }) } } @@ -1029,20 +958,6 @@ struct FnTupleStruct { args: usize, } -#[derive(Debug, Clone, TryClone)] -struct FnUnitVariant { - /// Runtime information fo variant. - rtti: Arc, -} - -#[derive(Debug, Clone, TryClone)] -struct FnTupleVariant { - /// Runtime information fo variant. - rtti: Arc, - /// The number of arguments the tuple takes. - args: usize, -} - impl FromValue for SyncFunction { #[inline] fn from_value(value: Value) -> Result { diff --git a/crates/rune/src/runtime/hasher.rs b/crates/rune/src/runtime/hasher.rs index 34a7183c3..ee5300644 100644 --- a/crates/rune/src/runtime/hasher.rs +++ b/crates/rune/src/runtime/hasher.rs @@ -28,16 +28,6 @@ impl Hasher { self.hasher.write(string.as_bytes()); } - /// Hash an 64-bit float. - /// - /// You should ensure that the float is normal per the [`f64::is_normal`] - /// function before hashing it, since otherwise equality tests against the - /// float won't work as intended. Otherwise, know what you're doing. - pub(crate) fn write_f64(&mut self, value: f64) { - let bits = value.to_bits(); - self.hasher.write_u64(bits); - } - /// Construct a hash. pub fn finish(&self) -> u64 { self.hasher.finish() diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index 1af7bbbe7..8eda20ea2 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -664,27 +664,6 @@ pub enum Inst { /// Where to write the constructed struct. out: Output, }, - /// Construct a push an object variant of the given type onto the stack. The - /// number of elements in the object are determined the slot of the object - /// keys `slot` and are popped from the stack. - /// - /// For each element, a value is popped corresponding to the object key. - /// - /// # Operation - /// - /// ```text - /// - /// => - /// ``` - #[musli(packed)] - StructVariant { - /// The address to load fields from. - addr: InstAddress, - /// The type hash of the object variant to construct. - hash: Hash, - /// Where to write the constructed variant. - out: Output, - }, /// Load a literal string from a static string slot. /// /// # Operation diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index bf8c8d6a9..4dbce9e37 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -9,6 +9,9 @@ mod macros; mod steps_between; use self::steps_between::StepsBetween; +mod dynamic; +pub use self::dynamic::{DynamicEmpty, DynamicStruct, DynamicTuple}; + mod access; pub use self::access::AccessError; pub(crate) use self::access::{Access, AccessErrorKind, RawAccessGuard, Snapshot}; @@ -131,9 +134,6 @@ mod r#ref; use self::r#ref::RefVtable; pub use self::r#ref::{Mut, RawAnyGuard, Ref}; -mod shared; -pub(crate) use self::shared::Shared; - mod stack; pub(crate) use self::stack::Pair; pub use self::stack::{Memory, SliceError, Stack, StackError}; @@ -163,12 +163,9 @@ pub use self::unit::{Unit, UnitStorage}; mod value; pub use self::value::{ Accessor, EmptyStruct, Inline, RawValueGuard, Rtti, Struct, TupleStruct, TypeValue, Value, - ValueMutGuard, ValueRefGuard, VariantRtti, + ValueMutGuard, ValueRefGuard, }; -pub(crate) use self::value::{Mutable, ReprMut, ReprOwned, ReprRef}; - -mod variant; -pub use self::variant::{Variant, VariantData}; +pub(crate) use self::value::{Dynamic, DynamicTakeError, ReprMut, ReprOwned, ReprRef, RttiKind}; pub mod slice; diff --git a/crates/rune/src/runtime/range.rs b/crates/rune/src/runtime/range.rs index c13acfe84..cf5acecd4 100644 --- a/crates/rune/src/runtime/range.rs +++ b/crates/rune/src/runtime/range.rs @@ -108,8 +108,8 @@ impl Range { } (start, end) => { return VmResult::err(VmErrorKind::UnsupportedIterRange { - start: vm_try!(start.type_info()), - end: vm_try!(end.type_info()), + start: start.type_info(), + end: end.type_info(), }) } }; diff --git a/crates/rune/src/runtime/range_from.rs b/crates/rune/src/runtime/range_from.rs index 26a2faa2f..ab33ebb77 100644 --- a/crates/rune/src/runtime/range_from.rs +++ b/crates/rune/src/runtime/range_from.rs @@ -100,7 +100,7 @@ impl RangeFrom { } start => { return VmResult::err(VmErrorKind::UnsupportedIterRangeFrom { - start: vm_try!(start.type_info()), + start: start.type_info(), }) } }; diff --git a/crates/rune/src/runtime/range_inclusive.rs b/crates/rune/src/runtime/range_inclusive.rs index a551f6146..6b3c32763 100644 --- a/crates/rune/src/runtime/range_inclusive.rs +++ b/crates/rune/src/runtime/range_inclusive.rs @@ -109,8 +109,8 @@ impl RangeInclusive { } (start, end) => { return VmResult::err(VmErrorKind::UnsupportedIterRangeInclusive { - start: vm_try!(start.type_info()), - end: vm_try!(end.type_info()), + start: start.type_info(), + end: end.type_info(), }) } }; diff --git a/crates/rune/src/runtime/shared.rs b/crates/rune/src/runtime/shared.rs deleted file mode 100644 index 6e6f1755c..000000000 --- a/crates/rune/src/runtime/shared.rs +++ /dev/null @@ -1,332 +0,0 @@ -use core::cell::{Cell, UnsafeCell}; -use core::fmt; -use core::mem::{transmute, ManuallyDrop}; -use core::ptr::{self, NonNull}; - -use crate::alloc::prelude::*; -use crate::alloc::{self, Box}; - -use super::{ - Access, AccessError, BorrowMut, BorrowRef, Mut, RawAnyGuard, Ref, RefVtable, Snapshot, -}; - -/// A shared value. -#[repr(transparent)] -pub(crate) struct Shared { - inner: NonNull>, -} - -impl Shared { - /// Construct a new shared value. - pub(crate) fn new(data: T) -> alloc::Result { - let shared = SharedBox { - access: Access::new(), - count: Cell::new(1), - data: data.into(), - }; - - let inner = Box::leak(Box::try_new(shared)?); - - Ok(Self { - inner: inner.into(), - }) - } - - /// Test if the value is sharable. - pub(crate) fn is_readable(&self) -> bool { - // Safety: Since we have a reference to this shared, we know that the - // inner is available. - unsafe { self.inner.as_ref().access.is_shared() } - } - - /// Test if the value is exclusively accessible. - pub(crate) fn is_writable(&self) -> bool { - // Safety: Since we have a reference to this shared, we know that the - // inner is available. - unsafe { self.inner.as_ref().access.is_exclusive() } - } - - /// Get access snapshot of shared value. - pub(crate) fn snapshot(&self) -> Snapshot { - unsafe { self.inner.as_ref().access.snapshot() } - } - - /// Take the interior value, if we have exlusive access to it and there - /// are no other live exlusive or shared references. - /// - /// A value that has been taken can no longer be accessed. - pub(crate) fn take(self) -> Result { - // Safety: We know that interior value is alive since this container is - // alive. - // - // Appropriate access is checked when constructing the guards. - unsafe { - let inner = self.inner.as_ref(); - - // Try to take the interior value, this should *only* work if the - // access is exclusively available. - inner.access.try_take()?; - - // Read the pointer out without dropping the inner structure. - // The data field will be invalid at this point, which should be - // flagged through a `taken` access flag. - // - // Future access is forever prevented since we never release - // the access (see above). - Ok(ptr::read(inner.data.get())) - } - } - - /// Get a reference to the interior value while checking for shared access - /// that holds onto a reference count of the inner value. - /// - /// This prevents other exclusive accesses from being performed while the - /// guard returned from this function is live. - pub(crate) fn into_ref(self) -> Result, AccessError> { - // Safety: We know that interior value is alive since this container is - // alive. - // - // Appropriate access is checked when constructing the guards. - unsafe { - self.inner.as_ref().access.try_shared()?; - let this = ManuallyDrop::new(self); - Ok(ref_from_shared(this.inner)) - } - } - - /// Get a reference to the interior value while checking for exclusive - /// access that holds onto a reference count of the inner value. - /// - /// This prevents other exclusive and shared accesses from being performed - /// while the guard returned from this function is live. - pub(crate) fn into_mut(self) -> Result, AccessError> { - // Safety: We know that interior value is alive since this container is - // alive. - // - // Appropriate access is checked when constructing the guards. - unsafe { - self.inner.as_ref().access.try_exclusive()?; - let this = ManuallyDrop::new(self); - Ok(mut_from_shared(this.inner)) - } - } -} - -impl Shared { - /// Get a reference to the interior value while checking for shared access. - /// - /// This prevents other exclusive accesses from being performed while the - /// guard returned from this function is live. - pub(crate) fn borrow_ref(&self) -> Result, AccessError> { - // Safety: We know that interior value is alive since this container is - // alive. - // - // Appropriate access is checked when constructing the guards. - unsafe { - let inner = self.inner.as_ref(); - let guard = inner.access.shared()?; - - Ok(BorrowRef::new( - NonNull::new_unchecked(inner.data.get()), - guard.into_raw(), - )) - } - } - - /// Get a reference to the interior value while checking for exclusive access. - /// - /// This prevents other shared or exclusive accesses from being performed - /// while the guard returned from this function is live. - pub(crate) fn borrow_mut(&self) -> Result, AccessError> { - // Safety: We know that interior value is alive since this container is - // alive. - // - // Appropriate access is checked when constructing the guards. - unsafe { - let inner = self.inner.as_ref(); - let guard = inner.access.exclusive()?; - - Ok(BorrowMut::new( - NonNull::new_unchecked(inner.data.get()), - guard.into_raw(), - )) - } - } -} - -impl fmt::Pointer for Shared { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.inner.as_ptr(), fmt) - } -} - -impl TryClone for Shared { - #[inline] - fn try_clone(&self) -> alloc::Result { - Ok(self.clone()) - } -} - -impl Clone for Shared { - #[inline] - fn clone(&self) -> Self { - // SAFETY: We know that the inner value is live in this instance. - unsafe { - SharedBox::inc(self.inner); - } - - Self { inner: self.inner } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - if ptr::eq(self.inner.as_ptr(), source.inner.as_ptr()) { - return; - } - - // SAFETY: We know that the inner value is live in both instances. - unsafe { - SharedBox::dec(self.inner); - SharedBox::inc(source.inner); - } - - self.inner = source.inner; - } -} - -impl Drop for Shared { - fn drop(&mut self) { - unsafe { - SharedBox::dec(self.inner); - } - } -} - -impl fmt::Debug for Shared -where - T: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - // Safety: by virtue of holding onto a shared we can safely access - // `inner` because it must outlive any `Shared` instances. - unsafe { - let inner = self.inner.as_ref(); - - if !inner.access.is_shared() { - write!(fmt, "*not accessible*") - } else { - write!(fmt, "{:?}", &&*inner.data.get()) - } - } - } -} - -/// The boxed internals of [Shared]. -#[repr(C)] -struct SharedBox { - /// The access of the shared data. - access: Access, - /// The number of strong references to the shared data. - count: Cell, - /// The value being held. Guarded by the `access` field to determine if it - /// can be access shared or exclusively. - data: UnsafeCell, -} - -impl SharedBox { - /// Increment the reference count of the inner value. - unsafe fn inc(this: NonNull) { - let count_ref = &*ptr::addr_of!((*this.as_ptr()).count); - let count = count_ref.get(); - - debug_assert_ne!( - count, 0, - "Reference count of zero should only happen if Shared is incorrectly implemented" - ); - - if count == usize::MAX { - crate::alloc::abort(); - } - - count_ref.set(count + 1); - } - - /// Decrement the reference count in inner, and free the underlying data if - /// it has reached zero. - /// - /// # Safety - /// - /// ProtocolCaller needs to ensure that `this` is a valid pointer. - unsafe fn dec(this: NonNull) -> bool { - let count_ref = &*ptr::addr_of!((*this.as_ptr()).count); - - let count = count_ref.get(); - - debug_assert_ne!( - count, 0, - "Reference count of zero should only happen if Shared is incorrectly implemented" - ); - - let count = count - 1; - count_ref.set(count); - - if count != 0 { - return false; - } - - let this = Box::from_raw_in(this.as_ptr(), rune_alloc::alloc::Global); - - if this.access.is_taken() { - // NB: This prevents the inner `T` from being dropped in case it - // has already been taken (as indicated by `is_taken`). - // - // If it has been taken, the shared box contains invalid memory. - let this = transmute::>, Box>>>(this); - drop(this); - } else { - // NB: At the point of the final drop, no on else should be using - // this. - debug_assert!( - this.access.is_exclusive(), - "expected exclusive, but was: {:?}", - this.access - ); - } - - true - } -} - -unsafe fn drop_shared(data: NonNull<()>) { - let data = data.cast::>(); - data.as_ref().access.release(); - SharedBox::dec(data); -} - -unsafe fn ref_from_shared(data: NonNull>) -> Ref { - let value = &*ptr::addr_of!((*data.as_ptr()).data); - let value = NonNull::new_unchecked(value.get()).cast(); - - let guard = RawAnyGuard::new( - data.cast(), - &RefVtable { - drop: drop_shared::, - }, - ); - - Ref::new(value, guard) -} - -unsafe fn mut_from_shared(data: NonNull>) -> Mut { - let value = &*ptr::addr_of_mut!((*data.as_ptr()).data); - let value = NonNull::new_unchecked(value.get()).cast(); - - let guard = RawAnyGuard::new( - data.cast(), - &RefVtable { - drop: drop_shared::, - }, - ); - - Mut::new(value, guard) -} diff --git a/crates/rune/src/runtime/stack.rs b/crates/rune/src/runtime/stack.rs index fcc070391..3675267c0 100644 --- a/crates/rune/src/runtime/stack.rs +++ b/crates/rune/src/runtime/stack.rs @@ -609,6 +609,7 @@ impl TryClone for Stack { } impl TryFromIteratorIn for Stack { + #[inline] fn try_from_iter_in>( iter: T, alloc: Global, diff --git a/crates/rune/src/runtime/tests.rs b/crates/rune/src/runtime/tests.rs index 0c8a2a823..aa395a9fd 100644 --- a/crates/rune/src/runtime/tests.rs +++ b/crates/rune/src/runtime/tests.rs @@ -13,7 +13,7 @@ use crate::alloc::prelude::*; use crate::support::Result; use crate::Any; -use super::{Access, AnyObj, Bytes, Mut, Ref, Shared, Tuple, TypeHash, Value, VmResult}; +use super::{Access, AnyObj, Bytes, Tuple, TypeHash, Value, VmResult}; #[derive(Debug, PartialEq, Eq, Any)] struct Thing(u32); @@ -89,207 +89,6 @@ fn test_from_mut() -> Result<()> { Ok(()) } -#[test] -fn shared_take() -> crate::support::Result<()> { - let a = Shared::new(Count(0))?; - let b = a.clone(); - - { - let mut a = a.borrow_mut()?; - // NB: this is prevented since we have a live reference. - assert!(b.take().is_err()); - a.0 += 1; - } - - let a = a.take()?; - assert_eq!(a.0, 1); - Ok(()) -} - -#[test] -fn shared_borrow_ref() -> crate::support::Result<()> { - let a = Shared::new(Count(0))?; - - a.borrow_mut()?.0 += 1; - - { - let a_ref = a.borrow_ref()?; - assert_eq!(a_ref.0, 1); - assert!(a.borrow_mut().is_err()); - assert!(a.borrow_ref().is_ok()); - } - - let mut a = a.borrow_mut()?; - a.0 += 1; - assert_eq!(a.0, 2); - Ok(()) -} - -#[test] -fn shared_borrow_mut() -> crate::support::Result<()> { - let a = Shared::new(Count(0))?; - - { - let mut a_mut = a.borrow_mut()?; - a_mut.0 += 1; - assert_eq!(a_mut.0, 1); - assert!(a.borrow_ref().is_err()); - } - - let a = a.borrow_ref()?; - assert_eq!(a.0, 1); - Ok(()) -} - -#[test] -fn shared_into_mut_raw() -> crate::support::Result<()> { - let value = Shared::new(Count(42))?; - - let (mut ptr, guard) = Mut::into_raw(value.clone().into_mut()?); - - assert_eq!(value.snapshot().shared(), 0); - assert!(value.snapshot().is_exclusive()); - - // SAFETY: The guard is held. - unsafe { - assert_eq!(*ptr.as_ref(), Count(42)); - *ptr.as_mut() = Count(43); - } - - drop(guard); - - assert_eq!(value.snapshot().shared(), 0); - assert!(!value.snapshot().is_exclusive()); - - assert_eq!(*value.borrow_ref()?, Count(43)); - Ok(()) -} - -#[test] -fn shared_into_ref() -> crate::support::Result<()> { - let a = Shared::new(Count(0))?; - let b = a.clone(); - - b.borrow_mut()?.0 += 1; - - { - // Consumes `a`. - let a = a.into_ref()?; - assert_eq!(a.0, 1); - assert!(b.borrow_mut().is_err()); - } - - let mut b = b.borrow_mut()?; - b.0 += 1; - assert_eq!(b.0, 2); - Ok(()) -} - -#[test] -fn shared_into_ref_map() -> crate::support::Result<()> { - let value = Shared::>::new(try_vec![1, 2, 3, 4])?; - - let values = Ref::map(value.clone().into_ref()?, |value| &value[2..]); - - assert_eq!(value.snapshot().shared(), 1); - assert!(!value.snapshot().is_exclusive()); - - assert_eq!(values.len(), 2); - assert_eq!(values.as_ref(), &[3, 4]); - - drop(values); - - assert_eq!(value.snapshot().shared(), 0); - assert!(!value.snapshot().is_exclusive()); - - assert_eq!(*value.borrow_ref()?, [1, 2, 3, 4]); - Ok(()) -} - -#[test] -fn shared_into_ref_raw() -> crate::support::Result<()> { - let value = Shared::new(Count(42))?; - - let (ptr, guard) = Ref::into_raw(value.clone().into_ref()?); - - assert_eq!(value.snapshot().shared(), 1); - assert!(!value.snapshot().is_exclusive()); - - // SAFETY: The guard is held. - unsafe { - assert_eq!(*ptr.as_ref(), Count(42)); - } - - drop(guard); - - assert_eq!(value.snapshot().shared(), 0); - assert!(!value.snapshot().is_exclusive()); - - assert_eq!(*value.borrow_ref()?, Count(42)); - Ok(()) -} - -#[test] -fn shared_into_mut() -> crate::support::Result<()> { - let a = Shared::new(Count(0))?; - let b = a.clone(); - - { - // Consumes `a`. - let mut a = a.into_mut().unwrap(); - a.0 += 1; - - assert!(b.borrow_ref().is_err()); - } - - assert_eq!(b.borrow_ref().unwrap().0, 1); - Ok(()) -} - -#[test] -fn shared_is_readable() -> crate::support::Result<()> { - let shared = Shared::new(1u32)?; - assert!(shared.is_readable()); - - { - let _guard = shared.borrow_ref()?; - assert!(shared.is_readable()); // Note: still readable. - } - - { - let _guard = shared.borrow_mut()?; - assert!(!shared.is_readable()); - } - - assert!(shared.is_readable()); - Ok(()) -} - -#[test] -fn shared_is_writable_take() -> crate::support::Result<()> { - let shared = Shared::new(1u32)?; - let shared2 = shared.clone(); - assert!(shared.is_readable()); - shared.take()?; - assert!(!shared2.is_readable()); - assert!(shared2.take().is_err()); - Ok(()) -} - -#[test] -fn shared_is_writable() -> crate::support::Result<()> { - let shared = Shared::new(1u32)?; - assert!(shared.is_writable()); - - { - let _guard = shared.borrow_ref()?; - assert!(!shared.is_writable()); - } - - assert!(shared.is_writable()); - Ok(()) -} - #[test] fn ensure_future_dropped_poll() -> crate::support::Result<()> { use crate::runtime::Future; diff --git a/crates/rune/src/runtime/tuple.rs b/crates/rune/src/runtime/tuple.rs index 81ae1745c..0337c683c 100644 --- a/crates/rune/src/runtime/tuple.rs +++ b/crates/rune/src/runtime/tuple.rs @@ -8,6 +8,7 @@ use crate::alloc::alloc::Global; use crate::alloc::borrow::TryToOwned; use crate::alloc::clone::TryClone; use crate::alloc::fmt::TryWrite; +use crate::alloc::iter::{IteratorExt, TryFromIteratorIn}; use crate::alloc::{self, Box}; use crate::Any; @@ -345,6 +346,18 @@ impl TryFrom<::rust_alloc::boxed::Box<[ConstValue]>> for OwnedTuple { } } +impl TryFromIteratorIn for OwnedTuple { + #[inline] + fn try_from_iter_in>( + iter: T, + alloc: Global, + ) -> alloc::Result { + Ok(Self { + inner: iter.into_iter().try_collect_in(alloc)?, + }) + } +} + macro_rules! impl_tuple { // Skip conflicting implementation with `()`. (0) => { diff --git a/crates/rune/src/runtime/type_info.rs b/crates/rune/src/runtime/type_info.rs index 1ff64cdc2..122da15c8 100644 --- a/crates/rune/src/runtime/type_info.rs +++ b/crates/rune/src/runtime/type_info.rs @@ -9,16 +9,14 @@ use crate::{Any, TypeHash}; use ::rust_alloc::sync::Arc; -use super::{Rtti, VariantRtti}; +use super::Rtti; #[derive(Debug, TryClone, PartialEq, Eq)] enum TypeInfoKind { /// Reference to an external type. Any(AnyTypeInfo), /// A named type. - Typed(Arc), - /// A variant. - Variant(Arc), + Runtime(Arc), } /// Diagnostical type information for a given type. @@ -66,21 +64,15 @@ impl TypeInfo { } #[inline] - pub(crate) const fn typed(rtti: Arc) -> Self { - Self::new(TypeInfoKind::Typed(rtti)) - } - - #[inline] - pub(crate) const fn variant(rtti: Arc) -> Self { - Self::new(TypeInfoKind::Variant(rtti)) + pub(crate) const fn rtti(rtti: Arc) -> Self { + Self::new(TypeInfoKind::Runtime(rtti)) } #[cfg(feature = "emit")] pub(crate) fn type_hash(&self) -> Hash { match &self.kind { - TypeInfoKind::Typed(ty) => ty.hash, - TypeInfoKind::Variant(ty) => ty.hash, TypeInfoKind::Any(ty) => ty.hash, + TypeInfoKind::Runtime(ty) => ty.type_hash(), } } } @@ -95,10 +87,7 @@ impl fmt::Debug for TypeInfo { impl fmt::Display for TypeInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.kind { - TypeInfoKind::Typed(rtti) => { - write!(f, "{}", rtti.item)?; - } - TypeInfoKind::Variant(rtti) => { + TypeInfoKind::Runtime(rtti) => { write!(f, "{}", rtti.item)?; } TypeInfoKind::Any(info) => { diff --git a/crates/rune/src/runtime/type_of.rs b/crates/rune/src/runtime/type_of.rs index fcf308690..8fb723ff3 100644 --- a/crates/rune/src/runtime/type_of.rs +++ b/crates/rune/src/runtime/type_of.rs @@ -2,7 +2,7 @@ use crate::alloc; use crate::compile::meta; use crate::Hash; -use super::{AnyTypeInfo, Mut, Ref, Shared, TypeInfo}; +use super::{AnyTypeInfo, Mut, Ref, TypeInfo}; /// Static type hash for a given type. /// @@ -134,16 +134,6 @@ where } } -impl MaybeTypeOf for Shared -where - T: ?Sized + MaybeTypeOf, -{ - #[inline] - fn maybe_type_of() -> alloc::Result { - T::maybe_type_of() - } -} - /// Blanket implementation for references. impl TypeOf for &T where @@ -195,20 +185,3 @@ where const PARAMETERS: Hash = T::PARAMETERS; const STATIC_TYPE_INFO: AnyTypeInfo = T::STATIC_TYPE_INFO; } - -/// Blanket implementation for owned shared values. -impl TypeHash for Shared -where - T: ?Sized + TypeHash, -{ - const HASH: Hash = T::HASH; -} - -/// Blanket implementation for owned shared values. -impl TypeOf for Shared -where - T: ?Sized + TypeOf, -{ - const PARAMETERS: Hash = T::PARAMETERS; - const STATIC_TYPE_INFO: AnyTypeInfo = T::STATIC_TYPE_INFO; -} diff --git a/crates/rune/src/runtime/unit.rs b/crates/rune/src/runtime/unit.rs index 4cddf814f..ff94cf6a8 100644 --- a/crates/rune/src/runtime/unit.rs +++ b/crates/rune/src/runtime/unit.rs @@ -18,9 +18,7 @@ use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, Box, String, Vec}; use crate::hash; -use crate::runtime::{ - Call, ConstValue, DebugInfo, Inst, Rtti, StaticString, VariantRtti, VmError, VmErrorKind, -}; +use crate::runtime::{Call, ConstValue, DebugInfo, Inst, Rtti, StaticString, VmError, VmErrorKind}; use crate::Hash; pub use self::storage::{ArrayUnit, EncodeError, UnitEncoder, UnitStorage}; @@ -72,14 +70,13 @@ pub struct Logic { static_object_keys: Vec>, /// Runtime information for types. rtti: hash::Map>, - /// Runtime information for variants. - variant_rtti: hash::Map>, /// Named constants constants: hash::Map, } impl Unit { /// Constructs a new unit from a pair of data and debug info. + #[inline] pub fn from_parts(data: Logic, debug: Option) -> alloc::Result { Ok(Self { logic: data, @@ -89,6 +86,7 @@ impl Unit { /// Construct a new unit with the given content. #[allow(clippy::too_many_arguments)] + #[inline] pub(crate) fn new( storage: S, functions: hash::Map, @@ -96,7 +94,6 @@ impl Unit { static_bytes: Vec>, static_object_keys: Vec>, rtti: hash::Map>, - variant_rtti: hash::Map>, debug: Option>, constants: hash::Map, ) -> Self { @@ -108,7 +105,6 @@ impl Unit { static_bytes, static_object_keys, rtti, - variant_rtti, constants, }, debug, @@ -116,35 +112,40 @@ impl Unit { } /// Access unit data. + #[inline] pub fn logic(&self) -> &Logic { &self.logic } /// Access debug information for the given location if it is available. + #[inline] pub fn debug_info(&self) -> Option<&DebugInfo> { - let debug = self.debug.as_ref()?; - Some(&**debug) + Some(&**self.debug.as_ref()?) } /// Get raw underlying instructions storage. + #[inline] pub(crate) fn instructions(&self) -> &S { &self.logic.storage } /// Iterate over all static strings in the unit. #[cfg(feature = "cli")] + #[inline] pub(crate) fn iter_static_strings(&self) -> impl Iterator> + '_ { self.logic.static_strings.iter() } /// Iterate over all constants in the unit. #[cfg(feature = "cli")] + #[inline] pub(crate) fn iter_constants(&self) -> impl Iterator + '_ { self.logic.constants.iter() } /// Iterate over all static object keys in the unit. #[cfg(feature = "cli")] + #[inline] pub(crate) fn iter_static_object_keys(&self) -> impl Iterator + '_ { use core::iter; @@ -158,11 +159,13 @@ impl Unit { /// Iterate over dynamic functions. #[cfg(feature = "cli")] + #[inline] pub(crate) fn iter_functions(&self) -> impl Iterator + '_ { self.logic.functions.iter().map(|(h, f)| (*h, f)) } /// Lookup the static string by slot, if it exists. + #[inline] pub(crate) fn lookup_string(&self, slot: usize) -> Result<&Arc, VmError> { Ok(self .logic @@ -172,6 +175,7 @@ impl Unit { } /// Lookup the static byte string by slot, if it exists. + #[inline] pub(crate) fn lookup_bytes(&self, slot: usize) -> Result<&[u8], VmError> { Ok(self .logic @@ -182,6 +186,7 @@ impl Unit { } /// Lookup the static object keys by slot, if it exists. + #[inline] pub(crate) fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> { self.logic .static_object_keys @@ -190,21 +195,19 @@ impl Unit { } /// Lookup run-time information for the given type hash. + #[inline] pub(crate) fn lookup_rtti(&self, hash: Hash) -> Option<&Arc> { self.logic.rtti.get(&hash) } - /// Lookup variant run-time information for the given variant hash. - pub(crate) fn lookup_variant_rtti(&self, hash: Hash) -> Option<&Arc> { - self.logic.variant_rtti.get(&hash) - } - /// Lookup a function in the unit. + #[inline] pub(crate) fn function(&self, hash: Hash) -> Option { self.logic.functions.get(&hash).copied() } /// Lookup a constant from the unit. + #[inline] pub(crate) fn constant(&self, hash: Hash) -> Option<&ConstValue> { self.logic.constants.get(&hash) } @@ -220,6 +223,7 @@ where } /// Get the instruction at the given instruction pointer. + #[inline] pub(crate) fn instruction_at( &self, ip: usize, @@ -229,6 +233,7 @@ where /// Iterate over all instructions in order. #[cfg(feature = "emit")] + #[inline] pub(crate) fn iter_instructions(&self) -> impl Iterator + '_ { self.logic.storage.iter() } @@ -262,18 +267,6 @@ pub(crate) enum UnitFn { /// The number of arguments the tuple takes. args: usize, }, - /// A unit variant of the type identified by the given hash. - UnitVariant { - /// The type hash of the empty variant. - hash: Hash, - }, - /// A tuple variant of the type identified by the given hash. - TupleVariant { - /// The type hash of the variant. - hash: Hash, - /// The number of arguments the tuple takes. - args: usize, - }, } impl TryClone for UnitFn { @@ -303,12 +296,6 @@ impl fmt::Display for UnitFn { Self::TupleStruct { hash, args } => { write!(f, "tuple hash={hash}, args={args}")?; } - Self::UnitVariant { hash } => { - write!(f, "empty-variant hash={hash}")?; - } - Self::TupleVariant { hash, args } => { - write!(f, "tuple-variant hash={hash}, args={args}")?; - } } Ok(()) diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 8b9cf78d6..82823cbbc 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -1,22 +1,28 @@ #[macro_use] mod macros; +#[cfg(test)] +mod tests; + mod inline; pub use self::inline::Inline; mod serde; mod rtti; -pub use self::rtti::{Accessor, Rtti, VariantRtti}; +pub(crate) use self::rtti::RttiKind; +pub use self::rtti::{Accessor, Rtti}; mod data; pub use self::data::{EmptyStruct, Struct, TupleStruct}; +mod dynamic; +pub use self::dynamic::Dynamic; +pub(crate) use self::dynamic::DynamicTakeError; + use core::any; use core::cmp::Ordering; use core::fmt; -#[cfg(feature = "alloc")] -use core::hash::Hasher as _; use core::mem::replace; use core::ptr::NonNull; @@ -26,15 +32,13 @@ use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::{self, String}; use crate::compile::meta; -use crate::runtime; use crate::{Any, Hash, TypeHash}; use super::{ AccessError, AnyObj, AnyObjDrop, BorrowMut, BorrowRef, CallResultOnly, ConstValue, ConstValueKind, DynGuardedArgs, EnvProtocolCaller, Formatter, FromValue, Future, IntoOutput, Iterator, MaybeTypeOf, Mut, Object, OwnedTuple, Protocol, ProtocolCaller, RawAnyObjGuard, Ref, - RuntimeError, Shared, Snapshot, Type, TypeInfo, Variant, Vec, VmErrorKind, VmIntegerRepr, - VmResult, + RuntimeError, Snapshot, Type, TypeInfo, Vec, VmErrorKind, VmIntegerRepr, VmResult, }; #[cfg(feature = "alloc")] use super::{Hasher, Tuple}; @@ -73,13 +77,13 @@ where enum Repr { Empty, Inline(Inline), - Mutable(Shared), + Dynamic(Dynamic, Value>), Any(AnyObj), } pub(crate) enum ReprOwned { Inline(Inline), - Mutable(Mutable), + Dynamic(Dynamic, Value>), Any(AnyObj), } @@ -88,7 +92,7 @@ impl ReprOwned { pub(crate) fn type_info(&self) -> TypeInfo { match self { ReprOwned::Inline(value) => value.type_info(), - ReprOwned::Mutable(value) => value.type_info(), + ReprOwned::Dynamic(value) => value.type_info(), ReprOwned::Any(value) => value.type_info(), } } @@ -96,17 +100,17 @@ impl ReprOwned { pub(crate) enum ReprRef<'a> { Inline(&'a Inline), - Mutable(&'a Shared), + Dynamic(&'a Dynamic, Value>), Any(&'a AnyObj), } impl ReprRef<'_> { #[inline] - pub(crate) fn type_info(&self) -> Result { + pub(crate) fn type_info(&self) -> TypeInfo { match self { - ReprRef::Inline(value) => Ok(value.type_info()), - ReprRef::Mutable(value) => Ok(value.borrow_ref()?.type_info()), - ReprRef::Any(value) => Ok(value.type_info()), + ReprRef::Inline(value) => value.type_info(), + ReprRef::Dynamic(value) => value.type_info(), + ReprRef::Any(value) => value.type_info(), } } } @@ -114,8 +118,8 @@ impl ReprRef<'_> { /// Access the internals of a value mutably. pub(crate) enum ReprMut<'a> { Inline(&'a mut Inline), - Mutable(#[allow(unused)] &'a mut Shared), - Any(#[allow(unused)] &'a mut AnyObj), + Dynamic(#[allow(unused)] &'a Dynamic, Value>), + Any(#[allow(unused)] &'a AnyObj), } /// An entry on the stack. @@ -273,7 +277,7 @@ impl Value { /// Optionally get the snapshot of the value if available. pub(crate) fn snapshot(&self) -> Option { match &self.repr { - Repr::Mutable(value) => Some(value.snapshot()), + Repr::Dynamic(value) => Some(value.snapshot()), Repr::Any(value) => Some(value.snapshot()), _ => None, } @@ -284,7 +288,7 @@ impl Value { match self.repr { Repr::Empty => false, Repr::Inline(..) => true, - Repr::Mutable(ref value) => value.is_writable(), + Repr::Dynamic(ref value) => value.is_writable(), Repr::Any(ref any) => any.is_writable(), } } @@ -294,7 +298,7 @@ impl Value { match &self.repr { Repr::Empty => false, Repr::Inline(..) => true, - Repr::Mutable(ref value) => value.is_readable(), + Repr::Dynamic(ref value) => value.is_readable(), Repr::Any(ref any) => any.is_readable(), } } @@ -396,28 +400,20 @@ impl Value { } pub(crate) fn clone_with(&self, caller: &mut dyn ProtocolCaller) -> VmResult { - 'fallback: { - let value = match vm_try!(self.as_ref()) { - ReprRef::Inline(value) => { - return VmResult::Ok(Self { - repr: Repr::Inline(*value), - }); - } - ReprRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { - Mutable::EmptyStruct(value) => Mutable::EmptyStruct(vm_try!(value.try_clone())), - Mutable::TupleStruct(value) => Mutable::TupleStruct(vm_try!(value.try_clone())), - Mutable::Struct(value) => Mutable::Struct(vm_try!(value.try_clone())), - Mutable::Variant(value) => Mutable::Variant(vm_try!(value.try_clone())), - }, - ReprRef::Any(..) => { - break 'fallback; - } - }; - - return VmResult::Ok(Self { - repr: Repr::Mutable(vm_try!(Shared::new(value))), - }); - }; + match vm_try!(self.as_ref()) { + ReprRef::Inline(value) => { + return VmResult::Ok(Self { + repr: Repr::Inline(*value), + }); + } + ReprRef::Dynamic(value) => { + // TODO: This type of cloning should be deep, not shallow. + return VmResult::Ok(Self { + repr: Repr::Dynamic(value.clone()), + }); + } + ReprRef::Any(..) => {} + } VmResult::Ok(vm_try!(caller.call_protocol_fn( Protocol::CLONE, @@ -448,37 +444,21 @@ impl Value { f: &mut Formatter, caller: &mut dyn ProtocolCaller, ) -> VmResult<()> { - 'fallback: { - let value = match self.repr { - Repr::Empty => { - vm_try!(vm_write!(f, "")); - return VmResult::Ok(()); - } - Repr::Inline(value) => { - vm_try!(vm_write!(f, "{value:?}")); - return VmResult::Ok(()); - } - Repr::Mutable(ref value) => value, - Repr::Any(..) => break 'fallback, - }; - - match &*vm_try!(value.borrow_ref()) { - Mutable::EmptyStruct(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } - Mutable::TupleStruct(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } - Mutable::Struct(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } - Mutable::Variant(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } - }; - - return VmResult::Ok(()); - }; + match &self.repr { + Repr::Empty => { + vm_try!(vm_write!(f, "")); + return VmResult::Ok(()); + } + Repr::Inline(value) => { + vm_try!(vm_write!(f, "{value:?}")); + return VmResult::Ok(()); + } + Repr::Dynamic(ref value) => { + vm_try!(value.debug_fmt_with(f, caller)); + return VmResult::Ok(()); + } + Repr::Any(..) => {} + } // reborrow f to avoid moving it let mut args = DynGuardedArgs::new((&mut *f,)); @@ -494,8 +474,8 @@ impl Value { Repr::Inline(value) => { vm_try!(vm_write!(f, "{value:?}")); } - Repr::Mutable(value) => { - let ty = vm_try!(value.borrow_ref()).type_info(); + Repr::Dynamic(value) => { + let ty = value.type_info(); vm_try!(vm_write!(f, "<{ty} object at {value:p}>")); } Repr::Any(value) => { @@ -576,38 +556,27 @@ impl Value { /// Construct a tuple. pub fn tuple(vec: alloc::Vec) -> alloc::Result { - let data = OwnedTuple::try_from(vec)?; - Value::try_from(data) + Value::try_from(OwnedTuple::try_from(vec)?) } /// Construct an empty. pub fn empty_struct(rtti: Arc) -> VmResult { - VmResult::Ok(vm_try!(Value::try_from(EmptyStruct { rtti }))) + VmResult::Ok(Value::from(vm_try!(Dynamic::new(rtti, [])))) } /// Construct a typed tuple. - pub fn tuple_struct(rtti: Arc, vec: alloc::Vec) -> VmResult { - let data = vm_try!(OwnedTuple::try_from(vec)); - VmResult::Ok(vm_try!(Value::try_from(TupleStruct { rtti, data }))) - } - - /// Construct an empty variant. - pub fn unit_variant(rtti: Arc) -> VmResult { - VmResult::Ok(vm_try!(Value::try_from(Variant::unit(rtti)))) - } - - /// Construct a tuple variant. - pub fn tuple_variant(rtti: Arc, vec: alloc::Vec) -> VmResult { - let data = vm_try!(OwnedTuple::try_from(vec)); - - VmResult::Ok(vm_try!(Value::try_from(Variant::tuple(rtti, data)))) + pub fn tuple_struct( + rtti: Arc, + data: impl IntoIterator, + ) -> VmResult { + VmResult::Ok(Value::from(vm_try!(Dynamic::new(rtti, data)))) } /// Drop the interior value. pub(crate) fn drop(self) -> VmResult<()> { match self.repr { - Repr::Mutable(value) => { - drop(vm_try!(value.take())); + Repr::Dynamic(value) => { + vm_try!(value.drop()); } Repr::Any(value) => { vm_try!(value.drop()); @@ -621,8 +590,8 @@ impl Value { /// Move the interior value. pub(crate) fn move_(self) -> VmResult { match self.repr { - Repr::Mutable(value) => VmResult::Ok(Value { - repr: Repr::Mutable(vm_try!(Shared::new(vm_try!(value.take())))), + Repr::Dynamic(value) => VmResult::Ok(Value { + repr: Repr::Dynamic(vm_try!(value.take())), }), Repr::Any(value) => VmResult::Ok(Value { repr: Repr::Any(vm_try!(value.take())), @@ -664,21 +633,26 @@ impl Value { /// Coerce into type value. #[doc(hidden)] #[inline] - pub fn into_type_value(self) -> Result { - match self.take_repr()? { - ReprOwned::Inline(value) => match value { + pub fn as_type_value(&self) -> Result, RuntimeError> { + match self.as_ref()? { + ReprRef::Inline(value) => match value { Inline::Unit => Ok(TypeValue::Unit), - value => Ok(TypeValue::NotTypedInline(NotTypedInlineValue(value))), + value => Ok(TypeValue::NotTypedInline(NotTypedInline(*value))), }, - ReprOwned::Mutable(value) => match value { - Mutable::EmptyStruct(empty) => Ok(TypeValue::EmptyStruct(empty)), - Mutable::TupleStruct(tuple) => Ok(TypeValue::TupleStruct(tuple)), - Mutable::Struct(object) => Ok(TypeValue::Struct(object)), - Mutable::Variant(object) => Ok(TypeValue::Variant(object)), + ReprRef::Dynamic(value) => match value.rtti().kind { + RttiKind::Empty => Ok(TypeValue::EmptyStruct(EmptyStruct { rtti: value.rtti() })), + RttiKind::Tuple => Ok(TypeValue::TupleStruct(TupleStruct { + rtti: value.rtti(), + data: value.borrow_ref()?, + })), + RttiKind::Struct => Ok(TypeValue::Struct(Struct { + rtti: value.rtti(), + data: value.borrow_ref()?, + })), }, - ReprOwned::Any(value) => match value.type_hash() { - OwnedTuple::HASH => Ok(TypeValue::Tuple(value.downcast()?)), - Object::HASH => Ok(TypeValue::Object(value.downcast()?)), + ReprRef::Any(value) => match value.type_hash() { + OwnedTuple::HASH => Ok(TypeValue::Tuple(value.borrow_ref()?)), + Object::HASH => Ok(TypeValue::Object(value.borrow_ref()?)), _ => Ok(TypeValue::NotTypedAnyObj(NotTypedAnyObj(value))), }, } @@ -689,7 +663,7 @@ impl Value { pub fn into_unit(&self) -> Result<(), RuntimeError> { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(()), - value => Err(RuntimeError::expected::<()>(value.type_info()?)), + value => Err(RuntimeError::expected::<()>(value.type_info())), } } @@ -742,16 +716,6 @@ impl Value { as_type_mut, } - into! { - /// Coerce into [`Struct`] - Struct(Struct), - into_struct_ref, - into_struct_mut, - borrow_struct_ref, - borrow_struct_mut, - into_struct, - } - /// Borrow as a tuple. /// /// This ensures that the value has read access to the underlying value @@ -761,9 +725,7 @@ impl Value { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(BorrowRef::from_static(Tuple::new(&[]))), ReprRef::Inline(value) => Err(RuntimeError::expected::(value.type_info())), - ReprRef::Mutable(value) => Err(RuntimeError::expected::( - value.borrow_ref()?.type_info(), - )), + ReprRef::Dynamic(value) => Err(RuntimeError::expected::(value.type_info())), ReprRef::Any(value) => { let value = value.borrow_ref::()?; let value = BorrowRef::map(value, OwnedTuple::as_ref); @@ -781,9 +743,7 @@ impl Value { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(BorrowMut::from_static(Tuple::new_mut(&mut []))), ReprRef::Inline(value) => Err(RuntimeError::expected::(value.type_info())), - ReprRef::Mutable(value) => Err(RuntimeError::expected::( - value.borrow_ref()?.type_info(), - )), + ReprRef::Dynamic(value) => Err(RuntimeError::expected::(value.type_info())), ReprRef::Any(value) => { let value = value.borrow_mut::()?; let value = BorrowMut::map(value, OwnedTuple::as_mut); @@ -801,9 +761,7 @@ impl Value { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(Tuple::from_boxed(Box::default())), ReprRef::Inline(value) => Err(RuntimeError::expected::(value.type_info())), - ReprRef::Mutable(value) => Err(RuntimeError::expected::( - value.borrow_ref()?.type_info(), - )), + ReprRef::Dynamic(value) => Err(RuntimeError::expected::(value.type_info())), ReprRef::Any(value) => Ok(value.clone().downcast::()?.into_boxed_tuple()), } } @@ -817,9 +775,7 @@ impl Value { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(Ref::from_static(Tuple::new(&[]))), ReprRef::Inline(value) => Err(RuntimeError::expected::(value.type_info())), - ReprRef::Mutable(value) => Err(RuntimeError::expected::( - value.borrow_ref()?.type_info(), - )), + ReprRef::Dynamic(value) => Err(RuntimeError::expected::(value.type_info())), ReprRef::Any(value) => { let value = value.clone().into_ref::()?; let value = Ref::map(value, OwnedTuple::as_ref); @@ -837,9 +793,7 @@ impl Value { match self.as_ref()? { ReprRef::Inline(Inline::Unit) => Ok(Mut::from_static(Tuple::new_mut(&mut []))), ReprRef::Inline(value) => Err(RuntimeError::expected::(value.type_info())), - ReprRef::Mutable(value) => Err(RuntimeError::expected::( - value.borrow_ref()?.type_info(), - )), + ReprRef::Dynamic(value) => Err(RuntimeError::expected::(value.type_info())), ReprRef::Any(value) => { let value = value.clone().into_mut::()?; let value = Mut::map(value, OwnedTuple::as_mut); @@ -856,9 +810,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any_obj(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any_obj( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any_obj(value.type_info())), Repr::Any(value) => Ok(value), } } @@ -912,9 +864,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => { let (ptr, guard) = value.borrow_ref_ptr::()?; let guard = RawValueGuard { guard }; @@ -938,9 +888,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => { let (ptr, guard) = value.borrow_mut_ptr::()?; let guard = RawValueGuard { guard }; @@ -986,9 +934,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => Ok(value.downcast::()?), } } @@ -1022,9 +968,7 @@ impl Value { match &self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => Ok(value.borrow_ref()?), } } @@ -1057,9 +1001,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => Ok(value.into_ref()?), } } @@ -1073,9 +1015,7 @@ impl Value { match &self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => Ok(value.borrow_mut()?), } } @@ -1116,9 +1056,7 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Err(RuntimeError::expected_any::(value.type_info())), - Repr::Mutable(value) => Err(RuntimeError::expected_any::( - value.borrow_ref()?.type_info(), - )), + Repr::Dynamic(value) => Err(RuntimeError::expected_any::(value.type_info())), Repr::Any(value) => Ok(value.into_mut()?), } } @@ -1131,7 +1069,7 @@ impl Value { match &self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Ok(value.type_hash()), - Repr::Mutable(value) => Ok(value.borrow_ref()?.type_hash()), + Repr::Dynamic(value) => Ok(value.type_hash()), Repr::Any(value) => Ok(value.type_hash()), } } @@ -1141,7 +1079,7 @@ impl Value { match &self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => Ok(value.type_info()), - Repr::Mutable(value) => Ok(value.borrow_ref()?.type_info()), + Repr::Dynamic(value) => Ok(value.type_info()), Repr::Any(value) => Ok(value.type_info()), } } @@ -1169,147 +1107,19 @@ impl Value { b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult { - match (vm_try!(self.as_ref()), vm_try!(b.as_ref())) { - (ReprRef::Inline(a), ReprRef::Inline(b)) => { - return VmResult::Ok(vm_try!(a.partial_eq(b))); - } - (ReprRef::Inline(a), b) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_EQ.name, - lhs: a.type_info(), - rhs: vm_try!(b.type_info()), - }); - } - (ReprRef::Mutable(a), ReprRef::Mutable(b)) => { - let a = vm_try!(a.borrow_ref()); - let b = vm_try!(b.borrow_ref()); - - match (&*a, &*b) { - (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - // NB: don't get any future ideas, this must fall through to - // the VmError below since it's otherwise a comparison - // between two incompatible types. - // - // Other than that, all units are equal. - return VmResult::Ok(true); - } - } - (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); - } - } - (Mutable::Struct(a), Mutable::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::partial_eq_with, caller); - } - } - (Mutable::Variant(a), Mutable::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::partial_eq_with(a, b, caller); - } - } - _ => {} - } - } - (ReprRef::Any(value), _) => match value.type_hash() { - runtime::Vec::HASH => { - let vec = vm_try!(value.borrow_ref::()); - return Vec::partial_eq_with(&vec, b.clone(), caller); - } - runtime::OwnedTuple::HASH => { - let tuple = vm_try!(value.borrow_ref::()); - return Vec::partial_eq_with(&tuple, b.clone(), caller); - } - _ => {} - }, - _ => {} - } - - if let CallResultOnly::Ok(value) = vm_try!(caller.try_call_protocol_fn( + self.bin_op_with( + b, + caller, Protocol::PARTIAL_EQ, - self.clone(), - &mut Some((b.clone(),)) - )) { - return VmResult::Ok(vm_try!(<_>::from_value(value))); - } - - err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_EQ.name, - lhs: vm_try!(self.type_info()), - rhs: vm_try!(b.type_info()), - }) - } - - /// Hash the current value. - #[cfg(feature = "alloc")] - pub fn hash(&self, hasher: &mut Hasher) -> VmResult<()> { - self.hash_with(hasher, &mut EnvProtocolCaller) - } - - /// Hash the current value. - #[cfg_attr(feature = "bench", inline(never))] - pub(crate) fn hash_with( - &self, - hasher: &mut Hasher, - caller: &mut dyn ProtocolCaller, - ) -> VmResult<()> { - match vm_try!(self.as_ref()) { - ReprRef::Inline(value) => match value { - Inline::Unsigned(value) => { - hasher.write_u64(*value); - return VmResult::Ok(()); - } - Inline::Signed(value) => { - hasher.write_i64(*value); - return VmResult::Ok(()); + Inline::partial_eq, + |lhs, rhs, caller| { + if lhs.0.variant_hash != rhs.0.variant_hash { + return VmResult::Ok(false); } - // Care must be taken whan hashing floats, to ensure that `hash(v1) - // === hash(v2)` if `eq(v1) === eq(v2)`. Hopefully we accomplish - // this by rejecting NaNs and rectifying subnormal values of zero. - Inline::Float(value) => { - if value.is_nan() { - return VmResult::err(VmErrorKind::IllegalFloatOperation { value: *value }); - } - let zero = *value == 0.0; - hasher.write_f64((zero as u8 as f64) * 0.0 + (!zero as u8 as f64) * *value); - return VmResult::Ok(()); - } - operand => { - return err(VmErrorKind::UnsupportedUnaryOperation { - op: Protocol::HASH.name, - operand: operand.type_info(), - }); - } + Vec::eq_with(lhs.1, rhs.1, Value::partial_eq_with, caller) }, - ReprRef::Any(value) => match value.type_hash() { - Vec::HASH => { - let vec = vm_try!(value.borrow_ref::()); - return Vec::hash_with(&vec, hasher, caller); - } - OwnedTuple::HASH => { - let tuple = vm_try!(value.borrow_ref::()); - return Tuple::hash_with(&tuple, hasher, caller); - } - _ => {} - }, - _ => {} - } - - let mut args = DynGuardedArgs::new((hasher,)); - - if let CallResultOnly::Ok(value) = - vm_try!(caller.try_call_protocol_fn(Protocol::HASH, self.clone(), &mut args)) - { - return VmResult::Ok(vm_try!(<_>::from_value(value))); - } - - err(VmErrorKind::UnsupportedUnaryOperation { - op: Protocol::HASH.name, - operand: vm_try!(self.type_info()), - }) + ) } /// Perform a total equality test between two values. @@ -1331,65 +1141,12 @@ impl Value { /// This is the basis for the eq operation (`==`). #[cfg_attr(feature = "bench", inline(never))] pub(crate) fn eq_with(&self, b: &Value, caller: &mut dyn ProtocolCaller) -> VmResult { - match (vm_try!(self.as_ref()), vm_try!(b.as_ref())) { - (ReprRef::Inline(a), ReprRef::Inline(b)) => { - return a.eq(b); - } - (ReprRef::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::EQ.name, - lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), - }); + self.bin_op_with(b, caller, Protocol::EQ, Inline::eq, |lhs, rhs, caller| { + if lhs.0.variant_hash != rhs.0.variant_hash { + return VmResult::Ok(false); } - (ReprRef::Mutable(a), ReprRef::Mutable(b)) => { - let a = vm_try!(a.borrow_ref()); - let b = vm_try!(b.borrow_ref()); - - match (&*a, &*b) { - (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - // NB: don't get any future ideas, this must fall through to - // the VmError below since it's otherwise a comparison - // between two incompatible types. - // - // Other than that, all units are equal. - return VmResult::Ok(true); - } - } - (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::eq_with, caller); - } - } - (Mutable::Struct(a), Mutable::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::eq_with(&a.data, &b.data, Value::eq_with, caller); - } - } - (Mutable::Variant(a), Mutable::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::eq_with(a, b, caller); - } - } - _ => {} - } - } - _ => {} - } - if let CallResultOnly::Ok(value) = vm_try!(caller.try_call_protocol_fn( - Protocol::EQ, - self.clone(), - &mut Some((b.clone(),)) - )) { - return VmResult::Ok(vm_try!(<_>::from_value(value))); - } - - err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::EQ.name, - lhs: vm_try!(self.type_info()), - rhs: vm_try!(b.type_info()), + Vec::eq_with(lhs.1, rhs.1, Value::eq_with, caller) }) } @@ -1416,66 +1173,21 @@ impl Value { b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult> { - match (vm_try!(self.as_ref()), vm_try!(b.as_ref())) { - (ReprRef::Inline(a), ReprRef::Inline(b)) => { - return VmResult::Ok(vm_try!(a.partial_cmp(b))) - } - (ReprRef::Inline(lhs), rhs) => { - return err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_CMP.name, - lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), - }) - } - (ReprRef::Mutable(a), ReprRef::Mutable(b)) => { - let a = vm_try!(a.borrow_ref()); - let b = vm_try!(b.borrow_ref()); - - match (&*a, &*b) { - (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - // NB: don't get any future ideas, this must fall through to - // the VmError below since it's otherwise a comparison - // between two incompatible types. - // - // Other than that, all units are equal. - return VmResult::Ok(Some(Ordering::Equal)); - } - } - (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::partial_cmp_with(&a.data, &b.data, caller); - } - } - (Mutable::Struct(a), Mutable::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::partial_cmp_with(&a.data, &b.data, caller); - } - } - (Mutable::Variant(a), Mutable::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::partial_cmp_with(a, b, caller); - } - } - _ => {} - } - } - _ => {} - } - - if let CallResultOnly::Ok(value) = vm_try!(caller.try_call_protocol_fn( + self.bin_op_with( + b, + caller, Protocol::PARTIAL_CMP, - self.clone(), - &mut Some((b.clone(),)) - )) { - return VmResult::Ok(vm_try!(<_>::from_value(value))); - } + Inline::partial_cmp, + |lhs, rhs, caller| { + let ord = lhs.0.variant_hash.cmp(&rhs.0.variant_hash); - err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::PARTIAL_CMP.name, - lhs: vm_try!(self.type_info()), - rhs: vm_try!(b.type_info()), - }) + if ord != Ordering::Equal { + return VmResult::Ok(Some(ord)); + } + + Vec::partial_cmp_with(lhs.1, rhs.1, caller) + }, + ) } /// Perform a total ordering comparison between two values. @@ -1501,61 +1213,114 @@ impl Value { b: &Value, caller: &mut dyn ProtocolCaller, ) -> VmResult { - match (vm_try!(self.as_ref()), vm_try!(b.as_ref())) { - (ReprRef::Inline(a), ReprRef::Inline(b)) => return a.cmp(b), - (ReprRef::Mutable(a), ReprRef::Mutable(b)) => { - let a = vm_try!(a.borrow_ref()); - let b = vm_try!(b.borrow_ref()); - - match (&*a, &*b) { - (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - // NB: don't get any future ideas, this must fall through to - // the VmError below since it's otherwise a comparison - // between two incompatible types. - // - // Other than that, all units are equal. - return VmResult::Ok(Ordering::Equal); - } - } - (Mutable::TupleStruct(a), Mutable::TupleStruct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::cmp_with(&a.data, &b.data, caller); - } - } - (Mutable::Struct(a), Mutable::Struct(b)) => { - if a.rtti.hash == b.rtti.hash { - return Vec::cmp_with(&a.data, &b.data, caller); - } - } - (Mutable::Variant(a), Mutable::Variant(b)) => { - if a.rtti().enum_hash == b.rtti().enum_hash { - return Variant::cmp_with(a, b, caller); - } - } - _ => {} + self.bin_op_with(b, caller, Protocol::CMP, Inline::cmp, |lhs, rhs, caller| { + let ord = lhs.0.variant_hash.cmp(&rhs.0.variant_hash); + + if ord != Ordering::Equal { + return VmResult::Ok(ord); + } + + Vec::cmp_with(lhs.1, rhs.1, caller) + }) + } + + /// Hash the current value. + #[cfg(feature = "alloc")] + pub fn hash(&self, hasher: &mut Hasher) -> VmResult<()> { + self.hash_with(hasher, &mut EnvProtocolCaller) + } + + /// Hash the current value. + #[cfg_attr(feature = "bench", inline(never))] + pub(crate) fn hash_with( + &self, + hasher: &mut Hasher, + caller: &mut dyn ProtocolCaller, + ) -> VmResult<()> { + match vm_try!(self.as_ref()) { + ReprRef::Inline(value) => return VmResult::Ok(vm_try!(value.hash(hasher))), + ReprRef::Any(value) => match value.type_hash() { + Vec::HASH => { + let vec = vm_try!(value.borrow_ref::()); + return Vec::hash_with(&vec, hasher, caller); } + OwnedTuple::HASH => { + let tuple = vm_try!(value.borrow_ref::()); + return Tuple::hash_with(&tuple, hasher, caller); + } + _ => {} + }, + _ => {} + } + + let mut args = DynGuardedArgs::new((hasher,)); + + if let CallResultOnly::Ok(value) = + vm_try!(caller.try_call_protocol_fn(Protocol::HASH, self.clone(), &mut args)) + { + return VmResult::Ok(vm_try!(<_>::from_value(value))); + } + + err(VmErrorKind::UnsupportedUnaryOperation { + op: Protocol::HASH.name, + operand: vm_try!(self.type_info()), + }) + } + + fn bin_op_with( + &self, + b: &Value, + caller: &mut dyn ProtocolCaller, + protocol: Protocol, + inline: fn(&Inline, &Inline) -> Result, + dynamic: fn( + (&Arc, &[Value]), + (&Arc, &[Value]), + &mut dyn ProtocolCaller, + ) -> VmResult, + ) -> VmResult + where + T: FromValue, + { + match (vm_try!(self.as_ref()), vm_try!(b.as_ref())) { + (ReprRef::Inline(lhs), ReprRef::Inline(rhs)) => { + return VmResult::Ok(vm_try!(inline(lhs, rhs))) } (ReprRef::Inline(lhs), rhs) => { return VmResult::err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::CMP.name, + op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), + }); + } + (ReprRef::Dynamic(lhs), ReprRef::Dynamic(rhs)) => { + let lhs_rtti = lhs.rtti(); + let rhs_rtti = rhs.rtti(); + + let lhs = vm_try!(lhs.borrow_ref()); + let rhs = vm_try!(rhs.borrow_ref()); + + if lhs_rtti.hash == rhs_rtti.hash { + return dynamic((lhs_rtti, &lhs), (rhs_rtti, &rhs), caller); + } + + return VmResult::err(VmErrorKind::UnsupportedBinaryOperation { + op: protocol.name, + lhs: lhs_rtti.clone().type_info(), + rhs: rhs_rtti.clone().type_info(), }); } _ => {} } - if let CallResultOnly::Ok(value) = vm_try!(caller.try_call_protocol_fn( - Protocol::CMP, - self.clone(), - &mut Some((b.clone(),)) - )) { - return VmResult::Ok(vm_try!(<_>::from_value(value))); + if let CallResultOnly::Ok(value) = + vm_try!(caller.try_call_protocol_fn(protocol, self.clone(), &mut Some((b.clone(),)))) + { + return VmResult::Ok(vm_try!(T::from_value(value))); } err(VmErrorKind::UnsupportedBinaryOperation { - op: Protocol::CMP.name, + op: protocol.name, lhs: vm_try!(self.type_info()), rhs: vm_try!(b.type_info()), }) @@ -1580,8 +1345,8 @@ impl Value { match self.repr { Repr::Empty => Err(RuntimeError::from(AccessError::empty())), Repr::Inline(value) => value.as_integer(), - Repr::Mutable(ref value) => Err(RuntimeError::new(VmErrorKind::ExpectedNumber { - actual: value.borrow_ref()?.type_info(), + Repr::Dynamic(ref value) => Err(RuntimeError::new(VmErrorKind::ExpectedNumber { + actual: value.type_info(), })), Repr::Any(ref value) => Err(RuntimeError::new(VmErrorKind::ExpectedNumber { actual: value.type_info(), @@ -1608,7 +1373,7 @@ impl Value { match &self.repr { Repr::Empty => Err(AccessError::empty()), Repr::Inline(value) => Ok(Some(value)), - Repr::Mutable(..) => Ok(None), + Repr::Dynamic(..) => Ok(None), Repr::Any(..) => Ok(None), } } @@ -1617,7 +1382,7 @@ impl Value { match &mut self.repr { Repr::Empty => Err(AccessError::empty()), Repr::Inline(value) => Ok(Some(value)), - Repr::Mutable(..) => Ok(None), + Repr::Dynamic(..) => Ok(None), Repr::Any(..) => Ok(None), } } @@ -1629,7 +1394,7 @@ impl Value { match &self.repr { Repr::Empty => Err(AccessError::empty()), Repr::Inline(..) => Ok(None), - Repr::Mutable(..) => Ok(None), + Repr::Dynamic(..) => Ok(None), Repr::Any(value) => Ok(Some(value)), } } @@ -1638,7 +1403,7 @@ impl Value { match self.repr { Repr::Empty => Err(AccessError::empty()), Repr::Inline(value) => Ok(ReprOwned::Inline(value)), - Repr::Mutable(value) => Ok(ReprOwned::Mutable(value.take()?)), + Repr::Dynamic(value) => Ok(ReprOwned::Dynamic(value)), Repr::Any(value) => Ok(ReprOwned::Any(value)), } } @@ -1646,7 +1411,7 @@ impl Value { pub(crate) fn as_ref(&self) -> Result, AccessError> { match &self.repr { Repr::Inline(value) => Ok(ReprRef::Inline(value)), - Repr::Mutable(value) => Ok(ReprRef::Mutable(value)), + Repr::Dynamic(value) => Ok(ReprRef::Dynamic(value)), Repr::Any(value) => Ok(ReprRef::Any(value)), Repr::Empty => Err(AccessError::empty()), } @@ -1656,7 +1421,7 @@ impl Value { match &mut self.repr { Repr::Empty => Err(AccessError::empty()), Repr::Inline(value) => Ok(ReprMut::Inline(value)), - Repr::Mutable(value) => Ok(ReprMut::Mutable(value)), + Repr::Dynamic(value) => Ok(ReprMut::Dynamic(value)), Repr::Any(value) => Ok(ReprMut::Any(value)), } } @@ -1667,7 +1432,7 @@ impl Value { { match &self.repr { Repr::Inline(..) => Ok(None), - Repr::Mutable(..) => Ok(None), + Repr::Dynamic(..) => Ok(None), Repr::Any(value) => value.try_borrow_ref(), Repr::Empty => Err(AccessError::empty()), } @@ -1679,7 +1444,7 @@ impl Value { { match &self.repr { Repr::Inline(..) => Ok(None), - Repr::Mutable(..) => Ok(None), + Repr::Dynamic(..) => Ok(None), Repr::Any(value) => value.try_borrow_mut(), Repr::Empty => Err(AccessError::empty()), } @@ -1739,7 +1504,7 @@ impl fmt::Debug for Value { write!(f, "{value:?}")?; return Ok(()); } - Repr::Mutable(value) => value.snapshot(), + Repr::Dynamic(value) => value.snapshot(), Repr::Any(value) => value.snapshot(), }; @@ -1759,15 +1524,10 @@ impl fmt::Debug for Value { Repr::Inline(value) => { write!(f, "<{value:?}: {e}>")?; } - Repr::Mutable(value) => match value.borrow_ref() { - Ok(v) => { - let ty = v.type_info(); - write!(f, "<{ty} object at {value:p}: {e}>")?; - } - Err(e2) => { - write!(f, "")?; - } - }, + Repr::Dynamic(value) => { + let ty = value.type_info(); + write!(f, "<{ty} object at {value:p}: {e}>")?; + } Repr::Any(value) => { let ty = value.type_info(); write!(f, "<{ty} object at {value:p}: {e}>")?; @@ -1832,14 +1592,12 @@ impl IntoOutput for Inline { } } -impl TryFrom for Value { - type Error = alloc::Error; - +impl From, Value>> for Value { #[inline] - fn try_from(value: Mutable) -> Result { - Ok(Self { - repr: Repr::Mutable(Shared::new(value)?), - }) + fn from(value: Dynamic, Value>) -> Self { + Self { + repr: Repr::Dynamic(value), + } } } @@ -1852,15 +1610,6 @@ impl TryFrom<&str> for Value { } } -impl IntoOutput for Mutable { - type Output = Mutable; - - #[inline] - fn into_output(self) -> VmResult { - VmResult::Ok(self) - } -} - inline_from! { Bool => bool, Char => char, @@ -1871,13 +1620,6 @@ inline_from! { Ordering => Ordering, } -from! { - EmptyStruct => EmptyStruct, - TupleStruct => TupleStruct, - Struct => Struct, - Variant => Variant, -} - any_from! { crate::alloc::String, super::Bytes, @@ -1916,7 +1658,7 @@ impl Clone for Value { let repr = match &self.repr { Repr::Empty => Repr::Empty, Repr::Inline(inline) => Repr::Inline(*inline), - Repr::Mutable(mutable) => Repr::Mutable(mutable.clone()), + Repr::Dynamic(mutable) => Repr::Dynamic(mutable.clone()), Repr::Any(any) => Repr::Any(any.clone()), }; @@ -1930,7 +1672,7 @@ impl Clone for Value { (Repr::Inline(lhs), Repr::Inline(rhs)) => { *lhs = *rhs; } - (Repr::Mutable(lhs), Repr::Mutable(rhs)) => { + (Repr::Dynamic(lhs), Repr::Dynamic(rhs)) => { lhs.clone_from(rhs); } (Repr::Any(lhs), Repr::Any(rhs)) => { @@ -1952,39 +1694,37 @@ impl TryClone for Value { /// Wrapper for a value kind. #[doc(hidden)] -pub struct NotTypedInlineValue(Inline); +pub struct NotTypedInline(Inline); /// Wrapper for an any ref value kind. #[doc(hidden)] -pub struct NotTypedAnyObj(AnyObj); +pub struct NotTypedAnyObj<'a>(&'a AnyObj); /// The coersion of a value into a typed value. #[non_exhaustive] #[doc(hidden)] -pub enum TypeValue { +pub enum TypeValue<'a> { /// The unit value. Unit, /// A tuple. - Tuple(OwnedTuple), + Tuple(BorrowRef<'a, OwnedTuple>), /// An object. - Object(Object), + Object(BorrowRef<'a, Object>), /// An struct with a well-defined type. - EmptyStruct(EmptyStruct), + EmptyStruct(EmptyStruct<'a>), /// A tuple with a well-defined type. - TupleStruct(TupleStruct), + TupleStruct(TupleStruct<'a>), /// An struct with a well-defined type. - Struct(Struct), - /// The variant of an enum. - Variant(Variant), + Struct(Struct<'a>), /// Not a typed immutable value. #[doc(hidden)] - NotTypedInline(NotTypedInlineValue), + NotTypedInline(NotTypedInline), /// Not a typed value. #[doc(hidden)] - NotTypedAnyObj(NotTypedAnyObj), + NotTypedAnyObj(NotTypedAnyObj<'a>), } -impl TypeValue { +impl TypeValue<'_> { /// Get the type info of the current value. #[doc(hidden)] pub fn type_info(&self) -> TypeInfo { @@ -1995,48 +1735,12 @@ impl TypeValue { TypeValue::EmptyStruct(empty) => empty.type_info(), TypeValue::TupleStruct(tuple) => tuple.type_info(), TypeValue::Struct(object) => object.type_info(), - TypeValue::Variant(empty) => empty.type_info(), TypeValue::NotTypedInline(value) => value.0.type_info(), TypeValue::NotTypedAnyObj(value) => value.0.type_info(), } } } -pub(crate) enum Mutable { - /// An struct with a well-defined type. - EmptyStruct(EmptyStruct), - /// A tuple with a well-defined type. - TupleStruct(TupleStruct), - /// An struct with a well-defined type. - Struct(Struct), - /// The variant of an enum. - Variant(Variant), -} - -impl Mutable { - pub(crate) fn type_info(&self) -> TypeInfo { - match self { - Mutable::EmptyStruct(empty) => empty.type_info(), - Mutable::TupleStruct(tuple) => tuple.type_info(), - Mutable::Struct(object) => object.type_info(), - Mutable::Variant(empty) => empty.type_info(), - } - } - - /// Get the type hash for the current value. - /// - /// One notable feature is that the type of a variant is its container - /// *enum*, and not the type hash of the variant itself. - pub(crate) fn type_hash(&self) -> Hash { - match self { - Mutable::EmptyStruct(empty) => empty.rtti.hash, - Mutable::TupleStruct(tuple) => tuple.rtti.hash, - Mutable::Struct(object) => object.rtti.hash, - Mutable::Variant(variant) => variant.rtti().enum_hash, - } - } -} - /// Ensures that `Value` and `Repr` is niche-filled when used in common /// combinations. #[test] diff --git a/crates/rune/src/runtime/value/data.rs b/crates/rune/src/runtime/value/data.rs index 474e3d753..f8620ba0e 100644 --- a/crates/rune/src/runtime/value/data.rs +++ b/crates/rune/src/runtime/value/data.rs @@ -1,126 +1,88 @@ use core::borrow::Borrow; use core::fmt; use core::hash; -use core::mem::take; use rust_alloc::sync::Arc; -use crate as rune; use crate::alloc::prelude::*; -use crate::runtime::{OwnedTuple, TypeInfo}; +use crate::runtime::{BorrowRef, TypeInfo}; -use super::{FromValue, Mutable, ReprOwned, Rtti, RuntimeError, Value}; +use super::{Rtti, Value}; /// A empty with a well-defined type. -#[derive(TryClone)] -#[try_clone(crate)] -pub struct EmptyStruct { +pub struct EmptyStruct<'a> { /// The type hash of the empty. - pub(crate) rtti: Arc, + pub(crate) rtti: &'a Arc, } -impl EmptyStruct { +impl<'a> EmptyStruct<'a> { /// Access runtime type information. - pub fn rtti(&self) -> &Arc { - &self.rtti + pub fn rtti(&self) -> &'a Arc { + self.rtti } /// Get type info for the typed tuple. pub fn type_info(&self) -> TypeInfo { - TypeInfo::typed(self.rtti.clone()) + TypeInfo::rtti(self.rtti.clone()) } } -impl FromValue for EmptyStruct { - fn from_value(value: Value) -> Result { - match value.take_repr()? { - ReprOwned::Inline(value) => Err(RuntimeError::expected_unit_struct(value.type_info())), - ReprOwned::Mutable(Mutable::EmptyStruct(value)) => Ok(value), - ReprOwned::Mutable(value) => Err(RuntimeError::expected_unit_struct(value.type_info())), - ReprOwned::Any(value) => Err(RuntimeError::expected_unit_struct(value.type_info())), - } - } -} - -impl fmt::Debug for EmptyStruct { +impl fmt::Debug for EmptyStruct<'_> { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.rtti.item) } } /// A tuple with a well-defined type. -#[derive(TryClone)] -pub struct TupleStruct { +pub struct TupleStruct<'a> { /// The type hash of the tuple. - pub(crate) rtti: Arc, + pub(crate) rtti: &'a Arc, /// Content of the tuple. - pub(crate) data: OwnedTuple, + pub(crate) data: BorrowRef<'a, [Value]>, } -impl TupleStruct { +impl<'a> TupleStruct<'a> { /// Access runtime type information. - pub fn rtti(&self) -> &Arc { - &self.rtti + pub fn rtti(&self) -> &'a Rtti { + self.rtti } /// Access underlying data. - pub fn data(&self) -> &OwnedTuple { + pub fn data(&self) -> &[Value] { &self.data } - /// Access underlying data mutably. - pub fn data_mut(&mut self) -> &mut OwnedTuple { - &mut self.data - } - - /// Get type info for the typed tuple. - pub fn type_info(&self) -> TypeInfo { - TypeInfo::typed(self.rtti.clone()) - } - /// Get the value at the given index in the tuple. pub fn get(&self, index: usize) -> Option<&Value> { self.data.get(index) } - /// Get the mutable value at the given index in the tuple. - pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> { - self.data.get_mut(index) - } -} - -impl FromValue for TupleStruct { - fn from_value(value: Value) -> Result { - match value.take_repr()? { - ReprOwned::Inline(value) => Err(RuntimeError::expected_tuple_struct(value.type_info())), - ReprOwned::Mutable(Mutable::TupleStruct(value)) => Ok(value), - ReprOwned::Mutable(value) => { - Err(RuntimeError::expected_tuple_struct(value.type_info())) - } - ReprOwned::Any(value) => Err(RuntimeError::expected_tuple_struct(value.type_info())), - } + /// Get type info for the typed tuple. + pub fn type_info(&self) -> TypeInfo { + TypeInfo::rtti(self.rtti.clone()) } } -impl fmt::Debug for TupleStruct { +impl fmt::Debug for TupleStruct<'_> { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{:?}", self.rtti.item, self.data) + write!(f, "{}", self.rtti.item) } } /// An object with a well-defined type. -#[derive(TryClone)] -pub struct Struct { +pub struct Struct<'a> { /// The type hash of the object. - pub(crate) rtti: Arc, + pub(crate) rtti: &'a Arc, /// Contents of the object. - pub(crate) data: Box<[Value]>, + pub(crate) data: BorrowRef<'a, [Value]>, } -impl Struct { +impl<'a> Struct<'a> { /// Access struct rtti. - pub fn rtti(&self) -> &Arc { - &self.rtti + pub fn rtti(&self) -> &'a Arc { + self.rtti } /// Access truct data. @@ -128,11 +90,6 @@ impl Struct { &self.data } - /// Access struct data mutably. - pub fn data_mut(&mut self) -> &mut [Value] { - &mut self.data - } - /// Get a field through the accessor. pub fn get(&self, key: &Q) -> Option<&Value> where @@ -142,51 +99,14 @@ impl Struct { self.data.get(*self.rtti.fields.get(key)?) } - /// Get a field through the accessor. - pub fn get_mut(&mut self, key: &Q) -> Option<&mut Value> - where - Box: Borrow, - Q: hash::Hash + Eq + ?Sized, - { - self.data.get_mut(*self.rtti.fields.get(key)?) - } - /// Get type info for the typed object. pub(crate) fn type_info(&self) -> TypeInfo { - TypeInfo::typed(self.rtti.clone()) + TypeInfo::rtti(self.rtti.clone()) } } -impl FromValue for Struct { - fn from_value(value: Value) -> Result { - match value.take_repr()? { - ReprOwned::Inline(value) => Err(RuntimeError::expected_struct(value.type_info())), - ReprOwned::Mutable(Mutable::Struct(value)) => Ok(value), - ReprOwned::Mutable(value) => Err(RuntimeError::expected_struct(value.type_info())), - ReprOwned::Any(value) => Err(RuntimeError::expected_struct(value.type_info())), - } - } -} - -impl fmt::Debug for Struct { +impl fmt::Debug for Struct<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} {{", self.rtti.item)?; - - let mut first = true; - - for (index, field) in self.data.iter().enumerate() { - let Some((name, _)) = self.rtti.fields.iter().find(|t| *t.1 == index) else { - continue; - }; - - if !take(&mut first) { - write!(f, ", ")?; - } - - write!(f, "{name}: {field:?}")?; - } - - write!(f, "}}")?; - Ok(()) + write!(f, "{}", self.rtti.item) } } diff --git a/crates/rune/src/runtime/value/dynamic.rs b/crates/rune/src/runtime/value/dynamic.rs new file mode 100644 index 000000000..c9df704fa --- /dev/null +++ b/crates/rune/src/runtime/value/dynamic.rs @@ -0,0 +1,455 @@ +use core::alloc::{Layout, LayoutError}; +use core::cell::Cell; +use core::fmt; +use core::mem::{align_of, needs_drop, replace, size_of, take}; +use core::ptr::{self, addr_of, addr_of_mut, NonNull}; + +use rust_alloc::sync::Arc; + +use crate::alloc; +use crate::alloc::alloc::{Allocator, Global}; +use crate::alloc::fmt::TryWrite; +use crate::hash::Hash; +use crate::runtime::{ + Access, AccessError, BorrowMut, BorrowRef, Formatter, IntoOutput, ProtocolCaller, Rtti, + RttiKind, Snapshot, TypeInfo, Value, VmResult, +}; + +#[derive(Debug)] +pub(crate) enum DynamicTakeError { + Access(AccessError), + Alloc(alloc::Error), +} + +impl fmt::Display for DynamicTakeError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DynamicTakeError::Access(error) => error.fmt(f), + DynamicTakeError::Alloc(error) => error.fmt(f), + } + } +} + +impl core::error::Error for DynamicTakeError {} + +impl From for DynamicTakeError { + fn from(error: AccessError) -> Self { + Self::Access(error) + } +} + +impl From for DynamicTakeError { + fn from(error: alloc::Error) -> Self { + Self::Alloc(error) + } +} + +/// A dynamic value defined at runtime. +/// +/// This is an allocation-optimized container which allows an interior slice of +/// data `T` to be checked for access and `H` to be immutably accessed inside of +/// a single reference-counted container. +pub struct Dynamic { + shared: NonNull>, +} + +impl Dynamic { + /// A dynamic value inside of the virtual machine. + pub(crate) fn new( + rtti: H, + it: impl IntoIterator, + ) -> alloc::Result { + let it = it.into_iter(); + let this = Self::alloc(rtti, it.len())?; + + // Fill out the newly allocated container. + unsafe { + let data = Shared::as_data_ptr(this.shared); + + for (i, value) in it.enumerate() { + data.add(i).write(value); + } + } + + Ok(this) + } + + /// A dynamic value inside of the virtual machine. + fn alloc(rtti: H, len: usize) -> alloc::Result { + let layout = Shared::::layout(len)?; + + let shared = Global.allocate(layout)?.cast::>(); + + // SAFETY: We've allocated space for both the shared header and the + // trailing data. + unsafe { + shared.write(Shared { + rtti, + count: Cell::new(1), + access: Access::new(), + len, + data: [], + }); + } + + Ok(Self { shared }) + } + + /// Test if the value is sharable. + pub(crate) fn is_readable(&self) -> bool { + // Safety: Since we have a reference to this shared, we know that the + // inner is available. + unsafe { self.shared.as_ref().access.is_shared() } + } + + /// Test if the value is exclusively accessible. + pub(crate) fn is_writable(&self) -> bool { + unsafe { self.shared.as_ref().access.is_exclusive() } + } + + /// Get access snapshot of shared value. + pub(crate) fn snapshot(&self) -> Snapshot { + // SAFETY: We know that the shared pointer is valid. + unsafe { self.shared.as_ref().access.snapshot() } + } + + /// Get the size of the dynamic collection of values. + pub(crate) fn len(&self) -> usize { + // SAFETY: We know that the shared pointer is valid. + unsafe { self.shared.as_ref().len } + } + + /// Get runtime type information of the dynamic value. + pub(crate) fn rtti(&self) -> &H { + // SAFETY: We know that the shared pointer is valid. + unsafe { &self.shared.as_ref().rtti } + } + + /// Borrow the interior data array by reference. + pub(crate) fn borrow_ref(&self) -> Result, AccessError> { + // SAFETY: We know the layout is valid since it is reference counted. + unsafe { + let guard = self.shared.as_ref().access.shared()?; + let data = Shared::as_data_ptr(self.shared); + let data = NonNull::slice_from_raw_parts(data, self.shared.as_ref().len); + Ok(BorrowRef::new(data, guard.into_raw())) + } + } + + /// Borrow the interior data array by mutable reference. + pub(crate) fn borrow_mut(&self) -> Result, AccessError> { + // SAFETY: We know the layout is valid since it is reference counted. + unsafe { + let guard = self.shared.as_ref().access.exclusive()?; + let data = Shared::as_data_ptr(self.shared); + let data = NonNull::slice_from_raw_parts(data, self.shared.as_ref().len); + Ok(BorrowMut::new(data, guard.into_raw())) + } + } + + /// Take the interior value and drop it if necessary. + pub(crate) fn drop(self) -> Result<(), AccessError> { + // SAFETY: We've checked for the appropriate type just above. + unsafe { + self.shared.as_ref().access.try_take()?; + let len = self.shared.as_ref().len; + Shared::drop_values(self.shared, len); + Ok(()) + } + } +} + +impl Dynamic +where + H: Clone, +{ + /// Take the interior value and return a handle to the taken value. + pub(crate) fn take(self) -> Result { + // SAFETY: We are checking the interior value for access before taking + // it. + unsafe { + self.shared.as_ref().access.try_take()?; + let len = self.shared.as_ref().len; + let new = Self::alloc(self.rtti().clone(), len)?; + let from = Shared::as_data_ptr(self.shared); + let to = Shared::as_data_ptr(new.shared); + to.copy_from_nonoverlapping(from, len); + Ok(new) + } + } +} + +impl Drop for Dynamic { + fn drop(&mut self) { + // Decrement a shared value. + unsafe { + Shared::dec(self.shared); + } + } +} + +impl Clone for Dynamic { + #[inline] + fn clone(&self) -> Self { + // SAFETY: We know that the inner value is live in this instance. + unsafe { + Shared::inc(self.shared); + } + + Self { + shared: self.shared, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + if ptr::eq(self.shared.as_ptr(), source.shared.as_ptr()) { + return; + } + + let old = replace(&mut self.shared, source.shared); + + // SAFETY: We know that the inner value is live in both instances. + unsafe { + Shared::dec(old); + Shared::inc(self.shared); + } + } +} + +struct Shared { + /// Run time type information of the shared value. + rtti: H, + /// Reference count. + count: Cell, + /// Access flags. + access: Access, + /// The size of the dynamic value. + len: usize, + /// Start of data pointer. Only used for alignment. + data: [T; 0], +} + +impl Shared { + fn layout(len: usize) -> Result { + let array = Layout::array::(len)?; + Layout::from_size_align( + size_of::>() + array.size(), + align_of::>(), + ) + } + + /// Get the rtti pointer in the shared container. + unsafe fn as_rtti_ptr(this: NonNull) -> NonNull { + NonNull::new_unchecked(addr_of_mut!((*this.as_ptr()).rtti)) + } + + /// Get the data pointer in the shared container. + unsafe fn as_data_ptr(this: NonNull) -> NonNull { + NonNull::new_unchecked(addr_of_mut!((*this.as_ptr()).data)).cast::() + } + + /// Increment the reference count of the inner value. + unsafe fn inc(this: NonNull) { + let count_ref = &*addr_of!((*this.as_ptr()).count); + let count = count_ref.get(); + + debug_assert_ne!( + count, 0, + "Reference count of zero should only happen if Shared is incorrectly implemented" + ); + + if count == usize::MAX { + crate::alloc::abort(); + } + + count_ref.set(count + 1); + } + + /// Decrement the reference count in inner, and free the underlying data if + /// it has reached zero. + /// + /// # Safety + /// + /// ProtocolCaller needs to ensure that `this` is a valid pointer. + unsafe fn dec(this: NonNull) { + let count_ref = &*addr_of!((*this.as_ptr()).count); + let access = &*addr_of!((*this.as_ptr()).access); + let count = count_ref.get(); + + debug_assert_ne!( + count, 0, + "Reference count of zero should only happen if Shared is incorrectly implemented" + ); + + let count = count - 1; + count_ref.set(count); + + if count != 0 { + return; + } + + let len = (*this.as_ptr()).len; + + let Ok(layout) = Self::layout(len) else { + unreachable!(); + }; + + if !access.is_taken() { + Self::drop_values(this, len); + } + + if needs_drop::() { + Self::as_rtti_ptr(this).drop_in_place(); + } + + Global.deallocate(this.cast(), layout); + } + + unsafe fn drop_values(this: NonNull, len: usize) { + if needs_drop::() { + let data = Self::as_data_ptr(this); + NonNull::slice_from_raw_parts(data, len).drop_in_place(); + } + } +} + +impl Dynamic, T> { + /// Access type hash on the dynamic value. + #[inline] + pub(crate) fn type_hash(&self) -> Hash { + self.rtti().hash + } + + /// Access type information on the dynamic value. + #[inline] + pub(crate) fn type_info(&self) -> TypeInfo { + self.rtti().clone().type_info() + } + + /// Access a field by name. + #[inline] + pub(crate) fn get_field_ref(&self, key: &str) -> Result>, AccessError> { + let Some(index) = self.rtti().fields.get(key) else { + return Ok(None); + }; + + let values = self.borrow_ref()?; + + let index = *index; + let value = BorrowRef::try_map(values, |value| value.get(index)); + Ok(value.ok()) + } + + /// Access a field mutably by name. + #[inline] + pub(crate) fn get_field_mut(&self, key: &str) -> Result>, AccessError> { + let Some(index) = self.rtti().fields.get(key) else { + return Ok(None); + }; + + let values = self.borrow_mut()?; + + let index = *index; + let value = BorrowMut::try_map(values, |value| value.get_mut(index)); + Ok(value.ok()) + } + + /// Access a field by index. + #[inline] + pub(crate) fn get_ref(&self, index: usize) -> Result>, AccessError> { + let values = self.borrow_ref()?; + let value = BorrowRef::try_map(values, |value| value.get(index)); + Ok(value.ok()) + } + + /// Access a field mutably by index. + #[inline] + pub(crate) fn get_mut(&self, index: usize) -> Result>, AccessError> { + let values = self.borrow_mut()?; + let value = BorrowMut::try_map(values, |value| value.get_mut(index)); + Ok(value.ok()) + } +} + +impl Dynamic, Value> { + /// Debug print the dynamic value. + pub(crate) fn debug_fmt_with( + &self, + f: &mut Formatter, + caller: &mut dyn ProtocolCaller, + ) -> VmResult<()> { + let rtti = self.rtti(); + let values = vm_try!(self.borrow_ref()); + + match rtti.kind { + RttiKind::Empty => debug_empty(rtti, f), + RttiKind::Tuple => debug_tuple(rtti, &values, f, caller), + RttiKind::Struct => debug_struct(rtti, &values, f, caller), + } + } +} + +fn debug_empty(rtti: &Rtti, f: &mut Formatter) -> VmResult<()> { + vm_try!(write!(f, "{}", rtti.item)); + VmResult::Ok(()) +} + +fn debug_tuple( + rtti: &Rtti, + values: &[Value], + f: &mut Formatter, + caller: &mut dyn ProtocolCaller, +) -> VmResult<()> { + vm_try!(write!(f, "{} (", rtti.item)); + + let mut first = true; + + for value in values.iter() { + if !take(&mut first) { + vm_try!(write!(f, ", ")); + } + + vm_try!(value.debug_fmt_with(f, caller)); + } + + vm_try!(write!(f, ")")); + VmResult::Ok(()) +} + +fn debug_struct( + rtti: &Rtti, + values: &[Value], + f: &mut Formatter, + caller: &mut dyn ProtocolCaller, +) -> VmResult<()> { + vm_try!(write!(f, "{} {{", rtti.item)); + + let mut first = true; + + for (index, field) in values.iter().enumerate() { + let Some((name, _)) = rtti.fields.iter().find(|t| *t.1 == index) else { + continue; + }; + + if !take(&mut first) { + vm_try!(write!(f, ", ")); + } + + vm_try!(write!(f, "{name}: ")); + vm_try!(field.debug_fmt_with(f, caller)); + } + + vm_try!(write!(f, "}}")); + VmResult::Ok(()) +} + +impl IntoOutput for Dynamic, Value> { + type Output = Dynamic, Value>; + + #[inline] + fn into_output(self) -> VmResult { + VmResult::Ok(self) + } +} diff --git a/crates/rune/src/runtime/value/inline.rs b/crates/rune/src/runtime/value/inline.rs index ceb970c23..7cab10f1f 100644 --- a/crates/rune/src/runtime/value/inline.rs +++ b/crates/rune/src/runtime/value/inline.rs @@ -1,18 +1,17 @@ use core::any; use core::cmp::Ordering; use core::fmt; +use core::hash::Hash as _; use musli::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::hash::Hash; use crate::runtime::{ - OwnedTuple, Protocol, RuntimeError, Type, TypeInfo, VmErrorKind, VmIntegerRepr, VmResult, + Hasher, OwnedTuple, Protocol, RuntimeError, Type, TypeInfo, VmErrorKind, VmIntegerRepr, }; use crate::TypeHash; -use super::err; - /// An inline value. #[derive(Clone, Copy, Encode, Decode, Deserialize, Serialize)] pub enum Inline { @@ -93,27 +92,30 @@ impl Inline { } /// Perform a total equality check over two inline values. - pub(crate) fn eq(&self, other: &Self) -> VmResult { + pub(crate) fn eq(&self, other: &Self) -> Result { match (self, other) { - (Inline::Unit, Inline::Unit) => VmResult::Ok(true), - (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(*a == *b), - (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(*a == *b), - (Inline::Unsigned(a), Inline::Unsigned(b)) => VmResult::Ok(*a == *b), - (Inline::Signed(a), Inline::Signed(b)) => VmResult::Ok(*a == *b), + (Inline::Unit, Inline::Unit) => Ok(true), + (Inline::Bool(a), Inline::Bool(b)) => Ok(*a == *b), + (Inline::Char(a), Inline::Char(b)) => Ok(*a == *b), + (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(*a == *b), + (Inline::Signed(a), Inline::Signed(b)) => Ok(*a == *b), (Inline::Float(a), Inline::Float(b)) => { let Some(ordering) = a.partial_cmp(b) else { - return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); + return Err(RuntimeError::new(VmErrorKind::IllegalFloatComparison { + lhs: *a, + rhs: *b, + })); }; - VmResult::Ok(matches!(ordering, Ordering::Equal)) + Ok(matches!(ordering, Ordering::Equal)) } - (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(*a == *b), - (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(*a == *b), - (lhs, rhs) => err(VmErrorKind::UnsupportedBinaryOperation { + (Inline::Type(a), Inline::Type(b)) => Ok(*a == *b), + (Inline::Ordering(a), Inline::Ordering(b)) => Ok(*a == *b), + (lhs, rhs) => Err(RuntimeError::new(VmErrorKind::UnsupportedBinaryOperation { op: Protocol::EQ.name, lhs: lhs.type_info(), rhs: rhs.type_info(), - }), + })), } } @@ -147,29 +149,66 @@ impl Inline { } /// Total comparison implementation for inline. - pub(crate) fn cmp(&self, other: &Self) -> VmResult { + pub(crate) fn cmp(&self, other: &Self) -> Result { match (self, other) { - (Inline::Unit, Inline::Unit) => VmResult::Ok(Ordering::Equal), - (Inline::Bool(a), Inline::Bool(b)) => VmResult::Ok(a.cmp(b)), - (Inline::Char(a), Inline::Char(b)) => VmResult::Ok(a.cmp(b)), - (Inline::Unsigned(a), Inline::Unsigned(b)) => VmResult::Ok(a.cmp(b)), - (Inline::Signed(a), Inline::Signed(b)) => VmResult::Ok(a.cmp(b)), + (Inline::Unit, Inline::Unit) => Ok(Ordering::Equal), + (Inline::Bool(a), Inline::Bool(b)) => Ok(a.cmp(b)), + (Inline::Char(a), Inline::Char(b)) => Ok(a.cmp(b)), + (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(a.cmp(b)), + (Inline::Signed(a), Inline::Signed(b)) => Ok(a.cmp(b)), (Inline::Float(a), Inline::Float(b)) => { let Some(ordering) = a.partial_cmp(b) else { - return VmResult::err(VmErrorKind::IllegalFloatComparison { lhs: *a, rhs: *b }); + return Err(RuntimeError::new(VmErrorKind::IllegalFloatComparison { + lhs: *a, + rhs: *b, + })); }; - VmResult::Ok(ordering) + Ok(ordering) } - (Inline::Type(a), Inline::Type(b)) => VmResult::Ok(a.cmp(b)), - (Inline::Ordering(a), Inline::Ordering(b)) => VmResult::Ok(a.cmp(b)), - (lhs, rhs) => VmResult::err(VmErrorKind::UnsupportedBinaryOperation { + (Inline::Type(a), Inline::Type(b)) => Ok(a.cmp(b)), + (Inline::Ordering(a), Inline::Ordering(b)) => Ok(a.cmp(b)), + (lhs, rhs) => Err(RuntimeError::new(VmErrorKind::UnsupportedBinaryOperation { op: Protocol::CMP.name, lhs: lhs.type_info(), rhs: rhs.type_info(), - }), + })), } } + + /// Hash an inline value. + pub(crate) fn hash(&self, hasher: &mut Hasher) -> Result<(), RuntimeError> { + match self { + Inline::Unsigned(value) => { + value.hash(hasher); + } + Inline::Signed(value) => { + value.hash(hasher); + } + // Care must be taken whan hashing floats, to ensure that `hash(v1) + // === hash(v2)` if `eq(v1) === eq(v2)`. Hopefully we accomplish + // this by rejecting NaNs and rectifying subnormal values of zero. + Inline::Float(value) => { + if value.is_nan() { + return Err(RuntimeError::new(VmErrorKind::IllegalFloatOperation { + value: *value, + })); + } + + let zero = *value == 0.0; + let value = ((zero as u8 as f64) * 0.0 + (!zero as u8 as f64) * *value).to_bits(); + value.hash(hasher); + } + operand => { + return Err(RuntimeError::new(VmErrorKind::UnsupportedUnaryOperation { + op: Protocol::HASH.name, + operand: operand.type_info(), + })); + } + } + + Ok(()) + } } impl fmt::Debug for Inline { diff --git a/crates/rune/src/runtime/value/macros.rs b/crates/rune/src/runtime/value/macros.rs index 00c848c69..34c0d245a 100644 --- a/crates/rune/src/runtime/value/macros.rs +++ b/crates/rune/src/runtime/value/macros.rs @@ -1,167 +1,3 @@ -/// Macro used to generate coersions for [`Value`]. -macro_rules! into_base { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - ) => { - $(#[$($meta)*])* - /// - /// This ensures that the value has read access to the underlying value - /// and does not consume it. - #[inline] - pub fn $into_ref(self) -> Result, RuntimeError> { - match self.repr { - Repr::Empty => { - Err(RuntimeError::from(AccessError::empty())) - } - Repr::Inline(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - Repr::Mutable(value) => { - let value = value.into_ref()?; - - let result = Ref::try_map(value, |value| match value { - Mutable::$kind(bytes) => Some(bytes), - _ => None, - }); - - match result { - Ok(bytes) => Ok(bytes), - Err(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - } - } - }, - Repr::Any(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has write access to the underlying value - /// and does not consume it. - #[inline] - pub fn $into_mut(self) -> Result, RuntimeError> { - match self.repr { - Repr::Empty => { - Err(RuntimeError::from(AccessError::empty())) - } - Repr::Inline(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - Repr::Mutable(value) => { - let value = value.into_mut()?; - - let result = Mut::try_map(value, |value| match value { - Mutable::$kind(value) => Some(value), - _ => None, - }); - - match result { - Ok(value) => Ok(value), - Err(value) => Err(RuntimeError::expected::<$ty>(value.type_info())), - } - } - Repr::Any(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has read access to the underlying value - /// and does not consume it. - #[inline] - pub fn $borrow_ref(&self) -> Result, RuntimeError> { - match self.as_ref()? { - ReprRef::Inline(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - ReprRef::Mutable(value) => { - let result = BorrowRef::try_map(value.borrow_ref()?, |kind| match kind { - Mutable::$kind(value) => Some(value), - _ => None, - }); - - match result { - Ok(value) => Ok(value), - Err(value) => Err(RuntimeError::expected::<$ty>(value.type_info())), - } - }, - ReprRef::Any(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - }, - } - } - - $(#[$($meta)*])* - /// - /// This ensures that the value has write access to the underlying value - /// and does not consume it. - #[inline] - pub fn $borrow_mut(&self) -> Result, RuntimeError> { - match self.as_ref()? { - ReprRef::Inline(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - } - ReprRef::Mutable(value) => { - let result = BorrowMut::try_map(value.borrow_mut()?, |kind| match kind { - Mutable::$kind(value) => Some(value), - _ => None, - }); - - match result { - Ok(value) => Ok(value), - Err(value) => Err(RuntimeError::expected::<$ty>(value.type_info())), - } - }, - ReprRef::Any(value) => { - Err(RuntimeError::expected::<$ty>(value.type_info())) - } - } - } - } -} - -macro_rules! into { - ( - $(#[$($meta:meta)*])* - $kind:ident($ty:ty), - $into_ref:ident, - $into_mut:ident, - $borrow_ref:ident, - $borrow_mut:ident, - $into:ident, - ) => { - into_base! { - $(#[$($meta)*])* - $kind($ty), - $into_ref, - $into_mut, - $borrow_ref, - $borrow_mut, - } - - $(#[$($meta)*])* - /// - /// This consumes the underlying value. - #[inline] - pub fn $into(self) -> Result<$ty, RuntimeError> { - match self.take_repr()? { - ReprOwned::Mutable(Mutable::$kind(value)) => Ok(value), - value => Err(RuntimeError::expected::<$ty>(value.type_info())), - } - } - } -} - macro_rules! inline_into { ( $(#[$($meta:meta)*])* @@ -181,8 +17,8 @@ macro_rules! inline_into { Repr::Inline(value) => { Err(RuntimeError::expected::<$ty>(value.type_info())) } - Repr::Mutable(value) => { - Err(RuntimeError::expected::<$ty>(value.borrow_ref()?.type_info())) + Repr::Dynamic(value) => { + Err(RuntimeError::expected::<$ty>(value.type_info())) } Repr::Any(value) => { Err(RuntimeError::expected::<$ty>(value.type_info())) @@ -205,8 +41,8 @@ macro_rules! inline_into { Repr::Inline(value) => { Err(RuntimeError::expected::<$ty>(value.type_info())) } - Repr::Mutable(value) => { - Err(RuntimeError::expected::<$ty>(value.borrow_ref()?.type_info())) + Repr::Dynamic(value) => { + Err(RuntimeError::expected::<$ty>(value.type_info())) } Repr::Any(value) => { Err(RuntimeError::expected::<$ty>(value.type_info())) @@ -219,37 +55,6 @@ macro_rules! inline_into { } } -macro_rules! from { - ($($variant:ident => $ty:ty),* $(,)*) => { - $( - impl TryFrom<$ty> for Value { - type Error = $crate::alloc::Error; - - #[inline] - fn try_from(value: $ty) -> Result { - Value::try_from(Mutable::$variant(value)) - } - } - - impl IntoOutput for $ty { - type Output = $ty; - - #[inline] - fn into_output(self) -> $crate::runtime::VmResult { - $crate::runtime::VmResult::Ok(self) - } - } - - impl $crate::runtime::ToValue for $ty { - #[inline] - fn to_value(self) -> Result { - Ok($crate::runtime::Value::try_from(self)?) - } - } - )* - }; -} - macro_rules! any_from { ($($ty:ty),* $(,)*) => { $( diff --git a/crates/rune/src/runtime/value/rtti.rs b/crates/rune/src/runtime/value/rtti.rs index 85bf2fb5b..4b45e413d 100644 --- a/crates/rune/src/runtime/value/rtti.rs +++ b/crates/rune/src/runtime/value/rtti.rs @@ -2,73 +2,16 @@ use core::borrow::Borrow; use core::cmp::Ordering; use core::hash; +use rust_alloc::sync::Arc; + use serde::{Deserialize, Serialize}; use crate::alloc::prelude::*; use crate::alloc::HashMap; -use crate::runtime::Value; +use crate::item::Item; +use crate::runtime::{TypeInfo, Value}; use crate::{Hash, ItemBuf}; -/// Runtime information on variant. -#[derive(Debug, Serialize, Deserialize)] -#[non_exhaustive] -pub struct VariantRtti { - /// The type hash of the enum. - pub enum_hash: Hash, - /// The type variant hash. - pub hash: Hash, - /// The name of the variant. - pub item: ItemBuf, - /// Fields associated with the variant. - pub fields: HashMap, usize>, -} - -impl VariantRtti { - /// Access a named field mutably from the given data. - pub fn get_field<'a, Q>(&self, data: &'a [Value], key: &Q) -> Option<&'a Value> - where - Box: Borrow, - Q: hash::Hash + Eq + ?Sized, - { - data.get(*self.fields.get(key)?) - } - - /// Access a named field immutably from the given data. - pub fn get_field_mut<'a, Q>(&self, data: &'a mut [Value], key: &Q) -> Option<&'a mut Value> - where - Box: Borrow, - Q: hash::Hash + Eq + ?Sized, - { - data.get_mut(*self.fields.get(key)?) - } -} - -impl PartialEq for VariantRtti { - fn eq(&self, other: &Self) -> bool { - self.hash == other.hash - } -} - -impl Eq for VariantRtti {} - -impl hash::Hash for VariantRtti { - fn hash(&self, state: &mut H) { - self.hash.hash(state) - } -} - -impl PartialOrd for VariantRtti { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for VariantRtti { - fn cmp(&self, other: &Self) -> Ordering { - self.hash.cmp(&other.hash) - } -} - /// Field accessor for a variant struct. #[doc(hidden)] pub struct Accessor<'a> { @@ -88,41 +31,63 @@ impl<'a> Accessor<'a> { } } +/// The kind of value stored. +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub(crate) enum RttiKind { + /// The value stored is empty. + Empty, + /// The value stored is a tuple. + Tuple, + /// The value stored is a strict. + Struct, +} + /// Runtime information on variant. #[derive(Debug, Serialize, Deserialize)] #[non_exhaustive] pub struct Rtti { + /// The kind of value. + pub(crate) kind: RttiKind, /// The type hash of the type. - pub hash: Hash, + pub(crate) hash: Hash, + /// If this type is a variant, designates the hash of the variant. + pub(crate) variant_hash: Hash, /// The item of the type. - pub item: ItemBuf, + pub(crate) item: ItemBuf, /// Mapping from field names to their corresponding indexes. - pub fields: HashMap, usize>, + pub(crate) fields: HashMap, usize>, } impl Rtti { - /// Access a named field mutably from the given data. - pub fn get_field<'a, Q>(&self, data: &'a [Value], key: &Q) -> Option<&'a Value> - where - Box: Borrow, - Q: hash::Hash + Eq + ?Sized, - { - data.get(*self.fields.get(key)?) + /// Test if this RTTI matches the given raw hashes. + #[inline] + pub(crate) fn is(&self, hash: Hash, variant_hash: Hash) -> bool { + self.hash == hash && self.variant_hash == variant_hash } - /// Access a named field immutably from the given data. - pub fn get_field_mut<'a, Q>(&self, data: &'a mut [Value], key: &Q) -> Option<&'a mut Value> - where - Box: Borrow, - Q: hash::Hash + Eq + ?Sized, - { - data.get_mut(*self.fields.get(key)?) + /// Access the item of the RTTI. + #[inline] + pub fn item(&self) -> &Item { + &self.item + } + + /// Access the type hash of the RTTI. + #[inline] + pub fn type_hash(&self) -> Hash { + self.hash + } + + /// Access the type information for the RTTI. + #[inline] + pub fn type_info(self: Arc) -> TypeInfo { + TypeInfo::rtti(self) } } impl PartialEq for Rtti { fn eq(&self, other: &Self) -> bool { - self.hash == other.hash + self.hash == other.hash && self.variant_hash == other.variant_hash } } @@ -130,7 +95,8 @@ impl Eq for Rtti {} impl hash::Hash for Rtti { fn hash(&self, state: &mut H) { - self.hash.hash(state) + self.hash.hash(state); + self.variant_hash.hash(state); } } @@ -141,7 +107,10 @@ impl PartialOrd for Rtti { } impl Ord for Rtti { + #[inline] fn cmp(&self, other: &Self) -> Ordering { - self.hash.cmp(&other.hash) + self.hash + .cmp(&other.hash) + .then_with(|| self.variant_hash.cmp(&other.variant_hash)) } } diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index 6a00f4968..bb58b06c9 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -2,7 +2,7 @@ use core::fmt; use crate::alloc; use crate::alloc::prelude::*; -use crate::runtime::{self, Bytes, Inline, Mutable, Object, OwnedTuple, ReprRef, Vec}; +use crate::runtime::{self, Bytes, Inline, Object, OwnedTuple, ReprRef, RttiKind, Vec}; use crate::TypeHash; use serde::de::{self, Deserialize as _, Error as _}; @@ -37,15 +37,19 @@ impl ser::Serialize for Value { Inline::Type(..) => Err(ser::Error::custom("cannot serialize types")), Inline::Ordering(..) => Err(ser::Error::custom("cannot serialize orderings")), }, - ReprRef::Mutable(value) => match &*value.borrow_ref().map_err(S::Error::custom)? { - Mutable::EmptyStruct(..) => { - Err(ser::Error::custom("cannot serialize empty structs")) - } - Mutable::TupleStruct(..) => { - Err(ser::Error::custom("cannot serialize tuple structs")) - } - Mutable::Struct(..) => Err(ser::Error::custom("cannot serialize objects structs")), - Mutable::Variant(..) => Err(ser::Error::custom("cannot serialize variants")), + ReprRef::Dynamic(value) => match value.rtti().kind { + RttiKind::Empty => Err(ser::Error::custom(format!( + "cannot serialize empty struct {}", + value.rtti().item + ))), + RttiKind::Tuple => Err(ser::Error::custom(format!( + "cannot serialize tuple struct {}", + value.rtti().item + ))), + RttiKind::Struct => Err(ser::Error::custom(format!( + "cannot serialize struct {}", + value.rtti().item + ))), }, ReprRef::Any(value) => match value.type_hash() { Option::::HASH => { diff --git a/crates/rune/src/runtime/value/tests.rs b/crates/rune/src/runtime/value/tests.rs new file mode 100644 index 000000000..4857fb2c0 --- /dev/null +++ b/crates/rune/src/runtime/value/tests.rs @@ -0,0 +1,138 @@ +use rust_alloc::boxed::Box; + +use super::Dynamic; + +#[derive(Debug, PartialEq, Eq)] +struct Count(isize); + +#[test] +fn dynamic_drop() { + let header = Box::new(42u32); + let v1 = crate::to_value([1u32, 2, 3, 4]).unwrap(); + let _dynamic = Dynamic::new(header, [v1]).unwrap(); +} + +#[test] +fn dynamic_borrow_ref() { + let header = Box::new(42u32); + let v1 = crate::to_value([1u32, 2, 3, 4]).unwrap(); + let dynamic = Dynamic::new(header, [v1]).unwrap(); + + let values = dynamic.borrow_ref().unwrap(); + let values2 = dynamic.borrow_ref().unwrap(); + + assert!(dynamic.borrow_mut().is_err()); + drop(values); + assert!(dynamic.borrow_mut().is_err()); + drop(values2); + assert!(dynamic.borrow_mut().is_ok()); +} + +#[test] +fn dynamic_borrow_ref_err() -> crate::support::Result<()> { + let a = Dynamic::new((), [Count(0)])?; + + a.borrow_mut()?[0].0 += 1; + + { + let a_ref = a.borrow_ref()?; + assert_eq!(a_ref[0].0, 1); + assert!(a.borrow_mut().is_err()); + assert!(a.borrow_ref().is_ok()); + } + + let mut a = a.borrow_mut()?; + a[0].0 += 1; + assert_eq!(a[0].0, 2); + Ok(()) +} + +#[test] +fn dynamic_borrow_mut() { + let header = Box::new(42u32); + let v1 = crate::to_value([1u32, 2, 3, 4]).unwrap(); + let dynamic = Dynamic::new(header, [v1]).unwrap(); + + let values = dynamic.borrow_mut().unwrap(); + + assert!(dynamic.borrow_ref().is_err()); + drop(values); + assert!(dynamic.borrow_ref().is_ok()); +} + +#[test] +fn dynamic_borrow_mut_err() -> crate::support::Result<()> { + let a = Dynamic::new((), [Count(0)])?; + + { + let mut a_mut = a.borrow_mut()?; + a_mut[0].0 += 1; + assert_eq!(a_mut[0].0, 1); + assert!(a.borrow_ref().is_err()); + } + + let a = a.borrow_ref()?; + assert_eq!(a[0].0, 1); + Ok(()) +} + +#[test] +fn dynamic_take() -> crate::support::Result<()> { + let a = Dynamic::new((), [Count(0)])?; + let b = a.clone(); + + { + let mut a = a.borrow_mut()?; + // NB: this is prevented since we have a live reference. + assert!(b.take().is_err()); + a[0].0 += 1; + } + + let a = a.take()?; + assert_eq!(a.borrow_ref()?[0].0, 1); + Ok(()) +} + +#[test] +fn dynamic_is_readable() -> crate::support::Result<()> { + let dynamic = Dynamic::new((), [1u32])?; + assert!(dynamic.is_readable()); + + { + let _guard = dynamic.borrow_ref()?; + assert!(dynamic.is_readable()); // Note: still readable. + } + + { + let _guard = dynamic.borrow_mut()?; + assert!(!dynamic.is_readable()); + } + + assert!(dynamic.is_readable()); + Ok(()) +} + +#[test] +fn dynamic_is_writable_take() -> crate::support::Result<()> { + let shared = Dynamic::new((), [1u32])?; + let shared2 = shared.clone(); + assert!(shared.is_readable()); + shared.take()?; + assert!(!shared2.is_readable()); + assert!(shared2.take().is_err()); + Ok(()) +} + +#[test] +fn dynamic_is_writable() -> crate::support::Result<()> { + let shared = Dynamic::new((), [1u32])?; + assert!(shared.is_writable()); + + { + let _guard = shared.borrow_ref()?; + assert!(!shared.is_writable()); + } + + assert!(shared.is_writable()); + Ok(()) +} diff --git a/crates/rune/src/runtime/variant.rs b/crates/rune/src/runtime/variant.rs deleted file mode 100644 index e079d577a..000000000 --- a/crates/rune/src/runtime/variant.rs +++ /dev/null @@ -1,217 +0,0 @@ -use core::cmp::Ordering; -use core::fmt; - -use ::rust_alloc::sync::Arc; - -use crate as rune; -use crate::alloc::clone::TryClone; -use crate::alloc::Box; - -use super::{ - Accessor, FromValue, Mutable, OwnedTuple, ProtocolCaller, ReprOwned, RuntimeError, Tuple, - TypeInfo, Value, VariantRtti, Vec, VmResult, -}; - -/// The variant of a type. -#[derive(TryClone)] -pub struct Variant { - pub(crate) rtti: Arc, - pub(crate) data: VariantData, -} - -impl Variant { - /// Construct a field accessor from this variant. - #[doc(hidden)] - pub fn accessor<'a>(&'a self, data: &'a [Value]) -> Accessor<'a> { - Accessor { - fields: &self.rtti.fields, - data, - } - } - - /// Try to access variant data as a tuple. - pub fn as_tuple(&self) -> Option<&Tuple> { - match &self.data { - VariantData::Tuple(tuple) => Some(tuple), - _ => None, - } - } - - /// Construct a unit variant. - pub(crate) fn unit(rtti: Arc) -> Self { - Self { - rtti, - data: VariantData::Empty, - } - } - - /// Construct a tuple variant. - pub(crate) fn tuple(rtti: Arc, tuple: OwnedTuple) -> Self { - Self { - rtti, - data: VariantData::Tuple(tuple), - } - } - - /// Construct a struct variant. - pub(crate) fn struct_(rtti: Arc, data: Box<[Value]>) -> Self { - Self { - rtti, - data: VariantData::Struct(data), - } - } - - /// Access the rtti of the variant. - pub fn rtti(&self) -> &VariantRtti { - &self.rtti - } - - /// Access the underlying variant data. - pub fn data(&self) -> &VariantData { - &self.data - } - - /// Access the underlying variant data mutably. - pub(crate) fn data_mut(&mut self) -> &mut VariantData { - &mut self.data - } - - /// Get type info for the variant. - pub(crate) fn type_info(&self) -> TypeInfo { - TypeInfo::variant(self.rtti.clone()) - } - - pub(crate) fn partial_eq_with( - a: &Self, - b: &Self, - caller: &mut dyn ProtocolCaller, - ) -> VmResult { - debug_assert_eq!( - a.rtti.enum_hash, b.rtti.enum_hash, - "comparison only makes sense if enum hashes match" - ); - - if a.rtti.hash != b.rtti.hash { - return VmResult::Ok(false); - } - - match (&a.data, &b.data) { - (VariantData::Empty, VariantData::Empty) => VmResult::Ok(true), - (VariantData::Tuple(a), VariantData::Tuple(b)) => { - Vec::eq_with(a, b, Value::partial_eq_with, caller) - } - (VariantData::Struct(a), VariantData::Struct(b)) => { - Vec::eq_with(a, b, Value::partial_eq_with, caller) - } - _ => VmResult::panic("data mismatch between variants"), - } - } - - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut dyn ProtocolCaller) -> VmResult { - debug_assert_eq!( - a.rtti.enum_hash, b.rtti.enum_hash, - "comparison only makes sense if enum hashes match" - ); - - if a.rtti.hash != b.rtti.hash { - return VmResult::Ok(false); - } - - match (&a.data, &b.data) { - (VariantData::Empty, VariantData::Empty) => VmResult::Ok(true), - (VariantData::Tuple(a), VariantData::Tuple(b)) => { - Vec::eq_with(a, b, Value::eq_with, caller) - } - (VariantData::Struct(a), VariantData::Struct(b)) => { - Vec::eq_with(a, b, Value::eq_with, caller) - } - _ => VmResult::panic("data mismatch between variants"), - } - } - - pub(crate) fn partial_cmp_with( - a: &Self, - b: &Self, - caller: &mut dyn ProtocolCaller, - ) -> VmResult> { - debug_assert_eq!( - a.rtti.enum_hash, b.rtti.enum_hash, - "comparison only makes sense if enum hashes match" - ); - - match a.rtti.hash.partial_cmp(&b.rtti.hash) { - Some(Ordering::Equal) => {} - ordering => return VmResult::Ok(ordering), - } - - match (&a.data, &b.data) { - (VariantData::Empty, VariantData::Empty) => VmResult::Ok(Some(Ordering::Equal)), - (VariantData::Tuple(a), VariantData::Tuple(b)) => Vec::partial_cmp_with(a, b, caller), - (VariantData::Struct(a), VariantData::Struct(b)) => Vec::partial_cmp_with(a, b, caller), - _ => VmResult::panic("data mismatch between variants"), - } - } - - pub(crate) fn cmp_with( - a: &Self, - b: &Self, - caller: &mut dyn ProtocolCaller, - ) -> VmResult { - debug_assert_eq!( - a.rtti.enum_hash, b.rtti.enum_hash, - "comparison only makes sense if enum hashes match" - ); - - match a.rtti.hash.cmp(&b.rtti.hash) { - Ordering::Equal => {} - ordering => return VmResult::Ok(ordering), - } - - match (&a.data, &b.data) { - (VariantData::Empty, VariantData::Empty) => VmResult::Ok(Ordering::Equal), - (VariantData::Tuple(a), VariantData::Tuple(b)) => Vec::cmp_with(a, b, caller), - (VariantData::Struct(a), VariantData::Struct(b)) => Vec::cmp_with(a, b, caller), - _ => VmResult::panic("data mismatch between variants"), - } - } -} - -impl FromValue for Variant { - fn from_value(value: Value) -> Result { - match value.take_repr()? { - ReprOwned::Inline(value) => Err(RuntimeError::expected_variant(value.type_info())), - ReprOwned::Mutable(Mutable::Variant(value)) => Ok(value), - ReprOwned::Mutable(value) => Err(RuntimeError::expected_variant(value.type_info())), - ReprOwned::Any(value) => Err(RuntimeError::expected_variant(value.type_info())), - } - } -} - -/// The data of the variant. -#[derive(TryClone)] -pub enum VariantData { - /// A unit variant. - Empty, - /// A struct variant. - Struct(Box<[Value]>), - /// A tuple variant. - Tuple(OwnedTuple), -} - -impl fmt::Debug for Variant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.rtti.item)?; - - match &self.data { - VariantData::Empty => {} - VariantData::Struct(st) => { - write!(f, "{:?}", st)?; - } - VariantData::Tuple(tuple) => { - write!(f, "{:?}", tuple)?; - } - } - - Ok(()) - } -} diff --git a/crates/rune/src/runtime/vec.rs b/crates/rune/src/runtime/vec.rs index 80540e76c..f6f37f1c9 100644 --- a/crates/rune/src/runtime/vec.rs +++ b/crates/rune/src/runtime/vec.rs @@ -574,6 +574,22 @@ impl UnsafeToRef for [Value] { } } +impl ToValue for [T; N] +where + T: ToValue, +{ + fn to_value(self) -> Result { + let mut inner = alloc::Vec::try_with_capacity(self.len())?; + + for value in self { + let value = value.to_value()?; + inner.try_push(value)?; + } + + Ok(Value::try_from(Vec { inner })?) + } +} + impl ToValue for alloc::Vec where T: ToValue, diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 34c96c61a..a737b92ed 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -15,15 +15,14 @@ use crate::modules::{option, result}; use crate::runtime; use super::{ - budget, Args, Awaited, BorrowMut, Bytes, Call, ControlFlow, DynArgs, DynGuardedArgs, - EmptyStruct, Format, FormatSpec, Formatter, FromValue, Function, Future, Generator, - GeneratorState, GuardedArgs, Inline, Inst, InstAddress, InstAssignOp, InstOp, InstRange, - InstTarget, InstValue, InstVariant, Mutable, Object, Output, OwnedTuple, Pair, Panic, Protocol, - ProtocolCaller, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, - ReprMut, ReprRef, RuntimeContext, Select, SelectFuture, Stack, Stream, Struct, Type, TypeCheck, - TypeHash, TypeInfo, TypeOf, Unit, UnitFn, UnitStorage, Value, Variant, VariantData, Vec, - VmDiagnostics, VmDiagnosticsObj, VmError, VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, - VmResult, VmSendExecution, + budget, Args, Awaited, BorrowMut, Bytes, Call, ControlFlow, DynArgs, DynGuardedArgs, Dynamic, + Format, FormatSpec, Formatter, FromValue, Function, Future, Generator, GeneratorState, + GuardedArgs, Inline, Inst, InstAddress, InstAssignOp, InstOp, InstRange, InstTarget, InstValue, + InstVariant, Object, Output, OwnedTuple, Pair, Panic, Protocol, ProtocolCaller, Range, + RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, ReprMut, ReprRef, RttiKind, + RuntimeContext, Select, SelectFuture, Stack, Stream, Type, TypeCheck, TypeHash, TypeInfo, + TypeOf, Unit, UnitFn, UnitStorage, Value, Vec, VmDiagnostics, VmDiagnosticsObj, VmError, + VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, VmResult, VmSendExecution, }; /// Helper to take a value, replacing the old one with empty. @@ -824,22 +823,10 @@ impl Vm { /// Implementation of getting a string index on an object-like type. fn try_object_like_index_get(target: &Value, field: &str) -> VmResult> { match vm_try!(target.as_ref()) { - ReprRef::Inline(..) => VmResult::Ok(None), - ReprRef::Mutable(target) => { - let target = vm_try!(target.borrow_ref()); - - let value = match &*target { - Mutable::Struct(target) => target.get(field), - Mutable::Variant(variant) => match variant.data() { - VariantData::Struct(target) => variant.rtti.get_field(target, field), - _ => return VmResult::Ok(None), - }, - _ => return VmResult::Ok(None), - }; - - let Some(value) = value else { + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => { + let Some(value) = vm_try!(data.get_field_ref(field)) else { return err(VmErrorKind::MissingField { - target: target.type_info(), + target: data.type_info(), field: vm_try!(field.try_to_owned()), }); }; @@ -861,6 +848,7 @@ impl Vm { } _ => VmResult::Ok(None), }, + _ => VmResult::Ok(None), } } @@ -871,24 +859,13 @@ impl Vm { Inline::Unit => Err(target.type_info()), _ => return VmResult::Ok(None), }, - ReprRef::Mutable(target) => { - let target = vm_try!(target.borrow_ref()); - - match &*target { - Mutable::TupleStruct(tuple_struct) => match tuple_struct.data().get(index) { - Some(value) => Ok(value.clone()), - None => Err(target.type_info()), - }, - Mutable::Variant(variant) => match variant.data() { - VariantData::Tuple(tuple) => match tuple.get(index) { - Some(value) => Ok(value.clone()), - None => Err(target.type_info()), - }, - _ => return VmResult::Ok(None), - }, - _ => return VmResult::Ok(None), + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => { + match vm_try!(data.get_ref(index)) { + Some(value) => Ok(value.clone()), + None => Err(data.type_info()), } } + ReprRef::Dynamic(data) => Err(data.type_info()), ReprRef::Any(target) => match target.type_hash() { Result::::HASH => { match ( @@ -948,37 +925,20 @@ impl Vm { index: usize, ) -> VmResult>> { match vm_try!(target.as_ref()) { - ReprRef::Mutable(value) => { - let mut unsupported = false; - - let result = BorrowMut::try_map(vm_try!(value.borrow_mut()), |kind| { - match kind { - Mutable::TupleStruct(tuple_struct) => return tuple_struct.get_mut(index), - Mutable::Variant(Variant { - data: VariantData::Tuple(tuple), - .. - }) => { - return tuple.get_mut(index); - } - _ => {} - } - - unsupported = true; - None - }); - - if unsupported { - return VmResult::Ok(None); - } - - match result { - Ok(value) => VmResult::Ok(Some(value)), - Err(actual) => err(VmErrorKind::MissingIndexInteger { - target: actual.type_info(), + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => { + let Some(value) = vm_try!(data.get_mut(index)) else { + return err(VmErrorKind::MissingIndexInteger { + target: data.type_info(), index: VmIntegerRepr::from(index), - }), - } + }); + }; + + VmResult::Ok(Some(value)) } + ReprRef::Dynamic(data) => err(VmErrorKind::MissingIndexInteger { + target: data.type_info(), + index: VmIntegerRepr::from(index), + }), ReprRef::Any(value) => match value.type_hash() { Result::::HASH => { let result = BorrowMut::try_map( @@ -1074,46 +1034,17 @@ impl Vm { field: &str, ) -> VmResult>> { match vm_try!(target.as_ref()) { - ReprRef::Inline(actual) => err(VmErrorKind::MissingField { - target: actual.type_info(), + ReprRef::Inline(value) => err(VmErrorKind::MissingField { + target: value.type_info(), field: vm_try!(field.try_to_owned()), }), - ReprRef::Mutable(target) => { - let target = vm_try!(target.borrow_mut()); - - let mut unsupported = false; - - let result = BorrowMut::try_map(target, |value| { - match *value { - Mutable::Struct(ref mut target) => { - return target.get_mut(field); - } - Mutable::Variant(Variant { - ref rtti, - data: VariantData::Struct(ref mut st), - .. - }) => { - return rtti.get_field_mut(st, field); - } - _ => {} - } - - unsupported = true; - None - }); - - if unsupported { - return VmResult::Ok(None); - } - - match result { - Ok(value) => VmResult::Ok(Some(value)), - Err(actual) => err(VmErrorKind::MissingField { - target: actual.type_info(), - field: vm_try!(field.try_to_owned()), - }), - } + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => { + VmResult::Ok(vm_try!(data.get_field_mut(field))) } + ReprRef::Dynamic(data) => err(VmErrorKind::MissingField { + target: data.type_info(), + field: vm_try!(field.try_to_owned()), + }), ReprRef::Any(value) => match value.type_hash() { Object::HASH => { let object = vm_try!(value.borrow_mut::()); @@ -1139,27 +1070,15 @@ impl Vm { Inline::Unit => VmResult::Ok(false), _ => VmResult::Ok(false), }, - ReprRef::Mutable(target) => match &mut *vm_try!(target.borrow_mut()) { - Mutable::TupleStruct(tuple_struct) => { - if let Some(target) = tuple_struct.get_mut(index) { - target.clone_from(from); - return VmResult::Ok(true); - } - - VmResult::Ok(false) + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => { + if let Some(target) = vm_try!(data.borrow_mut()).get_mut(index) { + target.clone_from(from); + return VmResult::Ok(true); } - Mutable::Variant(variant) => { - if let VariantData::Tuple(data) = variant.data_mut() { - if let Some(target) = data.get_mut(index) { - target.clone_from(from); - return VmResult::Ok(true); - } - } - VmResult::Ok(false) - } - _ => VmResult::Ok(false), - }, + VmResult::Ok(false) + } + ReprRef::Dynamic(..) => VmResult::Ok(false), ReprRef::Any(value) => match value.type_hash() { Result::::HASH => { let mut result = vm_try!(value.borrow_mut::>()); @@ -1220,30 +1139,12 @@ impl Vm { 'fallback: { match vm_try!(target.as_ref()) { - ReprRef::Inline(..) => { - return VmResult::Ok(CallResult::Unsupported(target.clone())); - } - ReprRef::Mutable(target) => match *vm_try!(target.borrow_ref()) { - Mutable::Struct(ref typed_object) => { - if let Some(value) = typed_object.get(index.as_str()) { - vm_try!(out.store(&mut self.stack, || value.clone())); - return VmResult::Ok(CallResult::Ok(())); - } - } - Mutable::Variant(Variant { - ref rtti, - data: VariantData::Struct(ref data), - .. - }) => { - if let Some(value) = rtti.get_field(data, index.as_str()) { - vm_try!(out.store(&mut self.stack, || value.clone())); - return VmResult::Ok(CallResult::Ok(())); - } + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => { + if let Some(value) = vm_try!(data.get_field_ref(index.as_str())) { + vm_try!(out.store(&mut self.stack, || value.clone())); + return VmResult::Ok(CallResult::Ok(())); } - _ => { - break 'fallback; - } - }, + } ReprRef::Any(value) => match value.type_hash() { Object::HASH => { let object = vm_try!(value.borrow_ref::()); @@ -1255,6 +1156,9 @@ impl Vm { } _ => break 'fallback, }, + _ => { + return VmResult::Ok(CallResult::Unsupported(target.clone())); + } } return err(VmErrorKind::ObjectIndexMissing { slot }); @@ -1267,35 +1171,14 @@ impl Vm { fn try_object_slot_index_set(target: &Value, field: &str, value: &Value) -> VmResult { match vm_try!(target.as_ref()) { - ReprRef::Inline(target) => err(VmErrorKind::MissingField { - target: target.type_info(), - field: vm_try!(field.try_to_owned()), - }), - ReprRef::Mutable(target) => { - let mut target = vm_try!(target.borrow_mut()); - - match &mut *target { - Mutable::Struct(object) => { - if let Some(v) = object.get_mut(field) { - v.clone_from(value); - return VmResult::Ok(true); - } - } - Mutable::Variant(variant) => { - if let VariantData::Struct(data) = &mut variant.data { - if let Some(v) = variant.rtti.get_field_mut(data, field) { - v.clone_from(value); - return VmResult::Ok(true); - } - } - } - _ => { - return VmResult::Ok(false); - } + ReprRef::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => { + if let Some(mut v) = vm_try!(data.get_field_mut(field)) { + v.clone_from(value); + return VmResult::Ok(true); } err(VmErrorKind::MissingField { - target: target.type_info(), + target: data.type_info(), field: vm_try!(field.try_to_owned()), }) } @@ -1308,6 +1191,10 @@ impl Vm { } _ => VmResult::Ok(false), }, + target => err(VmErrorKind::MissingField { + target: target.type_info(), + field: vm_try!(field.try_to_owned()), + }), } } @@ -1320,7 +1207,6 @@ impl Vm { (TypeCheck::Unit, Inline::Unit) => Some(f(&[])), _ => None, }, - ReprRef::Mutable(..) => None, ReprRef::Any(value) => match (ty, value.type_hash()) { (TypeCheck::Vec, runtime::Vec::HASH) => { let vec = vm_try!(value.borrow_ref::()); @@ -1332,6 +1218,7 @@ impl Vm { } _ => None, }, + _ => None, }; VmResult::Ok(value) @@ -1371,7 +1258,7 @@ impl Vm { ReprRef::Inline(Inline::Float(a)) => convert!(f64, *a), value => { return err(VmErrorKind::UnsupportedAs { - value: vm_try!(value.type_info()), + value: value.type_info(), type_hash: ty.into_hash(), }); } @@ -1420,8 +1307,8 @@ impl Vm { (lhs, rhs) => { return err(VmErrorKind::UnsupportedBinaryOperation { op, - lhs: vm_try!(lhs.type_info()), - rhs: vm_try!(rhs.type_info()), + lhs: lhs.type_info(), + rhs: rhs.type_info(), }); } }; @@ -1607,7 +1494,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => {} @@ -1726,7 +1613,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => { @@ -1795,7 +1682,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => { @@ -1896,7 +1783,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => {} @@ -1976,7 +1863,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => {} @@ -2072,7 +1959,7 @@ impl Vm { return err(VmErrorKind::UnsupportedBinaryOperation { op: protocol.name, lhs: lhs.type_info(), - rhs: vm_try!(rhs.type_info()), + rhs: rhs.type_info(), }); } _ => {} @@ -2269,7 +2156,7 @@ impl Vm { } }, actual => { - let operand = vm_try!(actual.type_info()); + let operand = actual.type_info(); return err(VmErrorKind::UnsupportedUnaryOperation { op: "!", operand }); } }; @@ -2292,7 +2179,7 @@ impl Vm { } }, actual => { - let operand = vm_try!(actual.type_info()); + let operand = actual.type_info(); return err(VmErrorKind::UnsupportedUnaryOperation { op: "-", operand }); } }; @@ -2714,22 +2601,6 @@ impl Vm { Function::from_tuple_struct(rtti.clone(), args) } - UnitFn::UnitVariant { hash } => { - let rtti = self - .unit - .lookup_variant_rtti(hash) - .ok_or(VmErrorKind::MissingVariantRtti { hash })?; - - Function::from_unit_variant(rtti.clone()) - } - UnitFn::TupleVariant { hash, args } => { - let rtti = self - .unit - .lookup_variant_rtti(hash) - .ok_or(VmErrorKind::MissingVariantRtti { hash })?; - - Function::from_tuple_variant(rtti.clone(), args) - } }, None => { let handler = self @@ -2999,11 +2870,10 @@ impl Vm { .lookup_rtti(hash) .ok_or(VmErrorKind::MissingRtti { hash })); - vm_try!(out.store( - &mut self.stack, - Mutable::EmptyStruct(EmptyStruct { rtti: rtti.clone() }) - )); - + vm_try!(out.store(&mut self.stack, || Dynamic::<_, Value>::new( + rtti.clone(), + [] + ))); VmResult::Ok(()) } @@ -3016,20 +2886,8 @@ impl Vm { .ok_or(VmErrorKind::MissingRtti { hash })); let values = vm_try!(self.stack.slice_at_mut(addr, rtti.fields.len())); - let mut data = vm_try!(alloc::Vec::try_with_capacity(rtti.fields.len())); - - for value in values { - vm_try!(data.try_push(take(value))); - } - - vm_try!(out.store( - &mut self.stack, - Mutable::Struct(Struct { - rtti: rtti.clone(), - data: vm_try!(data.try_into()), - }) - )); - + let value = vm_try!(Dynamic::new(rtti.clone(), values.iter_mut().map(take))); + vm_try!(out.store(&mut self.stack, value)); VmResult::Ok(()) } @@ -3053,29 +2911,6 @@ impl Vm { VmResult::Ok(()) } - /// Operation to allocate an object variant. - #[cfg_attr(feature = "bench", inline(never))] - fn op_struct_variant(&mut self, addr: InstAddress, hash: Hash, out: Output) -> VmResult<()> { - let rtti = vm_try!(self - .unit - .lookup_variant_rtti(hash) - .ok_or(VmErrorKind::MissingVariantRtti { hash })); - - let mut data = vm_try!(alloc::Vec::try_with_capacity(rtti.fields.len())); - let values = vm_try!(self.stack.slice_at_mut(addr, rtti.fields.len())); - - for value in values { - vm_try!(data.try_push(take(value))); - } - - vm_try!(out.store( - &mut self.stack, - Mutable::Variant(Variant::struct_(rtti.clone(), vm_try!(data.try_into()))) - )); - - VmResult::Ok(()) - } - #[cfg_attr(feature = "bench", inline(never))] fn op_string(&mut self, slot: usize, out: Output) -> VmResult<()> { let string = vm_try!(self.unit.lookup_string(slot)); @@ -3310,15 +3145,9 @@ impl Vm { let is_match = 'out: { match vm_try!(value.as_ref()) { - ReprRef::Mutable(value) => match &*vm_try!(value.borrow_ref()) { - Mutable::Variant(variant) => { - let rtti = variant.rtti(); - break 'out rtti.enum_hash == enum_hash && rtti.hash == variant_hash; - } - _ => { - break 'out false; - } - }, + ReprRef::Dynamic(value) => { + break 'out value.rtti().is(enum_hash, variant_hash); + } ReprRef::Any(any) => match enum_hash { Result::::HASH => { let result = vm_try!(any.borrow_ref::>()); @@ -3379,7 +3208,7 @@ impl Vm { (TypeCheck::Unit, Inline::Unit) => true, _ => false, }, - ReprRef::Mutable(..) => false, + ReprRef::Dynamic(..) => false, ReprRef::Any(value) => match (type_check, value.type_hash()) { (TypeCheck::Vec, runtime::Vec::HASH) => true, (TypeCheck::Tuple, runtime::OwnedTuple::HASH) => true, @@ -3564,40 +3393,9 @@ impl Vm { .ok_or(VmErrorKind::MissingRtti { hash })); let tuple = vm_try!(self.stack.slice_at_mut(addr, args)); - let tuple = vm_try!(tuple.iter_mut().map(take).try_collect()); - - vm_try!(out.store(&mut self.stack, || { - Value::tuple_struct(rtti.clone(), tuple) - })); - } - UnitFn::TupleVariant { - hash, - args: expected, - } => { - vm_try!(check_args(args, expected)); - - let rtti = vm_try!(self - .unit - .lookup_variant_rtti(hash) - .ok_or(VmErrorKind::MissingVariantRtti { hash })); - - let tuple = vm_try!(self.stack.slice_at_mut(addr, args)); - let tuple = vm_try!(tuple.iter_mut().map(take).try_collect()); - - vm_try!(out.store(&mut self.stack, || Value::tuple_variant( - rtti.clone(), - tuple - ))); - } - UnitFn::UnitVariant { hash } => { - vm_try!(check_args(args, 0)); - - let rtti = vm_try!(self - .unit - .lookup_variant_rtti(hash) - .ok_or(VmErrorKind::MissingVariantRtti { hash })); - - vm_try!(out.store(&mut self.stack, || Value::unit_variant(rtti.clone()))); + let data = tuple.iter_mut().map(take); + let value = vm_try!(Dynamic::new(rtti.clone(), data)); + vm_try!(out.store(&mut self.stack, value)); } } @@ -3684,8 +3482,8 @@ impl Vm { ReprRef::Inline(value) => err(VmErrorKind::UnsupportedCallFn { actual: value.type_info(), }), - ReprRef::Mutable(value) => err(VmErrorKind::UnsupportedCallFn { - actual: vm_try!(value.borrow_ref()).type_info(), + ReprRef::Dynamic(value) => err(VmErrorKind::UnsupportedCallFn { + actual: value.type_info(), }), ReprRef::Any(value) => { let value = value.clone(); @@ -3719,7 +3517,7 @@ impl Vm { }, actual => { return err(VmErrorKind::UnsupportedIterNextOperand { - actual: vm_try!(actual.type_info()), + actual: actual.type_info(), }); } }; @@ -3958,9 +3756,6 @@ impl Vm { } => { vm_try!(self.op_const_construct(addr, hash, count, out)); } - Inst::StructVariant { addr, hash, out } => { - vm_try!(self.op_struct_variant(addr, hash, out)); - } Inst::String { slot, out } => { vm_try!(self.op_string(slot, out)); } diff --git a/crates/rune/src/runtime/vm_error.rs b/crates/rune/src/runtime/vm_error.rs index 464acf74c..b37672a01 100644 --- a/crates/rune/src/runtime/vm_error.rs +++ b/crates/rune/src/runtime/vm_error.rs @@ -11,8 +11,8 @@ use crate::compile::meta; use crate::runtime::unit::{BadInstruction, BadJump}; use crate::runtime::{ AccessError, AccessErrorKind, AnyObjError, AnyObjErrorKind, AnyTypeInfo, BoxedPanic, CallFrame, - DynArgsUsed, ExecutionState, MaybeTypeOf, Panic, Protocol, SliceError, StackError, TypeInfo, - TypeOf, Unit, Vm, VmHaltInfo, + DynArgsUsed, DynamicTakeError, ExecutionState, MaybeTypeOf, Panic, Protocol, SliceError, + StackError, TypeInfo, TypeOf, Unit, Vm, VmHaltInfo, }; use crate::{Any, Hash, ItemBuf}; @@ -441,26 +441,6 @@ impl RuntimeError { }) } - /// Construct an expected error for a variant. - pub(crate) fn expected_variant(actual: TypeInfo) -> Self { - Self::new(VmErrorKind::ExpectedVariant { actual }) - } - - /// Construct an expected expecting a unit struct. - pub(crate) fn expected_unit_struct(actual: TypeInfo) -> Self { - Self::new(VmErrorKind::ExpectedUnitStruct { actual }) - } - - /// Construct an expected expecting a tuple struct. - pub(crate) fn expected_tuple_struct(actual: TypeInfo) -> Self { - Self::new(VmErrorKind::ExpectedTupleStruct { actual }) - } - - /// Construct an expected expecting a struct. - pub(crate) fn expected_struct(actual: TypeInfo) -> Self { - Self::new(VmErrorKind::ExpectedStruct { actual }) - } - /// Construct an expected error from any. pub(crate) fn expected_any(actual: TypeInfo) -> Self where @@ -481,6 +461,18 @@ impl RuntimeError { pub(crate) fn missing_constant_constructor(hash: Hash) -> Self { Self::new(VmErrorKind::MissingConstantConstructor { hash }) } + + pub(crate) fn expected_empty(actual: TypeInfo) -> Self { + Self::new(VmErrorKind::ExpectedEmpty { actual }) + } + + pub(crate) fn expected_tuple(actual: TypeInfo) -> Self { + Self::new(VmErrorKind::ExpectedTuple { actual }) + } + + pub(crate) fn expected_struct(actual: TypeInfo) -> Self { + Self::new(VmErrorKind::ExpectedStruct { actual }) + } } #[allow(non_snake_case)] @@ -549,6 +541,16 @@ impl From for RuntimeError { } } +impl From for RuntimeError { + #[inline] + fn from(value: DynamicTakeError) -> Self { + match value { + DynamicTakeError::Access(error) => Self::from(error), + DynamicTakeError::Alloc(error) => Self::from(error), + } + } +} + impl From for RuntimeError { #[inline] fn from(error: VmError) -> Self { @@ -692,9 +694,6 @@ pub(crate) enum VmErrorKind { MissingStaticObjectKeys { slot: usize, }, - MissingVariantRtti { - hash: Hash, - }, MissingRtti { hash: Hash, }, @@ -787,6 +786,15 @@ pub(crate) enum VmErrorKind { ExpectedNumber { actual: TypeInfo, }, + ExpectedEmpty { + actual: TypeInfo, + }, + ExpectedTuple { + actual: TypeInfo, + }, + ExpectedStruct { + actual: TypeInfo, + }, MissingConstantConstructor { hash: Hash, }, @@ -834,15 +842,6 @@ pub(crate) enum VmErrorKind { ExpectedVariant { actual: TypeInfo, }, - ExpectedUnitStruct { - actual: TypeInfo, - }, - ExpectedTupleStruct { - actual: TypeInfo, - }, - ExpectedStruct { - actual: TypeInfo, - }, UnsupportedObjectFieldGet { target: TypeInfo, }, @@ -911,10 +910,6 @@ impl fmt::Display for VmErrorKind { VmErrorKind::MissingStaticObjectKeys { slot } => { write!(f, "Static object keys slot `{slot}` does not exist") } - VmErrorKind::MissingVariantRtti { hash } => write!( - f, - "Missing runtime information for variant with hash `{hash}`", - ), VmErrorKind::MissingRtti { hash } => { write!(f, "Missing runtime information for type with hash `{hash}`") } @@ -1003,6 +998,15 @@ impl fmt::Display for VmErrorKind { VmErrorKind::ExpectedNumber { actual } => { write!(f, "Expected number type, but found `{actual}`") } + VmErrorKind::ExpectedEmpty { actual } => { + write!(f, "Expected empty, but found `{actual}`") + } + VmErrorKind::ExpectedTuple { actual } => { + write!(f, "Expected tuple, but found `{actual}`") + } + VmErrorKind::ExpectedStruct { actual } => { + write!(f, "Expected struct, but found `{actual}`") + } VmErrorKind::MissingConstantConstructor { hash } => { write!(f, "Missing constant constructor for type with hash {hash}") } @@ -1050,15 +1054,6 @@ impl fmt::Display for VmErrorKind { VmErrorKind::ExpectedVariant { actual } => { write!(f, "Expected an enum variant, but got `{actual}`") } - VmErrorKind::ExpectedUnitStruct { actual } => { - write!(f, "Expected a unit struct, but got `{actual}`") - } - VmErrorKind::ExpectedTupleStruct { actual } => { - write!(f, "Expected a tuple struct, but got `{actual}`") - } - VmErrorKind::ExpectedStruct { actual } => { - write!(f, "Expected a struct, but got `{actual}`") - } VmErrorKind::UnsupportedObjectFieldGet { target } => write!( f, "The object field get operation is not supported on `{target}`", @@ -1090,6 +1085,16 @@ impl From for VmErrorKind { } } +impl From for VmErrorKind { + #[inline] + fn from(value: DynamicTakeError) -> Self { + match value { + DynamicTakeError::Access(error) => Self::from(error), + DynamicTakeError::Alloc(error) => Self::from(error), + } + } +} + impl From for VmErrorKind { #[inline] fn from(error: AnyObjError) -> Self { diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index d165aa647..9df349c0e 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -16,9 +16,9 @@ pub(crate) mod prelude { pub(crate) use crate::module::InstallWith; pub(crate) use crate::parse; pub(crate) use crate::runtime::{ - self, Bytes, Formatter, Function, InstAddress, MaybeTypeOf, Mutable, Object, Output, - OwnedTuple, Protocol, RawAnyGuard, Ref, ReprOwned, Stack, Tuple, TupleStruct, TypeHash, - TypeInfo, TypeOf, UnsafeToRef, Variant, VecTuple, VmErrorKind, VmResult, + self, Bytes, DynamicTuple, Formatter, Function, InstAddress, MaybeTypeOf, Object, Output, + OwnedTuple, Protocol, RawAnyGuard, Ref, Stack, Tuple, TypeHash, TypeInfo, TypeOf, + UnsafeToRef, VecTuple, VmErrorKind, VmResult, }; pub(crate) use crate::support::Result; pub(crate) use crate::tests::{eval, run}; @@ -506,8 +506,6 @@ mod unit_constants; #[cfg(not(miri))] mod unreachable; #[cfg(not(miri))] -mod variants; -#[cfg(not(miri))] mod vm_arithmetic; #[cfg(not(miri))] mod vm_assign_exprs; diff --git a/crates/rune/src/tests/function_guardedargs.rs b/crates/rune/src/tests/function_guardedargs.rs index 929bf28c2..46ff3e3b9 100644 --- a/crates/rune/src/tests/function_guardedargs.rs +++ b/crates/rune/src/tests/function_guardedargs.rs @@ -52,16 +52,13 @@ fn references_disallowed_for_tuple_variant() { let mut mine = MyAny; - let tuple = constructor.call::((&mine,)).unwrap(); - let tuple = tuple.as_tuple().unwrap(); + let tuple = constructor.call::((&mine,)).unwrap(); assert!(tuple.first().unwrap().borrow_ref::().is_err()); - let tuple = constructor.call::((&mut mine,)).unwrap(); - let tuple = tuple.as_tuple().unwrap(); + let tuple = constructor.call::((&mut mine,)).unwrap(); assert!(tuple.first().unwrap().borrow_ref::().is_err()); - let tuple = constructor.call::((mine,)).unwrap(); - let tuple = tuple.as_tuple().unwrap(); + let tuple = constructor.call::((mine,)).unwrap(); assert!(tuple.first().unwrap().borrow_ref::().is_ok()); } @@ -75,12 +72,12 @@ fn references_disallowed_for_tuple_struct() { let mut mine = MyAny; - let st: TupleStruct = constructor.call::((&mine,)).unwrap(); - assert!(st.data().first().unwrap().borrow_ref::().is_err()); + let st = constructor.call::((&mine,)).unwrap(); + assert!(st.first().unwrap().borrow_ref::().is_err()); - let st: TupleStruct = constructor.call::((&mut mine,)).unwrap(); - assert!(st.data().first().unwrap().borrow_ref::().is_err()); + let st = constructor.call::((&mut mine,)).unwrap(); + assert!(st.first().unwrap().borrow_ref::().is_err()); - let st: TupleStruct = constructor.call::((mine,)).unwrap(); - assert!(st.data().first().unwrap().borrow_ref::().is_ok()); + let st = constructor.call::((mine,)).unwrap(); + assert!(st.first().unwrap().borrow_ref::().is_ok()); } diff --git a/crates/rune/src/tests/variants.rs b/crates/rune/src/tests/variants.rs deleted file mode 100644 index a381b8e22..000000000 --- a/crates/rune/src/tests/variants.rs +++ /dev/null @@ -1,33 +0,0 @@ -prelude!(); - -/// Tests that different variants of the same enum can be compared to each other -/// See: https://github.com/rune-rs/rune/pull/215 -#[test] -fn assert_variant_comparisons() { - let _: () = rune! { - enum Foo { A, B } - - pub fn main() { - assert!(Foo::A != Foo::B); - assert_eq!(Foo::A, Foo::A); - } - }; - - let _: () = rune! { - enum Foo { A(a), B } - - pub fn main() { - assert!(Foo::A(10) != Foo::B); - assert_eq!(Foo::A(10), Foo::A(10)); - } - }; - - let _: () = rune! { - enum Foo { A { a }, B } - - pub fn main() { - assert!(Foo::A { a: 10 } != Foo::B); - assert_eq!(Foo::A { a: 10 }, Foo::A { a: 10 }); - } - }; -} diff --git a/crates/rune/src/tests/vm_function.rs b/crates/rune/src/tests/vm_function.rs index 3bf06d34f..6eb2fa7b6 100644 --- a/crates/rune/src/tests/vm_function.rs +++ b/crates/rune/src/tests/vm_function.rs @@ -34,10 +34,7 @@ fn test_function() { assert!(function.call::(()).into_result().is_err()); let value: Value = function.call((1i64,)).unwrap(); - assert!(matches!( - value.take_repr().unwrap(), - ReprOwned::Mutable(Mutable::Variant(..)) - )); + assert!(rune::from_value::(value).is_ok()); // ptr to dynamic function. let function: Function = rune! { @@ -47,10 +44,7 @@ fn test_function() { assert!(function.call::(()).into_result().is_err()); let value: Value = function.call((1i64,)).unwrap(); - assert!(matches!( - value.take_repr().unwrap(), - ReprOwned::Mutable(Mutable::TupleStruct(..)) - )); + assert!(crate::from_value::(value).is_ok()); // non-capturing closure == free function let function: Function = rune! { diff --git a/crates/rune/tests/variants.rn b/crates/rune/tests/variants.rn new file mode 100644 index 000000000..a35d48750 --- /dev/null +++ b/crates/rune/tests/variants.rn @@ -0,0 +1,30 @@ +/// Tests that different variants of the same enum can be compared to each other +/// See: https://github.com/rune-rs/rune/pull/215 +#[test] +fn assert_variant_comparisons() { + enum Units { + A, + B, + } + + assert_ne!(Units::A, Units::B); + assert_eq!(Units::A, Units::A); + + enum Mixed1 { + A(a), + B, + } + + assert_ne!(Mixed1::A(10), Mixed1::B); + assert_eq!(Mixed1::A(10), Mixed1::A(10)); + + enum Mixed2 { + A { + a, + }, + B, + } + + assert_ne!(Mixed2::A { a: 10 }, Mixed2::B); + assert_eq!(Mixed2::A { a: 10 }, Mixed2::A { a: 10 }); +}