diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 955ae5b552..a34f6652f8 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -2846,7 +2846,7 @@ impl<'a> Typecheck<'a> { Some((found_arg_type, arg, ret)) if function_arg_type == Some(found_arg_type) || function_arg_type == None => { - return (found_arg_type, arg.clone(), ret.clone()) + return (found_arg_type, arg.clone(), ret.clone()); } _ => (), } diff --git a/codegen/src/getable.rs b/codegen/src/getable.rs index 66485e1481..ecb0d3a4bf 100644 --- a/codegen/src/getable.rs +++ b/codegen/src/getable.rs @@ -211,7 +211,7 @@ fn gen_impl( Ok(value) } - fn from_proxy(vm: &'__vm _gluon_thread::Thread, proxy: &'__value Self::Proxy) -> Self { + fn from_proxy(vm: &'__vm _gluon_thread::Thread, proxy: &'__value mut Self::Proxy) -> Self { Self::from_value(vm, *proxy) } diff --git a/codegen/src/userdata.rs b/codegen/src/userdata.rs index a18fea473d..81ba35ea18 100644 --- a/codegen/src/userdata.rs +++ b/codegen/src/userdata.rs @@ -1,6 +1,6 @@ use proc_macro2::{Ident, Span, TokenStream}; use shared::{map_lifetimes, map_type_params, split_for_impl}; -use syn::{self, Data, DeriveInput, GenericParam, Generics}; +use syn::{self, Data, DeriveInput, Generics}; use attr::{Container, CrateName}; @@ -48,12 +48,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr }, }; - let associated_type_generics = generics.params.iter().map(|param| match param { - GenericParam::Type(ty) => quote!( #ty :: Type ), - GenericParam::Lifetime(_) => quote!( 'static ), - GenericParam::Const(c) => quote!( #c ), - }); - let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site()); quote! { @@ -71,16 +65,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr #[automatically_derived] #[allow(unused_attributes, unused_variables)] impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics {} - - #[automatically_derived] - #[allow(unused_attributes, unused_variables)] - impl #impl_generics _gluon_api::VmType for #ident #ty_generics - #where_clause #(#trait_bounds,)* #(#lifetime_bounds),* - { - type Type = #ident< - #(#associated_type_generics),* - >; - } }; } } diff --git a/src/std_lib/io.rs b/src/std_lib/io.rs index 1635151271..a26c2f5339 100644 --- a/src/std_lib/io.rs +++ b/src/std_lib/io.rs @@ -49,7 +49,8 @@ fn eprintln(s: &str) -> IO<()> { IO::Value(()) } -#[derive(Userdata)] +#[derive(Userdata, VmType)] +#[gluon(vm_type = "std.io.File")] #[gluon(crate_name = "::vm")] struct GluonFile(Mutex>); diff --git a/src/std_lib/random.rs b/src/std_lib/random.rs index ffcdb81985..665c89ee41 100644 --- a/src/std_lib/random.rs +++ b/src/std_lib/random.rs @@ -10,7 +10,8 @@ use crate::vm::thread::Thread; use crate::vm::types::VmInt; use crate::vm::{self, ExternModule}; -#[derive(Clone, Debug, Userdata)] +#[derive(Clone, Debug, Userdata, VmType)] +#[gluon(vm_type = "std.random.XorShiftRng")] #[gluon(crate_name = "::vm")] struct XorShiftRng(self::rand_xorshift::XorShiftRng); diff --git a/src/std_lib/regex.rs b/src/std_lib/regex.rs index f444b9369f..3cc91a7d64 100644 --- a/src/std_lib/regex.rs +++ b/src/std_lib/regex.rs @@ -6,11 +6,13 @@ use crate::real_std::error::Error as StdError; use crate::vm::{self, api::Collect, thread::Thread, ExternModule}; -#[derive(Debug, Userdata)] +#[derive(Debug, Userdata, VmType)] +#[gluon(vm_type = "std.regex.Regex")] #[gluon(crate_name = "vm")] struct Regex(regex::Regex); -#[derive(Debug, Userdata)] +#[derive(Debug, Userdata, VmType)] +#[gluon(vm_type = "std.regex.Error")] #[gluon(crate_name = "vm")] struct Error(regex::Error); diff --git a/tests/api.rs b/tests/api.rs index bf93fc46ab..362a5a67c6 100644 --- a/tests/api.rs +++ b/tests/api.rs @@ -14,7 +14,7 @@ use gluon::{ import::{add_extern_module, Import}, vm::{ api::{ - de::De, scoped::Ref, FunctionRef, FutureResult, OpaqueValue, OwnedFunction, + de::De, scoped::{Ref, RefMut}, FunctionRef, FutureResult, OpaqueValue, OwnedFunction, RuntimeResult, Userdata, VmType, IO, }, thread::{RootedThread, Thread, Traverseable}, @@ -483,3 +483,47 @@ fn scoped_reference_out_of_scope() { Ok(_) => panic!("Unexpected success"), } } + +#[test] +fn scoped_mutable_reference() { + let _ = ::env_logger::try_init(); + + fn write(r: &mut Test, i: VmInt) -> IO<()> { + r.0 = i; + IO::Value(()) + } + + fn read(r: &mut Test) -> IO { + IO::Value(r.0) + } + + let expr = r#" + let { read, write } = import! function + let { ? } = import! std.io + \t i -> + seq write t (i + 10) + read t + "#; + + let vm = make_vm(); + vm.register_type::("Test", &[]) + .unwrap_or_else(|_| panic!("Could not add type")); + add_extern_module(&vm, "function", |thread| { + ExternModule::new(thread, record! { + write => primitive!(2, write), + read => primitive!(1, read) + }) + }); + + let (mut result, _) = Compiler::new() + .run_expr:: IO>>(&vm, "", expr) + .unwrap_or_else(|err| panic!("{}", err)); + + assert_eq!( + result + .call(&mut RefMut::new(&mut Test(2)), 3) + .unwrap_or_else(|err| panic!("{}", err)), + IO::Value(13) + ); +} + diff --git a/tests/pattern_match.rs b/tests/pattern_match.rs index 6fbd1ac855..3d5091e06b 100644 --- a/tests/pattern_match.rs +++ b/tests/pattern_match.rs @@ -5,8 +5,8 @@ extern crate gluon; #[macro_use] mod support; -use gluon::Compiler; use crate::support::*; +use gluon::Compiler; test_expr! { prelude match_on_bool, r#" diff --git a/vm/src/api/function.rs b/vm/src/api/function.rs index 423f176b0e..82e6060e62 100644 --- a/vm/src/api/function.rs +++ b/vm/src/api/function.rs @@ -330,7 +330,7 @@ where $($args: Getable<'vm, 'vm> + 'vm,)* let stack = StackFrame::::current(context.stack()); $( let variants = Variants::with_root(stack[i].clone(), vm); - let proxy = match $args::to_proxy(vm, variants) { + let mut proxy = match $args::to_proxy(vm, variants) { Ok(x) => x, Err(err) => { drop(stack); @@ -339,7 +339,7 @@ where $($args: Getable<'vm, 'vm> + 'vm,)* } }; // The proxy will live as along as the 'value lifetime we just created - let $args = $args::from_proxy(vm, &*(&proxy as *const _)); + let $args = $args::from_proxy(vm, &mut *(&mut proxy as *mut _)); i += 1; )* // Lock the frame to ensure that any references to the stack stay rooted diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index b9349a298e..f4a6fbf696 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -59,7 +59,7 @@ macro_rules! impl_getable_simple { Ok(value) } #[inline(always)] - fn from_proxy(vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self { + fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self { >::from_value(vm, *proxy) } }; @@ -549,11 +549,11 @@ where /// Trait which allows rust values to be retrieved from the virtual machine pub trait Getable<'vm, 'value>: Sized { - type Proxy; + type Proxy: 'value; fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self; fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result; - fn from_proxy(vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self; + fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self; } pub fn convert<'vm, T, U>(thread: &'vm Thread, t: T) -> Result @@ -674,7 +674,13 @@ where ValueRef::Userdata(data) => data.downcast_ref::().map_or_else( || { Ok(RefProxy::Lock( - data.downcast_ref::>().unwrap().read()?, + data.downcast_ref::>() + .map(|s| s.read()) + .or_else(|| { + data.downcast_ref::>() + .map(|s| s.read()) + }) + .unwrap()?, )) }, |x| Ok(RefProxy::Ref(x)), @@ -682,7 +688,7 @@ where _ => ice!("ValueRef is not an Userdata"), } } - fn from_proxy(_vm: &'vm Thread, proxy: &'value Self::Proxy) -> Self { + fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self { match proxy { RefProxy::Lock(v) => &*v, RefProxy::Ref(v) => v, @@ -697,6 +703,37 @@ where } } +impl<'vm, T: ?Sized + VmType> VmType for &'vm mut T { + type Type = T::Type; + fn make_type(vm: &Thread) -> ArcType { + T::make_type(vm) + } +} + +impl<'vm, 'value, T> Getable<'vm, 'value> for &'value mut T +where + T: vm::Userdata, +{ + type Proxy = scoped::WriteGuard<'value, T>; + + fn to_proxy(_vm: &'vm Thread, value: Variants<'value>) -> Result { + match value.as_ref() { + ValueRef::Userdata(data) => Ok(data + .downcast_ref::>() + .unwrap() + .write()?), + _ => ice!("ValueRef is not an Userdata"), + } + } + fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self { + proxy + } + // Only allow the unsafe version to be used + fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self { + panic!("Mutable references can only be created via proxies") + } +} + impl<'vm, 'value> Getable<'vm, 'value> for &'value str { impl_getable_simple!(); @@ -750,7 +787,17 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for WithVM<'vm, T> where T: Getable<'vm, 'value>, { - impl_getable_simple!(); + type Proxy = T::Proxy; + + fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result { + T::to_proxy(vm, value) + } + fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self { + WithVM { + vm, + value: T::from_proxy(vm, proxy), + } + } fn from_value(vm: &'vm Thread, value: Variants<'value>) -> WithVM<'vm, T> { let t = T::from_value(vm, value); diff --git a/vm/src/api/scoped.rs b/vm/src/api/scoped.rs index 6931ba3042..1f77f56b34 100644 --- a/vm/src/api/scoped.rs +++ b/vm/src/api/scoped.rs @@ -1,4 +1,9 @@ -use std::{fmt, ops::Deref, ptr::NonNull}; +use std::{ + fmt, + marker::PhantomData, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; use std::sync::RwLock; @@ -17,7 +22,7 @@ where T: Userdata, { reference: &'a T, - gluon_reference: Option>, + gluon_reference: Option>, } impl<'a, 'b, T> VmType for &'b mut Ref<'a, T> @@ -35,7 +40,7 @@ where T: VmType + Userdata, { fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> { - Scoped::::new(self.reference).push(context)?; + Scoped::::new(self.reference).push(context)?; let value = context.last().unwrap(); self.gluon_reference = Some(RefGuard { gluon_reference: Opaque::from_value(context.thread().root_value(value)), @@ -56,42 +61,109 @@ where } } -pub struct RefGuard +pub struct RefMut<'a, T> where T: Userdata, { - gluon_reference: OpaqueValue>, + reference: &'a mut T, + gluon_reference: Option>, +} + +impl<'a, 'b, T> VmType for &'b mut RefMut<'a, T> +where + T: Userdata + VmType, +{ + type Type = T::Type; + fn make_type(vm: &Thread) -> ArcType { + T::make_type(vm) + } } -impl Drop for RefGuard +impl<'vm, 'a, 'b, T> Pushable<'vm> for &'b mut RefMut<'a, T> +where + T: VmType + Userdata, +{ + fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> { + Scoped::::new_mut(self.reference).push(context)?; + let value = context.last().unwrap(); + self.gluon_reference = Some(RefGuard { + gluon_reference: Opaque::from_value(context.thread().root_value(value)), + }); + Ok(()) + } +} + +impl<'a, T> RefMut<'a, T> +where + T: Userdata, +{ + pub fn new(reference: &'a mut T) -> Self { + RefMut { + reference, + gluon_reference: None, + } + } +} + +struct RefGuard where T: Userdata, + M: 'static, +{ + gluon_reference: OpaqueValue>, +} + +impl Drop for RefGuard +where + T: Userdata, + M: 'static, { fn drop(&mut self) { Scoped::invalidate(&*self.gluon_reference); } } -pub(crate) struct Scoped { +pub(crate) struct Scoped { ptr: RwLock>>, + _marker: PhantomData, } -unsafe impl Send for Scoped where T: Send + Sync + ?Sized {} -unsafe impl Sync for Scoped where T: Send + Sync + ?Sized {} +unsafe impl Send for Scoped where T: Send + Sync + ?Sized {} +unsafe impl Sync for Scoped where T: Send + Sync + ?Sized {} -impl fmt::Debug for Scoped { +impl fmt::Debug for Scoped { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Scoped") } } -impl Scoped { +impl Scoped { pub fn new(ptr: &T) -> Self { Scoped { ptr: RwLock::new(NonNull::new(ptr as *const T as *mut T)), + _marker: PhantomData, } } +} +impl Scoped { + pub fn new_mut(ptr: &mut T) -> Self { + Scoped { + ptr: RwLock::new(NonNull::new(ptr as *mut T)), + _marker: PhantomData, + } + } + + pub fn write(&self) -> Result> { + let ptr = self.ptr.write().unwrap(); + if let None = *ptr { + return Err("Scoped pointer is invalidated".to_string().into()); + } + Ok(WriteGuard(ptr)) + } +} + +impl Scoped { pub fn read(&self) -> Result> { let ptr = self.ptr.read().unwrap(); if let None = *ptr { @@ -105,14 +177,14 @@ impl Scoped { } } -impl<'vm, T: VmType> VmType for Scoped { +impl<'vm, T: VmType, M> VmType for Scoped { type Type = T::Type; fn make_type(vm: &Thread) -> ArcType { T::make_type(vm) } } -impl Traverseable for Scoped +impl Traverseable for Scoped where T: Traverseable, { @@ -125,7 +197,12 @@ where } } -impl Userdata for Scoped where T: Userdata {} +impl Userdata for Scoped +where + T: Userdata, + M: 'static, +{ +} #[doc(hidden)] pub struct ReadGuard<'a, T>(std::sync::RwLockReadGuard<'a, Option>>); @@ -142,3 +219,30 @@ impl<'a, T> Deref for ReadGuard<'a, T> { } } } + +#[doc(hidden)] +pub struct WriteGuard<'a, T>(std::sync::RwLockWriteGuard<'a, Option>>); + +impl<'a, T> Deref for WriteGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { + match *self.0 { + Some(v) => &*v.as_ptr(), + None => panic!("Scoped pointer is invalidated"), + } + } + } +} + +impl<'a, T> DerefMut for WriteGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + match *self.0 { + Some(v) => &mut *v.as_ptr(), + None => panic!("Scoped pointer is invalidated"), + } + } + } +} diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs index 6aff0c8f54..6acde75122 100644 --- a/vm/src/primitives.rs +++ b/vm/src/primitives.rs @@ -593,11 +593,13 @@ pub enum Component<'a> { Normal(&'a OsStr), } -#[derive(Userdata, Debug)] +#[derive(Userdata, Debug, VmType)] +#[gluon(vm_type = "std.fs.Metadata")] #[gluon(gluon_vm)] pub struct Metadata(fs::Metadata); -#[derive(Userdata, Debug)] +#[derive(Userdata, Debug, VmType)] +#[gluon(vm_type = "std.fs.DirEntry")] #[gluon(gluon_vm)] pub struct DirEntry(fs::DirEntry); diff --git a/vm/src/thread.rs b/vm/src/thread.rs index 4d4f6eca65..c99e50cc78 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -1484,9 +1484,7 @@ impl<'b> OwnedContext<'b> { maybe_context = match state { State::Unknown => return Ok(Async::Ready(Some(context))), - State::Extern(ref ext) if ext.is_locked() => { - return Ok(Async::Ready(Some(context))) - } + State::Extern(ref ext) if ext.is_locked() => return Ok(Async::Ready(Some(context))), State::Extern(mut ext) => { // We are currently in the poll call of this extern function. @@ -1760,7 +1758,7 @@ impl<'b> ExecuteContext<'b> { return Err(Error::Panic( format!("ICE: Stack push out of bounds in {}", function.name), Some(self.stack.stack.stacktrace(0)), - )) + )); } }; self.stack.push(v);