From 719d184ec47669fac00c3a814630df0939ae5080 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 16 May 2020 21:34:22 +0200 Subject: [PATCH 1/4] refactor: Store ExternLoaders in salsa --- src/compiler_pipeline.rs | 61 ++++++------- src/import.rs | 188 ++++++++++++++------------------------- src/lib.rs | 26 +++--- src/query.rs | 70 +++++++++++---- vm/src/compiler.rs | 5 +- vm/src/lib.rs | 11 ++- 6 files changed, 170 insertions(+), 191 deletions(-) diff --git a/src/compiler_pipeline.rs b/src/compiler_pipeline.rs index 5dd5925549..7d88581914 100644 --- a/src/compiler_pipeline.rs +++ b/src/compiler_pipeline.rs @@ -15,12 +15,11 @@ use std::{ #[cfg(feature = "serde")] use either::Either; -use futures::prelude::*; use salsa::ParallelDatabase; use crate::{ base::{ - ast::{self, RootExpr, OwnedExpr, SpannedExpr, Typed}, + ast::{self, OwnedExpr, RootExpr, SpannedExpr, Typed}, error::{Errors, InFile}, fnv::FnvMap, metadata::Metadata, @@ -49,7 +48,7 @@ fn call(v: T, f: impl FnOnce(T) -> U) -> U { } macro_rules! join_result { - ($result: expr, $f: expr, $join: expr $(,)?) => {{ + ($result: expr, |$f_arg: pat| $f_body: expr, $join: expr $(,)?) => {{ let mut first_error = None; let mut x = match $result { Ok(x) => x, @@ -59,8 +58,9 @@ macro_rules! join_result { } Err((None, err)) => return Err((None, err)), }; - let result = call(&mut x, $f) - .await + + let $f_arg = &mut x; + let result = $f_body .map(|_| ()) .map_err(|(value, err)| (value.map(|_| ()), err)); if let Err((value, err)) = result { @@ -159,12 +159,10 @@ impl<'s> MacroExpandable for &'s str { parse_expr(compiler, thread.global_env().type_cache(), file, self) .map_err(|(x, err)| (x, err.into())), |expr| { - async move { - expr.expand_macro(compiler, thread, file, expr_str) - .map_ok(|_| ()) - .map_err(|(opt, err)| (opt.map(|_| ()), err)) - .await - } + expr.expand_macro(compiler, thread, file, expr_str) + .await + .map(|_| ()) + .map_err(|(opt, err)| (opt.map(|_| ()), err)) }, |expr| MacroValue { expr }, ) @@ -290,7 +288,8 @@ where expr: expr.borrow_mut(), } .rename(compiler, thread, file, expr_str) - .map_ok(|_| ()) + .await + .map(|_| ()) .map_err(|(opt, err)| (opt.map(|_| ()), err)) }, |MacroValue { expr }| Renamed { expr }, @@ -999,23 +998,19 @@ where let closure = vm.global_env().new_global_thunk(&vm, module)?; let vm1 = vm.clone(); - vm1.call_thunk_top(&closure) - .map_ok(move |value| ExecuteValue { - id: module_id, - expr, - typ, - value, - metadata, - }) - .map_err(Error::from) - .and_then(move |v| async move { - if run_io { - crate::compiler_pipeline::run_io(vm, v).await - } else { - Ok(v) - } - }) - .await + let value = vm1.call_thunk_top(&closure).await.map_err(Error::from)?; + let v = ExecuteValue { + id: module_id, + expr, + typ, + value, + metadata, + }; + if run_io { + crate::compiler_pipeline::run_io(vm, v).await + } else { + Ok(v) + } } async fn load_script( @@ -1106,7 +1101,8 @@ where let metadata = module.metadata; let closure = vm.global_env().new_global_thunk(&vm, module.module)?; vm.call_thunk_top(&closure) - .map_ok(move |value| ExecuteValue { + .await + .map(move |value| ExecuteValue { id: module_id, expr: (), typ: typ, @@ -1114,7 +1110,6 @@ where value, }) .map_err(Error::from) - .await } async fn load_script( @@ -1213,7 +1208,8 @@ where let vm1 = vm.clone(); vm1.execute_io_top(value.get_variant()) - .map_ok(move |value| { + .await + .map(move |value| { // The type of the new value will be `a` instead of `IO a` let actual = resolve::remove_aliases_cow(&vm.get_env(), &mut NullInterner, &typ); let actual = match **actual { @@ -1229,7 +1225,6 @@ where } }) .map_err(Error::from) - .await } else { Ok(v) } diff --git a/src/import.rs b/src/import.rs index 9452df6e16..515ff1b797 100644 --- a/src/import.rs +++ b/src/import.rs @@ -11,19 +11,20 @@ use std::{ sync::{Arc, Mutex, MutexGuard, RwLock}, }; -use async_trait::async_trait; -use futures::{ - future::{self, BoxFuture}, - prelude::*, - task::SpawnExt, +use { + async_trait::async_trait, + futures::{ + future::{self}, + prelude::*, + task::SpawnExt, + }, + itertools::Itertools, + salsa::{debug::DebugQueryTable, Database}, }; -use itertools::Itertools; use crate::base::{ ast::{self, expr_to_path, Expr, Literal, SpannedExpr}, - filename_to_module, - fnv::FnvMap, - pos, + filename_to_module, pos, symbol::Symbol, types::ArcType, }; @@ -32,14 +33,14 @@ use crate::vm::{ self, gc::Trace, macros::{Error as MacroError, Macro, MacroExpander, MacroFuture}, - thread::{RootedThread, Thread, ThreadInternal}, + thread::{RootedThread, Thread}, vm::VmEnv, ExternLoader, ExternModule, }; use crate::{ - query::{Compilation, CompilerDatabase}, - IoError, ModuleCompiler, + query::{Compilation, CompilationMut, CompilerDatabase}, + IoError, ModuleCompiler, ThreadExt, }; quick_error! { @@ -103,11 +104,6 @@ impl Importer for DefaultImporter { } } -enum UnloadedModule { - Source, - Extern(Vec), -} - pub struct DatabaseSnapshot { snapshot: Option>, } @@ -212,7 +208,13 @@ where vm: &Thread, module_id: &Symbol, ) -> Result, MacroError)> { - Self::load_module(self, compiler, vm, module_id).await + assert!(module_id.is_global()); + let modulename = module_id.name().definition_name(); + + self.importer + .import(compiler, vm, &modulename) + .await + .map_err(|(t, err)| (t, MacroError::new(err))) } fn snapshot(&self, thread: RootedThread) -> DatabaseSnapshot { Self::snapshot(self, thread) @@ -230,18 +232,46 @@ where /// already loaded and then a global access to the loaded module pub struct Import { pub paths: RwLock>, - pub loaders: RwLock>, pub importer: I, compiler: Mutex, } +#[derive(Debug)] +pub struct PtrEq(pub Arc); + +impl std::ops::Deref for PtrEq { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +impl Clone for PtrEq { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Eq for PtrEq {} + +impl PartialEq for PtrEq { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +impl std::hash::Hash for PtrEq { + fn hash(&self, hasher: &mut H) { + (&*self.0 as *const T).hash(hasher) + } +} + impl Import { /// Creates a new import macro pub fn new(importer: I) -> Import { Import { paths: RwLock::new(vec![PathBuf::from(".")]), - loaders: RwLock::default(), compiler: CompilerDatabase::new_base(None).into(), importer: importer, } @@ -256,18 +286,18 @@ impl Import { *self.paths.write().unwrap() = paths; } - pub fn add_loader(&self, module: &str, loader: ExternLoader) { - self.loaders - .write() - .unwrap() - .insert(String::from(module), loader); - } - - pub fn modules(&self) -> Vec> { + pub fn modules(&self, compiler: &mut ModuleCompiler<'_>) -> Vec> { STD_LIBS .iter() .map(|t| Cow::Borrowed(t.0)) - .chain(self.loaders.read().unwrap().keys().cloned().map(Cow::Owned)) + .chain( + compiler + .database + .query(crate::query::ExternLoaderQuery) + .entries::>() + .into_iter() + .map(|entry| Cow::Owned(entry.key)), + ) .collect() } @@ -309,16 +339,6 @@ impl Import { DatabaseFork { fork: Some(fork) } } - fn get_unloaded_module(&self, module: &str) -> UnloadedModule { - { - let loaders = self.loaders.read().unwrap(); - if let Some(loader) = loaders.get(module) { - return UnloadedModule::Extern(loader.dependencies.clone()); - } - } - UnloadedModule::Source - } - pub(crate) fn get_module_source( &self, use_standard_lib: bool, @@ -365,74 +385,6 @@ impl Import { } }) } - - pub async fn load_module( - &self, - compiler: &mut ModuleCompiler<'_>, - vm: &Thread, - module_id: &Symbol, - ) -> Result, MacroError)> - where - I: Importer, - { - assert!(module_id.is_global()); - let modulename = module_id.name().definition_name(); - // Retrieve the source, first looking in the standard library included in the - // binary - let unloaded_module = self.get_unloaded_module(&modulename); - - Ok(match unloaded_module { - UnloadedModule::Extern(dependencies) => { - for dep in dependencies { - let dep_id = Symbol::from(if dep.starts_with('@') { - dep - } else { - format!("@{}", dep) - }); - self.load_module_boxed(compiler, vm, &dep_id).await?; - } - - let ExternModule { - value, - typ, - metadata, - } = (self - .loaders - .write() - .unwrap() - .get_mut(modulename) - .expect("bug: Missing loader but it was already seen in get_unloaded_module") - .load_fn)(vm) - .map_err(|err| (None, MacroError::new(err)))?; - - vm.set_global( - module_id.clone(), - typ.clone(), - metadata.into(), - value.get_value(), - ) - .map_err(|err| (None, MacroError::new(err)))?; - typ - } - UnloadedModule::Source => self - .importer - .import(compiler, vm, &modulename) - .await - .map_err(|(t, err)| (t, MacroError::new(err)))?, - }) - } - - fn load_module_boxed<'a, 'b>( - &'a self, - compiler: &'a mut ModuleCompiler<'b>, - vm: &'a Thread, - module_id: &'a Symbol, - ) -> BoxFuture<'a, Result, MacroError)>> - where - I: Importer, - { - self.load_module(compiler, vm, module_id).boxed() - } } /// Adds an extern module to `thread`, letting it be loaded with `import! name` from gluon code. @@ -479,7 +431,7 @@ impl Import { /// ``` pub fn add_extern_module(thread: &Thread, name: &str, loader: F) where - F: FnMut(&Thread) -> vm::Result + Send + Sync + 'static, + F: Fn(&Thread) -> vm::Result + Send + Sync + 'static, { add_extern_module_( thread, @@ -497,7 +449,7 @@ pub fn add_extern_module_with_deps( loader: F, dependencies: Vec, ) where - F: FnMut(&Thread) -> vm::Result + Send + Sync + 'static, + F: Fn(&Thread) -> vm::Result + Send + Sync + 'static, { add_extern_module_( thread, @@ -510,17 +462,9 @@ pub fn add_extern_module_with_deps( } fn add_extern_module_(thread: &Thread, name: &str, loader: ExternLoader) { - let opt_macro = thread.get_macros().get("import"); - let import = opt_macro - .as_ref() - .and_then(|mac| mac.downcast_ref::()) - .unwrap_or_else(|| { - ice!( - "Can't add an extern module with a import macro. \ - Did you mean to create this `Thread` with `gluon::new_vm`" - ) - }); - import.add_loader(name, loader); + thread + .get_database_mut() + .set_extern_loader(name.into(), PtrEq(Arc::new(loader))); } macro_rules! add_extern_module_if { @@ -659,9 +603,9 @@ where Ok(From::from(move || { async move { db.import(modulename) - .map_err(|err| MacroError::message(err.to_string())) - .map_ok(move |id| pos::spanned(span, Expr::Ident(id))) .await + .map_err(|err| MacroError::message(err.to_string())) + .map(move |id| pos::spanned(span, Expr::Ident(id))) } .boxed() })) diff --git a/src/lib.rs b/src/lib.rs index aa08f0f823..1dc1cad8fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,8 +53,6 @@ pub mod std_lib; pub use crate::vm::thread::{RootedThread, Thread}; -use futures::prelude::*; - use either::Either; use std as real_std; @@ -245,9 +243,9 @@ impl From> for Error { impl Error { pub fn emit_string(&self) -> ::std::io::Result { let mut output = Vec::new(); - self.emit( - &mut ::codespan_reporting::termcolor::NoColor::new(&mut output), - )?; + self.emit(&mut ::codespan_reporting::termcolor::NoColor::new( + &mut output, + ))?; Ok(String::from_utf8(output).unwrap()) } @@ -587,8 +585,8 @@ pub trait ThreadExt: Send + Sync { .. } = db .typechecked_module(module_name, None) - .map_err(|(_, err)| err) - .await?; + .await + .map_err(|(_, err)| err)?; if db.compiler_settings().full_metadata { Ok((expr, typ, metadata)) @@ -700,7 +698,7 @@ pub trait ThreadExt: Send + Sync { let vm = self.thread(); let expected = T::make_type(&vm); - expr_str + let execute_value = expr_str .run_expr( &mut ModuleCompiler::new(&mut vm.get_database()), vm, @@ -708,13 +706,11 @@ pub trait ThreadExt: Send + Sync { expr_str, Some(&expected), ) - .and_then(move |execute_value| async move { - Ok(( - T::from_value(vm, execute_value.value.get_variant()), - execute_value.typ, - )) - }) - .await + .await?; + Ok(( + T::from_value(vm, execute_value.value.get_variant()), + execute_value.typ, + )) } fn format_expr(&self, formatter: &mut Formatter, file: &str, input: &str) -> Result { diff --git a/src/query.rs b/src/query.rs index 605428664c..835b016023 100644 --- a/src/query.rs +++ b/src/query.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, Mutex, MutexGuard}, }; -use {futures::prelude::*, salsa::Database}; +use salsa::Database; use { base::{ @@ -27,10 +27,11 @@ use { macros, thread::{RootedThread, RootedValue, Thread, ThreadInternal}, vm::VmEnv, + ExternLoader, ExternModule, }, }; -use crate::{compiler_pipeline::*, Error, ModuleCompiler, Result, Settings}; +use crate::{compiler_pipeline::*, import::PtrEq, Error, ModuleCompiler, Result, Settings}; pub use {crate::import::DatabaseSnapshot, salsa}; @@ -199,10 +200,7 @@ impl crate::query::CompilationBase for CompilerDatabase { state.add_filemap(&module, &contents[..]); } - fn peek_typechecked_module( - &self, - key: &str, - ) -> Option>>> { + fn peek_typechecked_module(&self, key: &str) -> Option>>> { self.query(TypecheckedModuleQuery) .peek(&(key.into(), None)) .and_then(|r| r.ok()) @@ -303,10 +301,7 @@ pub trait CompilationBase: Send { fn thread(&self) -> &Thread; fn add_module(&mut self, module: String, contents: &str); - fn peek_typechecked_module( - &self, - key: &str, - ) -> Option>>>; + fn peek_typechecked_module(&self, key: &str) -> Option>>>; fn peek_core_expr(&self, key: &str) -> Option>; fn peek_global(&self, key: &str) -> Option; } @@ -316,6 +311,13 @@ pub trait Compilation: CompilationBase { #[salsa::input] fn compiler_settings(&self) -> Settings; + #[salsa::input] + fn extern_loader(&self, module: String) -> PtrEq; + + #[salsa::cycle(recover_cycle)] + #[salsa::dependencies] // FIXME + async fn extern_module(&self, module: String) -> Result>; + #[salsa::dependencies] fn module_text(&self, module: String) -> StdResult>, Error>; @@ -442,8 +444,8 @@ async fn typechecked_module( &text, expected_type.as_ref(), ) - .map_err(|(opt, err)| (opt.map(|value| value.map(Arc::new)), err)) - .await?; + .await + .map_err(|(opt, err)| (opt.map(|value| value.map(Arc::new)), err))?; Ok(value.map(Arc::new)) } @@ -457,8 +459,8 @@ async fn core_expr( let value = db .typechecked_module(module.clone(), expected_type) - .map_err(|(_, err)| err) - .await?; + .await + .map_err(|(_, err)| err)?; let settings = db.compiler_settings(); let env = db.compiler(); @@ -547,10 +549,22 @@ async fn import( } async fn global_inner(db: &mut dyn Compilation, name: String) -> Result { + if db.compiler().query(ExternLoaderQuery).peek(&name).is_some() { + let (id, module) = &*db.extern_module(name).await?; + let mut value = module.value.clone(); + unsafe { value.vm_mut().unroot() }; // FIXME + return Ok(UnrootedGlobal { + id: id.clone(), + typ: module.typ.clone(), + metadata: Arc::new(module.metadata.clone()), + value: UnrootedValue(value), + }); + } + let TypecheckValue { metadata, typ, .. } = db .typechecked_module(name.clone(), None) - .map_err(|(_, err)| err) - .await?; + .await + .map_err(|(_, err)| err)?; let closure = db.compiled_module(name.clone(), None).await?; let module_id = closure.function.name.clone(); @@ -558,15 +572,15 @@ async fn global_inner(db: &mut dyn Compilation, name: String) -> Result Result Result> { + let symbol = Symbol::from(format!("@{}", name)); + let loader = db.extern_loader(name); + + for dep in &loader.dependencies { + db.import(dep.clone()).await?; + } + + let vm = db.thread(); + Ok(PtrEq(Arc::new(( + symbol, // TODO + (loader.load_fn)(vm)?, + )))) +} + async fn global(db: &mut dyn Compilation, name: String) -> Result { db.global_inner(name) .await diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index 9a989157b5..9c2c087850 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -1119,7 +1119,10 @@ impl<'a> Compiler<'a> { let current_line = self.source.line_number_at_byte(body.span().end()); let f = function.end_function(self, current_line); for &(ref var, _) in f.free_vars.iter() { - match self.find(var, function).expect("free_vars: find") { + match self + .find(var, function) + .unwrap_or_else(|| panic!("free_vars: find {}", var)) + { Stack(index) => { debug!("Load stack {}", var); function.emit(Push(index)) diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 98a646d4cd..c093340010 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -214,11 +214,20 @@ impl<'a> fmt::Display for Panic<'a> { } } +impl fmt::Debug for ExternLoader { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ExternLoader") + .field("dependencies", &self.dependencies) + .finish() + } +} + pub struct ExternLoader { - pub load_fn: Box Result + Send + Sync>, + pub load_fn: Box Result + Send + Sync>, pub dependencies: Vec, } +#[derive(Debug)] pub struct ExternModule { pub metadata: Metadata, pub value: RootedValue, From 80ec849f0b07fe91bda4520712c03b6a5a6b038c Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 17 May 2020 14:37:53 +0200 Subject: [PATCH 2/4] Run ignored test --- tests/vm.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/vm.rs b/tests/vm.rs index 18b2dbbab9..e7cff03ba4 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -14,7 +14,7 @@ use gluon::{ vm::{ api::{FunctionRef, Hole, OpaqueValue, ValueRef, IO}, channel::Sender, - thread::{RootedThread, Thread, ThreadInternal}, + thread::{Thread, ThreadInternal}, }, Error, ThreadExt, }; @@ -888,14 +888,11 @@ async fn dont_use_the_implicit_prelude_span_in_the_top_expr() { } #[test] -#[ignore] // FIXME fn deep_clone_partial_application() { use gluon::base::symbol::Symbol; let _ = ::env_logger::try_init(); - let vm = RootedThread::new(); - - assert_eq!(vm.allocated_memory(), 0); + let vm = gluon::VmBuilder::new().build(); let child = vm.new_thread().unwrap(); From ce041744e5dff915961dc18e9311eb210b61f630 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 25 May 2020 20:35:04 +0200 Subject: [PATCH 3/4] refactor: Remove the global table from the vm --- Cargo.lock | 1 + build.rs | 37 +++--- doc/src/lib.rs | 2 +- repl/src/repl.rs | 41 ++++--- src/compiler_pipeline.rs | 66 +++++----- src/import.rs | 24 ++-- src/lib.rs | 12 +- src/query.rs | 259 ++++++++++++++++++++++----------------- tests/vm.rs | 13 +- vm/Cargo.toml | 1 + vm/src/api/ser.rs | 28 +++-- vm/src/compiler.rs | 12 +- vm/src/core/mod.rs | 26 +++- vm/src/primitives.rs | 10 -- vm/src/thread.rs | 62 +++------- vm/src/vm.rs | 74 +---------- 16 files changed, 313 insertions(+), 355 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c691e9800d..86dcd03be9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1417,6 +1417,7 @@ dependencies = [ "serde_state", "slab", "smallvec 0.6.13", + "tokio 0.2.20", "typed-arena 1.7.0", ] diff --git a/build.rs b/build.rs index 736932cd64..d33209cc2f 100644 --- a/build.rs +++ b/build.rs @@ -1,27 +1,24 @@ -extern crate gluon_base; -extern crate itertools; -extern crate walkdir; +use std::{ + env, + fs::File, + io::{Read, Write}, + path::Path, +}; -use std::env; -use std::fs::File; -use std::io::{Read, Write}; -use std::path::Path; - -use itertools::Itertools; - -use walkdir::WalkDir; +use {itertools::Itertools, walkdir::WalkDir}; use gluon_base::filename_to_module; #[cfg(feature = "test")] mod gen_skeptic { - extern crate little_skeptic as skeptic; - extern crate walkdir; + use little_skeptic as skeptic; - use std::env; - use std::fs::{self, File}; - use std::io::prelude::*; - use std::path::{Path, PathBuf}; + use std::{ + env, + fs::{self, File}, + io::Read, + path::{Path, PathBuf}, + }; /// skeptic templates look for `rust` after the opening code fences so writing /// ```f#,rust @@ -64,14 +61,12 @@ return; File::open(file) .and_then(|mut raw_file| raw_file.read_to_end(&mut contents)) .unwrap(); - File::create(&out_file_name) - .and_then(|mut out_file| out_file.write_all(&contents)) - .unwrap(); + fs::write(&out_file_name, contents).unwrap(); out_file_name.to_str().expect("UTF-8 string").into() } pub fn generate() { - let test_locations: Vec<_> = self::walkdir::WalkDir::new("book/src") + let test_locations: Vec<_> = walkdir::WalkDir::new("book/src") .into_iter() .filter_map(|e| { let e = e.unwrap(); diff --git a/doc/src/lib.rs b/doc/src/lib.rs index cb9d40d861..52635ed07e 100644 --- a/doc/src/lib.rs +++ b/doc/src/lib.rs @@ -549,7 +549,7 @@ impl DocCollector<'_> { ); let (expr, typ) = thread.typecheck_str(&name, &content, None)?; - let (meta, _) = metadata(&thread.get_database(), &expr.expr()); + let (meta, _) = metadata(&thread.get_database().as_env(), &expr.expr()); create_dir_all(out_path.join(module_path.parent().unwrap_or(Path::new(""))))?; diff --git a/repl/src/repl.rs b/repl/src/repl.rs index fbec6dabef..0058376f4f 100644 --- a/repl/src/repl.rs +++ b/repl/src/repl.rs @@ -20,13 +20,14 @@ use crate::vm::{ IO, }, internal::ValuePrinter, - thread::{ActiveThread, RootedValue, Thread, ThreadInternal}, + thread::{ActiveThread, RootedValue, Thread}, {self, Error as VMError, Result as VMResult}, }; use gluon::{ compiler_pipeline::{Executable, ExecuteValue}, import::add_extern_module, + query::CompilerDatabase, Error as GluonError, Result as GluonResult, RootedThread, ThreadExt, }; @@ -436,6 +437,9 @@ async fn eval_line_(vm: RootedThread, line: &str) -> gluon::Result<()> { let ExecuteValue { value, typ, .. } = (&mut eval_expr) .run_expr(&mut module_compiler, vm.clone(), "line", line, None) .await?; + + drop(db); + if is_let_binding { let mut expr = eval_expr.expr(); let mut last_bind = None; @@ -448,7 +452,13 @@ async fn eval_line_(vm: RootedThread, line: &str) -> gluon::Result<()> { _ => break, } } - set_globals(&vm, &last_bind.unwrap().name, &typ, &value.as_ref())?; + set_globals( + &vm, + &mut vm.get_database_mut(), + &last_bind.unwrap().name, + &typ, + &value.as_ref(), + )?; } let vm = value.vm(); let env = vm.get_env(); @@ -464,18 +474,19 @@ async fn eval_line_(vm: RootedThread, line: &str) -> gluon::Result<()> { fn set_globals( vm: &Thread, + db: &mut CompilerDatabase, pattern: &SpannedPattern, typ: &ArcType, value: &RootedValue<&Thread>, ) -> GluonResult<()> { match pattern.value { Pattern::Ident(ref id) => { - vm.set_global( - Symbol::from(format!("@{}", id.name.declared_name())), + db.set_global( + id.name.declared_name(), typ.clone(), Default::default(), value.get_value(), - )?; + ); Ok(()) } Pattern::Tuple { ref elems, .. } => { @@ -483,14 +494,14 @@ fn set_globals( .iter() .zip(crate::vm::dynamic::field_iter(&value, typ, vm)); for (elem_pattern, (elem_value, elem_type)) in iter { - set_globals(vm, elem_pattern, &elem_type, &elem_value)?; + set_globals(vm, db, elem_pattern, &elem_type, &elem_value)?; } Ok(()) } Pattern::Record { ref fields, .. } => { let resolved_type = { let mut type_cache = vm.global_env().type_cache(); - let env = vm.get_env(); + let env = db.as_env(); resolve::remove_aliases_cow(&env, &mut type_cache, typ) }; @@ -517,26 +528,26 @@ fn set_globals( .clone(); match pattern_value { Some(ref sub_pattern) => { - set_globals(vm, sub_pattern, &field_type, &field_value)? + set_globals(vm, db, sub_pattern, &field_type, &field_value)? } - None => vm.set_global( - Symbol::from(format!("@{}", name.value.declared_name())), + None => db.set_global( + name.value.declared_name(), field_type.to_owned(), Default::default(), field_value.get_value(), - )?, + ), } } Ok(()) } Pattern::As(ref id, ref pattern) => { - vm.set_global( - Symbol::from(format!("@{}", id.value.declared_name())), + db.set_global( + id.value.declared_name(), typ.clone(), Default::default(), value.get_value(), - )?; - set_globals(vm, pattern, typ, value) + ); + set_globals(vm, db, pattern, typ, value) } Pattern::Constructor(..) | Pattern::Literal(_) | Pattern::Error => { Err(VMError::Message("The repl cannot bind variables from this pattern".into()).into()) diff --git a/src/compiler_pipeline.rs b/src/compiler_pipeline.rs index 7d88581914..8c850051ae 100644 --- a/src/compiler_pipeline.rs +++ b/src/compiler_pipeline.rs @@ -28,14 +28,14 @@ use crate::{ types::{ArcType, NullInterner, Type, TypeCache}, }, check::{metadata, rename}, - query::{Compilation, CompilerDatabase}, + query::{env, Compilation, CompilerDatabase}, vm::{ compiler::CompiledModule, core::{self, interpreter, CoreExpr}, macros::MacroExpander, thread::{RootedThread, RootedValue, Thread, ThreadInternal, VmRoot}, }, - Error, ModuleCompiler, Result, + Error, ModuleCompiler, Result, ThreadExt, }; pub type BoxFuture<'vm, T, E> = @@ -394,8 +394,8 @@ where _file: &str, _expr_str: &str, ) -> SalvageResult> { - let env = &*compiler.database; - let (metadata, metadata_map) = metadata::metadata(env, self.expr.borrow_mut().expr_mut()); + let env = env(compiler.database); + let (metadata, metadata_map) = metadata::metadata(&env, self.expr.borrow_mut().expr_mut()); Ok(WithMetadata { expr: self.expr, metadata, @@ -605,12 +605,12 @@ fn typecheck_expr( metadata_map: &mut FnvMap>, ) -> Result { use crate::check::typecheck::Typecheck; - let env = &*compiler.database; + let env = env(compiler.database); let (arena, expr) = expr.arena_expr(); let mut tc = Typecheck::new( file.into(), &mut compiler.symbols, - &*env, + &env, &thread.global_env().type_cache(), metadata_map, arena.borrow(), @@ -658,7 +658,7 @@ where typ: expr .borrow_mut() .expr() - .try_type_of(&*compiler.database) + .try_type_of(&env(compiler.database)) .unwrap_or_else(|_| thread.global_env().type_cache().error()), expr, metadata_map, @@ -671,8 +671,8 @@ where // Some metadata requires typechecking so recompute it if full metadata is required let (metadata, metadata_map) = if compiler.compiler_settings().full_metadata { - let env = &*compiler.database; - metadata::metadata(&*env, expr.borrow_mut().expr_mut()) + let env = env(compiler.database); + metadata::metadata(&env, expr.borrow_mut().expr_mut()) } else { (metadata, metadata_map) }; @@ -824,22 +824,23 @@ where let core_expr; let mut module = { - let env = &*compiler.database; - - core_expr = core::with_translator(&*env, |translator| { - let expr = translator.translate_expr(self.expr.borrow().expr()); - - debug!("Translation returned: {}", expr); - - if settings.optimize { - core::optimize::optimize(&translator.allocator, env, expr) - } else { - interpreter::Global { - value: core::freeze_expr(&translator.allocator, expr), - info: Default::default(), + core_expr = { + let env = env(compiler.database); + core::with_translator(&env, |translator| { + let expr = translator.translate_expr(self.expr.borrow().expr()); + + debug!("Translation returned: {}", expr); + + if settings.optimize { + core::optimize::optimize(&translator.allocator, &env, expr) + } else { + interpreter::Global { + value: core::freeze_expr(&translator.allocator, expr), + info: Default::default(), + } } - } - }); + }) + }; debug!("Optimization returned: {}", core_expr); @@ -854,8 +855,9 @@ where &mut compiler.symbols, ); + let env = env(compiler.database); let mut compiler = Compiler::new( - &*env, + &env, thread.global_env(), symbols, &source, @@ -1114,7 +1116,7 @@ where async fn load_script( self, - compiler: &mut ModuleCompiler<'_>, + _compiler: &mut ModuleCompiler<'_>, vm: T, name: &str, _expr_str: &str, @@ -1124,9 +1126,7 @@ where T: Send + Sync + VmRoot<'vm>, 'vm: 'async_trait, { - use crate::vm::internal::Global; - use crate::vm::serialization::DeSeed; - use base::symbol::SymbolData; + use crate::vm::{internal::Global, serialization::DeSeed}; let Global { metadata, @@ -1136,12 +1136,8 @@ where } = DeSeed::new(&vm, &mut vm.current_context()) .deserialize(self.0) .map_err(|err| err.to_string())?; - let id = compiler.symbols.symbol(SymbolData { - global: true, - location: None, - name: name, - }); - vm.set_global(id, typ, metadata.clone(), &value)?; + vm.get_database_mut() + .set_global(name, typ, metadata.clone(), &value); info!("Loaded module `{}`", name); Ok(()) } diff --git a/src/import.rs b/src/import.rs index 515ff1b797..cff103a959 100644 --- a/src/import.rs +++ b/src/import.rs @@ -390,12 +390,8 @@ impl Import { /// Adds an extern module to `thread`, letting it be loaded with `import! name` from gluon code. /// /// ``` -/// extern crate gluon; -/// #[macro_use] -/// extern crate gluon_vm; -/// /// use gluon::vm::{self, ExternModule}; -/// use gluon::{Thread, ThreadExt}; +/// use gluon::{primitive, record, Thread, ThreadExt}; /// use gluon::import::add_extern_module; /// /// fn yell(s: &str) -> String { @@ -412,22 +408,18 @@ impl Import { /// ) /// } /// -/// fn main_() -> gluon::Result<()> { -/// let thread = gluon::new_vm(); +/// #[tokio::main] +/// async fn main() -> gluon::Result<()> { +/// let thread = gluon::new_vm_async().await; /// add_extern_module(&thread, "my_module", my_module); /// let script = r#" /// let module = import! "my_module" /// module.yell module.message /// "#; -/// let (result, _) = thread.run_expr::("example", script)?; +/// let (result, _) = thread.run_expr_async::("example", script).await?; /// assert_eq!(result, "HELLO WORLD!"); /// Ok(()) /// } -/// fn main() { -/// if let Err(err) = main_() { -/// panic!("{}", err) -/// } -/// } /// ``` pub fn add_extern_module(thread: &Thread, name: &str, loader: F) where @@ -506,9 +498,9 @@ where id: TypeId, ) -> Option> { if id == TypeId::of::>() { - Some(Box::new( - Box::new(self.snapshot(thread.root_thread())) as Box - )) + Some(Box::new(Box::new(crate::query::snapshot_env( + self.snapshot(thread.root_thread()), + )) as Box)) } else if id == TypeId::of::>() { Some(Box::new( arc_self.clone().downcast_arc::().ok().unwrap() as Arc, diff --git a/src/lib.rs b/src/lib.rs index 1dc1cad8fd..5fed292fd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,10 @@ pub mod lift_io; pub mod query; pub mod std_lib; -pub use crate::vm::thread::{RootedThread, Thread}; +pub use crate::vm::{ + field_decl, primitive, record, record_p, record_type, + thread::{RootedThread, Thread}, +}; use either::Either; @@ -761,9 +764,7 @@ fn skip_implicit_prelude<'a, 'ast>( ) -> &'a SpannedExpr<'ast, Symbol> { loop { match l.value { - ast::Expr::LetBindings(_, ref e) if !span.contains(l.span) => { - l = e; - } + ast::Expr::LetBindings(_, ref e) if !span.contains(l.span) => l = e, _ => break l, } } @@ -845,6 +846,9 @@ let { ? } = import! std.array let { error } = import! std.prim +let __error = error +let __string_eq: String -> String -> Bool = (==) + in () "#; diff --git a/src/query.rs b/src/query.rs index 835b016023..12da3ac281 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,6 +1,7 @@ use std::{ borrow::Cow, collections::hash_map, + ops::DerefMut, result::Result as StdResult, sync::{Arc, Mutex, MutexGuard}, }; @@ -10,7 +11,7 @@ use salsa::Database; use { base::{ ast::{self, OwnedExpr, TypedIdent}, - fnv::FnvMap, + fnv::{FnvMap, FnvSet}, kind::{ArcKind, KindEnv}, metadata::{Metadata, MetadataEnv}, pos::BytePos, @@ -24,6 +25,7 @@ use { core::{self, interpreter, optimize::OptimizeEnv, CoreExpr}, gc::{GcPtr, Trace}, internal::ClosureData, + internal::Value, macros, thread::{RootedThread, RootedValue, Thread, ThreadInternal}, vm::VmEnv, @@ -77,6 +79,7 @@ pub(crate) struct State { pub(crate) code_map: codespan::CodeMap, pub(crate) inline_modules: FnvMap>>, pub(crate) index_map: FnvMap, + extern_globals: FnvSet, } impl State { @@ -284,6 +287,27 @@ impl CompilerDatabase { self.state().add_filemap(file, source) } + pub fn set_global(&mut self, name: &str, typ: ArcType, metadata: Arc, value: &Value) { + let thread = self.thread().root_thread(); + let mut gc = thread.global_env().gc.lock().unwrap(); + let mut cloner = vm::internal::Cloner::new(&thread, &mut gc); + let mut value: RootedValue = + thread.root_value(cloner.deep_clone(&value).unwrap()); + + let id = Symbol::from(format!("@{}", name)); + unsafe { value.vm_mut().unroot() }; + self.state().extern_globals.insert(name.into()); + self.set_extern_global( + name.into(), + UnrootedGlobal { + id, + typ, + metadata, + value: UnrootedValue(value), + }, + ) + } + pub(crate) fn collect_garbage(&mut self) { let strategy = salsa::SweepStrategy::default() .discard_values() @@ -314,10 +338,17 @@ pub trait Compilation: CompilationBase { #[salsa::input] fn extern_loader(&self, module: String) -> PtrEq; + #[doc(hidden)] + #[salsa::input] + fn extern_global(&self, name: String) -> UnrootedGlobal; + #[salsa::cycle(recover_cycle)] #[salsa::dependencies] // FIXME async fn extern_module(&self, module: String) -> Result>; + #[salsa::transparent] + fn get_extern_global(&self, name: &str) -> Option; + #[salsa::dependencies] fn module_text(&self, module: String) -> StdResult>, Error>; @@ -397,6 +428,22 @@ fn recover_cycle( .into()) } +fn get_extern_global( + db: &mut (impl Compilation + salsa::Database), + name: &str, +) -> Option { + if db.compiler().state().extern_globals.contains(name) { + unsafe { + Some(root_global_with( + db.extern_global(name.into()), + db.thread().root_thread(), + )) + } + } else { + None + } +} + fn module_text( db: &mut (impl Compilation + salsa::Database), module: String, @@ -463,14 +510,14 @@ async fn core_expr( .map_err(|(_, err)| err)?; let settings = db.compiler_settings(); - let env = db.compiler(); - Ok(core::with_translator(&*env, |translator| { + let env = env(db.compiler()); + Ok(core::with_translator(&env, |translator| { let expr = translator.translate_expr(value.expr.expr()); debug!("Translation returned: {}", expr); let core_expr = if settings.optimize { - core::optimize::optimize(&translator.allocator, env, expr) + core::optimize::optimize(&translator.allocator, &env, expr) } else { interpreter::Global { value: core::freeze_expr(&translator.allocator, expr), @@ -503,10 +550,11 @@ async fn compiled_module( &mut compiler.symbols, ); - let env = db.compiler(); + let thread = db.thread().root_thread(); + let env = env(db.compiler()); let mut compiler = vm::compiler::Compiler::new( - &*env, - env.thread().global_env(), + &env, + thread.global_env(), symbols, &source, module.clone(), @@ -516,10 +564,9 @@ async fn compiled_module( let mut compiled_module = compiler.compile_expr(core_expr.value.expr())?; let module_id = Symbol::from(format!("@{}", name)); compiled_module.function.id = module_id.clone(); - let closure = env - .thread() + let closure = thread .global_env() - .new_global_thunk(&env.thread(), compiled_module)?; + .new_global_thunk(&thread, compiled_module)?; Ok(closure) } @@ -634,7 +681,23 @@ async fn global(db: &mut dyn Compilation, name: String) -> Result(RefCell); + +pub(crate) fn env(env: &mut CompilerDatabase) -> Env<&'_ mut CompilerDatabase> { + Env(RefCell::new(env)) +} +pub(crate) fn snapshot_env(env: T) -> Env +where + T: DerefMut, +{ + Env(RefCell::new(env)) +} + +impl CompilerEnv for Env +where + T: DerefMut, +{ fn find_var(&self, id: &Symbol) -> Option<(Variable, ArcType)> { if id.is_global() { self.get_global(id.definition_name()) @@ -642,21 +705,31 @@ impl CompilerEnv for CompilerDatabase { } else { let name = id.definition_name(); - let globals = self.thread().global_env().get_globals(); - globals - .globals - .get(name) + self.0 + .borrow_mut() + .get_extern_global(name) .map(|g| (Variable::UpVar(g.id.clone()), g.typ.clone())) } } } -impl KindEnv for CompilerDatabase { +impl KindEnv for Env +where + T: DerefMut, +{ fn find_kind(&self, id: &SymbolRef) -> Option { if id.is_global() { TypeEnv::find_type_info(self, id).map(|t| { - t.kind(&self.thread().global_env().type_cache().kind_cache) - .into_owned() + t.kind( + &self + .0 + .borrow_mut() + .thread() + .global_env() + .type_cache() + .kind_cache, + ) + .into_owned() }) } else { None @@ -664,7 +737,10 @@ impl KindEnv for CompilerDatabase { } } -impl TypeEnv for CompilerDatabase { +impl TypeEnv for Env +where + T: DerefMut, +{ type Type = ArcType; fn find_type(&self, id: &SymbolRef) -> Option { @@ -673,14 +749,17 @@ impl TypeEnv for CompilerDatabase { } else { let name = id.definition_name(); - let globals = self.thread().global_env().get_globals(); - globals.globals.get(name).map(|global| global.typ.clone()) + self.0 + .borrow_mut() + .get_extern_global(name) + .map(|global| global.typ.clone()) } } fn find_type_info(&self, id: &SymbolRef) -> Option> { if id.is_global() { - let globals = self.thread().global_env().get_globals(); + let env = self.0.borrow(); + let globals = env.thread().global_env().get_globals(); globals.type_infos.find_type_info(id) } else { None @@ -688,18 +767,28 @@ impl TypeEnv for CompilerDatabase { } } -impl PrimitiveEnv for CompilerDatabase { +impl PrimitiveEnv for Env +where + T: DerefMut, +{ fn get_bool(&self) -> ArcType { - self.find_type_info("std.types.Bool") + self.0 + .borrow_mut() + .find_type_info("std.types.Bool") .expect("std.types.Bool") .into_type() } } -impl MetadataEnv for CompilerDatabase { +impl MetadataEnv for Env +where + T: DerefMut, +{ fn get_metadata(&self, id: &SymbolRef) -> Option> { if id.is_global() { - self.peek_typechecked_module(id.definition_name()) + self.0 + .borrow_mut() + .peek_typechecked_module(id.definition_name()) .map(|v| v.metadata.clone()) } else { None @@ -707,94 +796,43 @@ impl MetadataEnv for CompilerDatabase { } } -impl OptimizeEnv for CompilerDatabase { +impl OptimizeEnv for Env +where + T: DerefMut, +{ fn find_expr(&self, id: &Symbol) -> Option> { if id.is_global() { - self.peek_core_expr(id.definition_name().into()) + self.0 + .borrow_mut() + .peek_core_expr(id.definition_name().into()) } else { None } } } -unsafe impl Trace for CompilerDatabase { +unsafe impl Trace for Env +where + T: DerefMut, +{ impl_trace! { self, _gc, () } } -impl VmEnv for CompilerDatabase { +impl VmEnv for Env +where + T: DerefMut, +{ fn get_global(&self, name: &str) -> Option>> { let module = Name::new(name.trim_start_matches('@')); - let globals = self.thread().global_env().get_globals(); - globals - .globals - .get(name) - .map(|global| DatabaseGlobal { - id: global.id.clone(), - typ: global.typ.clone(), - metadata: global.metadata.clone(), - value: self.thread().root_value(global.value.get_variants()), - }) - .or_else(|| self.peek_global(module.as_str().into())) - } -} - -impl CompilerEnv for DatabaseSnapshot { - fn find_var(&self, id: &Symbol) -> Option<(Variable, ArcType)> { - CompilerEnv::find_var(&**self, id) - } -} - -impl KindEnv for DatabaseSnapshot { - fn find_kind(&self, id: &SymbolRef) -> Option { - KindEnv::find_kind(&**self, id) - } -} - -impl TypeEnv for DatabaseSnapshot { - type Type = ArcType; - - fn find_type(&self, id: &SymbolRef) -> Option { - TypeEnv::find_type(&**self, id) - } - - fn find_type_info(&self, id: &SymbolRef) -> Option> { - TypeEnv::find_type_info(&**self, id) - } -} - -impl PrimitiveEnv for DatabaseSnapshot { - fn get_bool(&self) -> ArcType { - (&**self).get_bool() - } -} - -impl MetadataEnv for DatabaseSnapshot { - fn get_metadata(&self, id: &SymbolRef) -> Option> { - MetadataEnv::get_metadata(&**self, id) - } -} - -impl OptimizeEnv for DatabaseSnapshot { - fn find_expr(&self, id: &Symbol) -> Option> { - (&**self).find_expr(id) - } -} - -unsafe impl Trace for DatabaseSnapshot { - fn trace(&self, gc: &mut vm::gc::Gc) { - (**self).trace(gc) - } -} - -impl VmEnv for DatabaseSnapshot { - fn get_global(&self, name: &str) -> Option>> { - VmEnv::get_global(&**self, name) + let mut env = self.0.borrow_mut(); + env.get_extern_global(name) + .or_else(|| env.peek_global(module.as_str().into())) } } impl CompilerDatabase { - pub fn find_type_info(&self, name: &str) -> Result> { + pub fn find_type_info(&mut self, name: &str) -> Result> { let name = Name::new(name); let (_, typ) = self.get_binding(name.module().as_str())?; let maybe_type_info = { @@ -808,7 +846,10 @@ impl CompilerDatabase { .ok_or_else(move || vm::Error::UndefinedField(typ, name.name().as_str().into()).into()) } - fn get_scoped_global<'s, 'n>(&'s self, name: &'n str) -> Option<(&'n Name, DatabaseGlobal)> { + fn get_scoped_global<'s, 'n>( + &'s mut self, + name: &'n str, + ) -> Option<(&'n Name, DatabaseGlobal)> { let mut module = Name::new(name.trim_start_matches('@')); // Try to find a global by successively reducing the module path // Input: "x.y.z.w" @@ -816,20 +857,12 @@ impl CompilerDatabase { // Test: "x.y" // Test: "x" // Test: -> Error - let globals = self.thread().global_env().get_globals(); let global = loop { if module.as_str() == "" { return None; } - if let Some(g) = globals - .globals - .get(name) - .map(|global| DatabaseGlobal { - id: global.id.clone(), - typ: global.typ.clone(), - metadata: global.metadata.clone(), - value: self.thread().root_value(global.value.get_variants()), - }) + if let Some(g) = self + .get_extern_global(name) .or_else(|| self.peek_global(module.as_str().into())) { break g; @@ -841,7 +874,7 @@ impl CompilerDatabase { Some((remaining_fields, global)) } - pub fn get_binding(&self, name: &str) -> Result<(RootedValue, ArcType)> { + pub fn get_binding(&mut self, name: &str) -> Result<(RootedValue, ArcType)> { use crate::base::resolve; let (remaining_fields, global) = self @@ -869,7 +902,7 @@ impl CompilerDatabase { )) .into()); } - typ = resolve::remove_aliases(self, &mut NullInterner, typ); + typ = resolve::remove_aliases(&env(self), &mut NullInterner, typ); let next_type = { typ.row_iter() .enumerate() @@ -889,12 +922,12 @@ impl CompilerDatabase { Ok((self.thread().root_value(value), typ)) } - pub fn get_metadata(&self, name_str: &str) -> Result> { + pub fn get_metadata(&mut self, name_str: &str) -> Result> { self.get_metadata_(name_str) .ok_or_else(|| vm::Error::MetadataDoesNotExist(name_str.into()).into()) } - fn get_metadata_(&self, name_str: &str) -> Option> { + fn get_metadata_(&mut self, name_str: &str) -> Option> { let (remaining, global) = self.get_scoped_global(name_str)?; let mut metadata = &global.metadata; @@ -903,4 +936,8 @@ impl CompilerDatabase { } Some(metadata.clone()) } + + pub fn as_env(&mut self) -> Env<&mut Self> { + env(self) + } } diff --git a/tests/vm.rs b/tests/vm.rs index e7cff03ba4..3cd6033826 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -14,7 +14,7 @@ use gluon::{ vm::{ api::{FunctionRef, Hole, OpaqueValue, ValueRef, IO}, channel::Sender, - thread::{Thread, ThreadInternal}, + thread::Thread, }, Error, ThreadExt, }; @@ -889,8 +889,6 @@ async fn dont_use_the_implicit_prelude_span_in_the_top_expr() { #[test] fn deep_clone_partial_application() { - use gluon::base::symbol::Symbol; - let _ = ::env_logger::try_init(); let vm = gluon::VmBuilder::new().build(); @@ -911,13 +909,12 @@ fn deep_clone_partial_application() { let global_memory_without_closures = vm.global_env().gc.lock().unwrap().allocated_memory(); let memory_for_closures = child.allocated_memory(); - vm.set_global( - Symbol::from("@test"), + vm.get_database_mut().set_global( + "test", Type::hole(), Default::default(), - result.unwrap().0.get_value(), - ) - .unwrap(); + &result.unwrap().0.into_inner(), + ); let global_memory_with_closures = vm.global_env().gc.lock().unwrap().allocated_memory(); diff --git a/vm/Cargo.toml b/vm/Cargo.toml index e1834074cd..4e3e100502 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -66,6 +66,7 @@ gluon = { path = "..", version = ">=0.9" } lalrpop-util = "0.19" regex = "1" serde_json = "1.0.0" +tokio = { version = "0.2", features = ["macros"] } gluon_parser = { path = "../parser", version = "0.14.1" } # GLUON diff --git a/vm/src/api/ser.rs b/vm/src/api/ser.rs index baedb94ac0..fab045624d 100644 --- a/vm/src/api/ser.rs +++ b/vm/src/api/ser.rs @@ -24,18 +24,18 @@ use crate::{ ## Struct ``` -#[macro_use] -extern crate serde_derive; +use serde::Serialize; -extern crate gluon; -#[macro_use] -extern crate gluon_vm; +use gluon_vm::{field_decl, record_type}; -use gluon::{Thread, ThreadExt, new_vm}; -use gluon::base::types::ArcType; -use gluon::vm::api::{FunctionRef, VmType}; -use gluon::vm::api::ser::Ser; -# fn main() { +use gluon::{ + Thread, ThreadExt, new_vm_async, + base::types::ArcType, + vm::api::{ser::Ser, FunctionRef, VmType}, +}; + +# #[tokio::main] +# async fn main() { #[derive(Serialize)] struct Vec2 { @@ -60,18 +60,20 @@ impl VmType for Vec2 { # ::std::env::set_var("GLUON_PATH", ".."); # } -let thread = new_vm(); +let thread = new_vm_async().await; let (mut f, _): (FunctionRef) -> i32>, _) = thread - .run_expr("", r#"let f v: _ -> Int = v.x + v.y in f"#) + .run_expr_async("", r#"let f v: _ -> Int = v.x + v.y in f"#) + .await .unwrap_or_else(|err| panic!("{}", err)); let vec = Vec2 { x: 3, y: 10 }; -let result = f.call(Ser(vec)).unwrap_or_else(|err| panic!("{}", err)); +let result = f.call_async(Ser(vec)).await.unwrap_or_else(|err| panic!("{}", err)); assert_eq!(result, 13); # } + ``` ## Enum diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index 9c2c087850..010fdb8056 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -7,7 +7,7 @@ use crate::base::{ resolve, scoped_map::ScopedMap, source::Source, - symbol::{Symbol, SymbolModule, SymbolRef}, + symbol::{Symbol, SymbolData, SymbolModule, SymbolRef}, types::{Alias, ArcType, BuiltinType, NullInterner, Type, TypeEnv, TypeExt}, }; @@ -848,7 +848,15 @@ impl<'a> Compiler<'a> { function.emit(FloatEQ); } Literal::String(ref s) => { - self.load_identifier(&Symbol::from("@string_eq"), function)?; + let prim_symbol = self.symbols.symbol(SymbolData { + global: true, + name: "std.prim", + location: None, + }); + self.load_identifier(&prim_symbol, function)?; + let prim_type = self.globals.find_type(&prim_symbol).unwrap(); + let string_eq_symbol = self.symbols.simple_symbol("string_eq"); + function.emit_field(self, &prim_type, &string_eq_symbol)?; let lhs_i = function.stack_size() - 2; function.emit(Push(lhs_i)); function.emit_string(self.intern(&s)?); diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index 3481917cd4..d4b7ef137c 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -746,6 +746,7 @@ pub struct Translator<'a, 'e> { env: &'e dyn PrimitiveEnv, dummy_symbol: TypedIdent, error_symbol: TypedIdent, + std_prim_symbol: Symbol, dummy_record_symbol: TypedIdent, } @@ -760,8 +761,9 @@ impl<'a, 'e> Translator<'a, 'e> { name: Symbol::from(""), typ: hole.clone(), }, + std_prim_symbol: Symbol::from("@std.prim"), error_symbol: TypedIdent { - name: Symbol::from("@error"), + name: Symbol::from("error"), typ: hole.clone(), }, dummy_record_symbol: TypedIdent { @@ -1397,11 +1399,29 @@ impl<'a, 'e> Translator<'a, 'e> { fn error_expr(&'a self, msg: &str) -> Expr<'a> { let arena = &self.allocator.arena; - let error = arena.alloc(Expr::Ident(self.error_symbol.clone(), Span::default())); + let std_prim_type = self + .env + .find_type(&self.std_prim_symbol) + .unwrap_or_else(|| self.error_symbol.typ.clone()); + let std_prim = arena.alloc(Expr::Ident( + TypedIdent { + name: self.std_prim_symbol.clone(), + typ: std_prim_type.clone(), + }, + Span::default(), + )); let args = arena.alloc_fixed( Some(Expr::Const(Literal::String(msg.into()), Span::default())).into_iter(), ); - Expr::Call(error, args) + Expr::Call( + arena.alloc(self.project_expr( + Span::default(), + std_prim, + &self.error_symbol.name, + &self.error_symbol.typ, + )), + args, + ) } } diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs index ee46fb8bb1..a8f76dc82c 100644 --- a/vm/src/primitives.rs +++ b/vm/src/primitives.rs @@ -793,16 +793,6 @@ pub fn load_string_buf(vm: &Thread) -> Result { #[allow(non_camel_case_types, deprecated)] pub fn load<'vm>(vm: &'vm Thread) -> Result { - vm.define_global( - "error", - primitive:: Pushed>("@error", std::prim::error), - )?; - - vm.define_global( - "string_eq", - primitive!(2, "@string_eq", ::eq), - )?; - ExternModule::new( vm, record! { diff --git a/vm/src/thread.rs b/vm/src/thread.rs index 4624002229..806476d8f3 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -28,7 +28,6 @@ use futures::{ use async_trait::async_trait; use crate::base::{ - metadata::Metadata, pos::Line, symbol::Symbol, types::{self, Alias, ArcType}, @@ -323,6 +322,16 @@ where .unwrap_or_else(|| ice!("Rooted value has already been dropped")); rooted_values.swap_remove(i); } + + pub fn into_owned(self) -> RootedValue { + let value = RootedValue { + vm: self.vm.root_thread(), + rooted: self.rooted, + value: unsafe { self.value.clone_unrooted() }, + }; + mem::forget(self); + value + } } impl<'vm> RootedValue<&'vm Thread> { @@ -794,22 +803,6 @@ impl Thread { } } - pub(crate) fn define_global<'vm, T>(&'vm self, name: &str, value: T) -> Result<()> - where - T: Pushable<'vm> + VmType, - { - // Value gets rooted by set_global - unsafe { - let value = value.marshal_unrooted(self)?; - self.set_global( - Symbol::from(format!("@{}", name)), - T::make_forall_type(self), - Default::default(), - &value, - ) - } - } - pub fn spawner(&self) -> Option<&(dyn futures::task::Spawn + Send + Sync)> { self.global_env().spawner() } @@ -822,21 +815,23 @@ impl Thread { /// to an `add` function in rust /// /// ```rust - /// # use gluon::{new_vm, Thread, ThreadExt}; + /// # use gluon::{new_vm_async, Thread, ThreadExt}; /// # use gluon::vm::api::{FunctionRef, Hole, OpaqueValue}; - /// # fn main() { + /// # #[tokio::main] + /// # async fn main() { /// /// # if ::std::env::var("GLUON_PATH").is_err() { /// # ::std::env::set_var("GLUON_PATH", ".."); /// # } /// - /// let vm = new_vm(); + /// let vm = new_vm_async().await; /// - /// vm.run_expr::>("example", r#" import! std.int "#) + /// vm.run_expr_async::>("example", r#" import! std.int "#) + /// .await /// .unwrap_or_else(|err| panic!("{}", err)); /// let mut add: FunctionRef i32> = /// vm.get_global("std.int.num.(+)").unwrap(); - /// let result = add.call(1, 2); + /// let result = add.call_async(1, 2).await; /// assert_eq!(result, Ok(3)); /// # } /// ``` @@ -1131,15 +1126,6 @@ where fn resume(&self, cx: &mut task::Context<'_>) -> Poll>; - fn set_global( - &self, - name: Symbol, - typ: ArcType, - metadata: Arc, - value: &Value, - ) -> Result<()>; - - /// `owner` is theread that owns `value` which is not necessarily the same as `self` fn deep_clone_value(&self, owner: &Thread, value: &Value) -> Result>; fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool; @@ -1272,20 +1258,6 @@ impl ThreadInternal for Thread { Ok(context).into() } - fn set_global( - &self, - name: Symbol, - typ: ArcType, - metadata: Arc, - value: &Value, - ) -> Result<()> { - let mut gc = self.global_env().gc.lock().unwrap(); - let mut cloner = crate::value::Cloner::new(self, &mut gc); - let value = cloner.deep_clone(&value)?; - self.global_env() - .set_global(name, typ, metadata, value.get_value()) - } - fn deep_clone_value(&self, owner: &Thread, value: &Value) -> Result> { let mut context = self.owned_context(); let full_clone = !self.can_share_values_with(&mut context.gc, owner); diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 355fb93298..85209053f4 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -26,13 +26,13 @@ use crate::{ api::{OpaqueValue, ValueRef, IO}, compiler::{CompiledFunction, CompiledModule, CompilerEnv, Variable}, core::{interpreter, optimize::OptimizeEnv, CoreExpr}, - gc::{CloneUnrooted, Gc, GcPtr, GcRef, Generation, Move, Trace}, + gc::{Gc, GcPtr, GcRef, Generation, Move, Trace}, interner::{InternedStr, Interner}, lazy::Lazy, macros::MacroEnv, thread::ThreadInternal, types::*, - value::{BytecodeFunction, ClosureData, ClosureDataDef, Value}, + value::{BytecodeFunction, ClosureData, ClosureDataDef}, Error, Result, Variants, }; @@ -249,10 +249,6 @@ pub struct GlobalVmState { unsafe impl Trace for GlobalVmState { unsafe fn root(&mut self) { - for g in self.env.get_mut().globals.values_mut() { - g.root(); - } - self.macros.root(); // Also need to check the interned string table @@ -260,10 +256,6 @@ unsafe impl Trace for GlobalVmState { self.generation_0_threads.get_mut().unwrap().root(); } unsafe fn unroot(&mut self) { - for g in self.env.get_mut().globals.values_mut() { - g.unroot(); - } - self.macros.unroot(); // Also need to check the interned string table @@ -272,10 +264,6 @@ unsafe impl Trace for GlobalVmState { } fn trace(&self, gc: &mut Gc) { - for g in self.env.read().globals.values() { - g.trace(gc); - } - self.macros.trace(gc); // Also need to check the interned string table @@ -300,12 +288,6 @@ unsafe impl Trace for GlobalVmState { pub struct Globals { #[cfg_attr(feature = "serde_derive", serde(state))] pub type_infos: TypeInfos, - #[cfg_attr(feature = "serde_derive", serde(state))] - pub globals: FnvMap>, -} - -unsafe impl Trace for Globals { - impl_trace_fields! { self, gc; globals } } pub trait VmEnv: @@ -322,7 +304,7 @@ pub struct VmEnvInstance<'a> { } unsafe impl Trace for VmEnvInstance<'_> { - impl_trace_fields! { self, gc; vm_envs, globals } + impl_trace_fields! { self, gc; vm_envs } } impl<'a> OptimizeEnv for VmEnvInstance<'a> { @@ -337,12 +319,6 @@ impl<'a> CompilerEnv for VmEnvInstance<'a> { .iter() .filter_map(|env| env.find_var(id)) .next() - .or_else(|| { - self.globals - .globals - .get(id.definition_name()) - .map(|g| (Variable::UpVar(g.id.clone()), g.typ.clone())) - }) .or_else(|| self.globals.type_infos.find_var(id)) } } @@ -365,12 +341,6 @@ impl<'a> TypeEnv for VmEnvInstance<'a> { .iter() .filter_map(|env| env.find_type(id)) .next() - .or_else(|| { - self.globals - .globals - .get(id.definition_name()) - .map(|g| g.typ.clone()) - }) .or_else(|| { self.globals .type_infos @@ -643,44 +613,6 @@ impl GlobalVmState { self.typeids.read().unwrap().get(&id).cloned() } - /// Checks if a global exists called `name` - pub fn global_exists(&self, name: &str) -> bool { - self.env.read_recursive().globals.get(name).is_some() - } - - pub(crate) fn set_global( - &self, - id: Symbol, - typ: ArcType, - metadata: Arc, - value: &Value, - ) -> Result<()> { - assert!(value.generation().is_root()); - assert!(id.is_global(), "Symbol is not global"); - let mut env = self.env.write(); - let globals = &mut env.globals; - let global = Global { - id: id.clone(), - typ, - metadata, - // SAFETY The global table are scanned - value: unsafe { value.clone_unrooted() }, - }; - globals.insert(StdString::from(id.definition_name()), global); - Ok(()) - } - - // Currently necessary for the language server - #[doc(hidden)] - pub fn set_dummy_global(&self, id: &str, typ: ArcType, metadata: Metadata) -> Result<()> { - self.set_global( - Symbol::from(format!("@{}", id)), - typ, - Arc::new(metadata), - &Value::int(0), - ) - } - pub fn get_generic(&self, name: &str) -> ArcType { let mut generics = self.generics.write().unwrap(); if let Some(g) = generics.get(name) { From c59e8f2ae295fd38f6e6ce941de520384262c0f7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 6 Jun 2020 10:40:02 +0200 Subject: [PATCH 4/4] Fix appveyor build --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5fed292fd6..cd15ea90d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -652,6 +652,8 @@ pub trait ThreadExt: Send + Sync { /// ``` /// # use gluon::{new_vm, ThreadExt}; /// # fn main() { + /// # // Workaround stack overflow on appveyor + /// # std::thread::Builder::new().stack_size(2_000_000).spawn(move || { /// let vm = new_vm(); /// let (result, _) = vm /// .run_expr::( @@ -660,6 +662,7 @@ pub trait ThreadExt: Send + Sync { /// ) /// .unwrap(); /// assert_eq!(result, "Hello world"); + /// }).unwrap().join().unwrap() /// # } /// ``` ///