diff --git a/nac3artiq/src/lib.rs b/nac3artiq/src/lib.rs index 205ee5c5..14c4b028 100644 --- a/nac3artiq/src/lib.rs +++ b/nac3artiq/src/lib.rs @@ -60,13 +60,13 @@ use nac3core::{ use nac3ld::Linker; -use tempfile::{self, TempDir}; - use crate::codegen::{attributes_writeback, gen_core_log, gen_rtio_log}; use crate::{ codegen::{rpc_codegen_callback, ArtiqCodeGenerator}, symbol_resolver::{DeferredEvaluationStore, InnerResolver, PythonHelper, Resolver}, }; +use nac3core::toplevel::composer::{BuiltinFuncCreator, BuiltinFuncSpec}; +use tempfile::{self, TempDir}; mod codegen; mod symbol_resolver; @@ -127,7 +127,7 @@ struct Nac3 { isa: Isa, time_fns: &'static (dyn TimeFns + Sync), primitive: PrimitiveStore, - builtins: Vec<(StrRef, FunSignature, Arc)>, + builtins: Vec, pyid_to_def: Arc>>, primitive_ids: PrimitivePythonId, working_directory: TempDir, @@ -301,6 +301,64 @@ impl Nac3 { None } + /// Returns a [`Vec`] of builtins that needs to be initialized during method compilation time. + fn get_lateinit_builtins() -> Vec> { + vec![ + Box::new(|primitives, unifier| { + let arg_ty = unifier.get_fresh_var(Some("T".into()), None); + + ( + "core_log".into(), + FunSignature { + args: vec![FuncArg { + name: "arg".into(), + ty: arg_ty.ty, + default_value: None, + is_vararg: false, + }], + ret: primitives.none, + vars: into_var_map([arg_ty]), + }, + Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| { + gen_core_log(ctx, &obj, fun, &args, generator)?; + + Ok(None) + }))), + ) + }), + Box::new(|primitives, unifier| { + let arg_ty = unifier.get_fresh_var(Some("T".into()), None); + + ( + "rtio_log".into(), + FunSignature { + args: vec![ + FuncArg { + name: "channel".into(), + ty: primitives.str, + default_value: None, + is_vararg: false, + }, + FuncArg { + name: "arg".into(), + ty: arg_ty.ty, + default_value: None, + is_vararg: false, + }, + ], + ret: primitives.none, + vars: into_var_map([arg_ty]), + }, + Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| { + gen_rtio_log(ctx, &obj, fun, &args, generator)?; + + Ok(None) + }))), + ) + }), + ] + } + fn compile_method( &self, obj: &PyAny, @@ -313,6 +371,7 @@ impl Nac3 { let size_t = self.isa.get_size_type(); let (mut composer, mut builtins_def, mut builtins_ty) = TopLevelComposer::new( self.builtins.clone(), + Self::get_lateinit_builtins(), ComposerConfig { kernel_ann: Some("Kernel"), kernel_invariant_ann: "KernelInvariant" }, size_t, ); @@ -853,7 +912,7 @@ impl Nac3 { Isa::RiscV32IMA => &timeline::NOW_PINNING_TIME_FNS, Isa::CortexA9 | Isa::Host => &timeline::EXTERN_TIME_FNS, }; - let (primitive, mut unifier) = TopLevelComposer::make_primitives(isa.get_size_type()); + let (primitive, _) = TopLevelComposer::make_primitives(isa.get_size_type()); let builtins = vec![ ( "now_mu".into(), @@ -902,58 +961,6 @@ impl Nac3 { Ok(None) }))), ), - { - let arg_ty = unifier.get_fresh_var(Some("T".into()), None); - - ( - "core_log".into(), - FunSignature { - args: vec![FuncArg { - name: "arg".into(), - ty: arg_ty.ty, - default_value: None, - is_vararg: false, - }], - ret: primitive.none, - vars: into_var_map([arg_ty]), - }, - Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| { - gen_core_log(ctx, &obj, fun, &args, generator)?; - - Ok(None) - }))), - ) - }, - { - let arg_ty = unifier.get_fresh_var(Some("T".into()), None); - - ( - "rtio_log".into(), - FunSignature { - args: vec![ - FuncArg { - name: "channel".into(), - ty: primitive.str, - default_value: None, - is_vararg: false, - }, - FuncArg { - name: "arg".into(), - ty: arg_ty.ty, - default_value: None, - is_vararg: false, - }, - ], - ret: primitive.none, - vars: into_var_map([arg_ty]), - }, - Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| { - gen_rtio_log(ctx, &obj, fun, &args, generator)?; - - Ok(None) - }))), - ) - }, ]; let builtins_mod = PyModule::import(py, "builtins").unwrap(); diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index 8d128ae3..9ed495e0 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -94,7 +94,7 @@ fn test_primitives() { "}; let statements = parse_program(source, FileName::default()).unwrap(); - let composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 32).0; + let composer = TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 32).0; let mut unifier = composer.unifier.clone(); let primitives = composer.primitives_ty; let top_level = Arc::new(composer.make_top_level_context()); @@ -258,7 +258,7 @@ fn test_simple_call() { "}; let statements_2 = parse_program(source_2, FileName::default()).unwrap(); - let composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 32).0; + let composer = TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 32).0; let mut unifier = composer.unifier.clone(); let primitives = composer.primitives_ty; let top_level = Arc::new(composer.make_top_level_context()); diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 547c7e27..2f0f7e87 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -44,12 +44,27 @@ pub struct TopLevelComposer { pub size_t: u32, } +/// The specification for a builtin function, consisting of the function name, the function +/// signature, and a [code generation callback][`GenCall`]. +pub type BuiltinFuncSpec = (StrRef, FunSignature, Arc); + +/// A function that creates a [`BuiltinFuncSpec`] using the provided [`PrimitiveStore`] and +/// [`Unifier`]. +pub type BuiltinFuncCreator = dyn Fn(&PrimitiveStore, &mut Unifier) -> BuiltinFuncSpec; + impl TopLevelComposer { /// return a composer and things to make a "primitive" symbol resolver, so that the symbol - /// resolver can later figure out primitive type definitions when passed a primitive type name + /// resolver can later figure out primitive tye definitions when passed a primitive type name + /// + /// `lateinit_builtins` are specifically for the ARTIQ module. Since the [`Unifier`] instance + /// used to create builtin functions do not persist until method compilation, any types + /// created (e.g. [`TypeEnum::TVar`]) also do not persist. Those functions should be instead put + /// in `lateinit_builtins`, where they will be instantiated with the [`Unifier`] instance used + /// for method compilation. #[must_use] pub fn new( - builtins: Vec<(StrRef, FunSignature, Arc)>, + builtins: Vec, + lateinit_builtins: Vec>, core_config: ComposerConfig, size_t: u32, ) -> (Self, HashMap, HashMap) { @@ -119,7 +134,13 @@ impl TopLevelComposer { } } - for (name, sig, codegen_callback) in builtins { + // Materialize lateinit_builtins, now that the unifier is ready + let lateinit_builtins = lateinit_builtins + .into_iter() + .map(|builtin| builtin(&primitives_ty, &mut unifier)) + .collect_vec(); + + for (name, sig, codegen_callback) in builtins.into_iter().chain(lateinit_builtins) { let fun_sig = unifier.add_ty(TypeEnum::TFunc(sig)); builtin_ty.insert(name, fun_sig); builtin_id.insert(name, DefinitionId(definition_ast_list.len())); diff --git a/nac3core/src/toplevel/test.rs b/nac3core/src/toplevel/test.rs index 7678452d..522cb23d 100644 --- a/nac3core/src/toplevel/test.rs +++ b/nac3core/src/toplevel/test.rs @@ -117,7 +117,8 @@ impl SymbolResolver for Resolver { "register" )] fn test_simple_register(source: Vec<&str>) { - let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0; + let mut composer = + TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; for s in source { let ast = parse_program(s, FileName::default()).unwrap(); @@ -137,7 +138,8 @@ fn test_simple_register(source: Vec<&str>) { "register" )] fn test_simple_register_without_constructor(source: &str) { - let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0; + let mut composer = + TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; let ast = parse_program(source, FileName::default()).unwrap(); let ast = ast[0].clone(); composer.register_top_level(ast, None, "", true).unwrap(); @@ -171,7 +173,8 @@ fn test_simple_register_without_constructor(source: &str) { "function compose" )] fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) { - let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0; + let mut composer = + TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; let internal_resolver = Arc::new(ResolverInternal { id_to_def: Mutex::default(), @@ -519,7 +522,8 @@ fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) { )] fn test_analyze(source: &[&str], res: &[&str]) { let print = false; - let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0; + let mut composer = + TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; let internal_resolver = make_internal_resolver_with_tvar( vec![ @@ -696,7 +700,8 @@ fn test_analyze(source: &[&str], res: &[&str]) { )] fn test_inference(source: Vec<&str>, res: &[&str]) { let print = true; - let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0; + let mut composer = + TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0; let internal_resolver = make_internal_resolver_with_tvar( vec![ diff --git a/nac3standalone/src/main.rs b/nac3standalone/src/main.rs index 228b75c4..cc4811c1 100644 --- a/nac3standalone/src/main.rs +++ b/nac3standalone/src/main.rs @@ -301,7 +301,7 @@ fn main() { let primitive: PrimitiveStore = TopLevelComposer::make_primitives(size_t).0; let (mut composer, builtins_def, builtins_ty) = - TopLevelComposer::new(vec![], ComposerConfig::default(), size_t); + TopLevelComposer::new(vec![], vec![], ComposerConfig::default(), size_t); let internal_resolver: Arc = ResolverInternal { id_to_type: builtins_ty.into(),