1#![doc(
2 html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
3 html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
4)]
5#[macro_use]
6extern crate netidx_core;
7#[macro_use]
8extern crate combine;
9#[macro_use]
10extern crate serde_derive;
11
12pub mod env;
13pub mod expr;
14pub mod node;
15pub mod typ;
16
17use crate::{
18 env::Env,
19 expr::{ExprId, ModPath},
20 node::lambda::LambdaDef,
21 typ::{FnType, Type},
22};
23use ahash::{AHashMap, AHashSet};
24use anyhow::{bail, Result};
25use arcstr::ArcStr;
26use enumflags2::{bitflags, BitFlags};
27use expr::Expr;
28use futures::channel::mpsc;
29use log::info;
30use netidx::{
31 path::Path,
32 publisher::{Id, Val, WriteRequest},
33 subscriber::{self, Dval, SubId, UpdatesFlags, Value},
34};
35use netidx_protocols::rpc::server::{ArgSpec, RpcCall};
36use netidx_value::{abstract_type::AbstractWrapper, Abstract};
37use node::compiler;
38use nohash::{IntMap, IntSet};
39use parking_lot::{Mutex, RwLock};
40use poolshark::{
41 global::{GPooled, Pool},
42 local::LPooled,
43};
44use std::{
45 any::{Any, TypeId},
46 cell::Cell,
47 collections::hash_map::{self, Entry},
48 fmt::Debug,
49 mem,
50 sync::{
51 self,
52 atomic::{AtomicBool, Ordering},
53 LazyLock,
54 },
55 time::Duration,
56};
57use tokio::{task, time::Instant};
58use triomphe::Arc;
59use uuid::Uuid;
60
61#[derive(Debug, Clone, Copy)]
62#[bitflags]
63#[repr(u64)]
64pub enum CFlag {
65 WarnUnhandled,
66 WarnUnused,
67 WarningsAreErrors,
68}
69
70#[allow(dead_code)]
71static TRACE: AtomicBool = AtomicBool::new(false);
72
73#[allow(dead_code)]
74pub fn set_trace(b: bool) {
75 TRACE.store(b, Ordering::Relaxed)
76}
77
78#[allow(dead_code)]
79pub fn with_trace<F: FnOnce() -> Result<R>, R>(
80 enable: bool,
81 spec: &Expr,
82 f: F,
83) -> Result<R> {
84 let prev = trace();
85 set_trace(enable);
86 if !prev && enable {
87 eprintln!("trace enabled at {}, spec: {}", spec.pos, spec);
88 } else if prev && !enable {
89 eprintln!("trace disabled at {}, spec: {}", spec.pos, spec);
90 }
91 let r = match f() {
92 Err(e) => {
93 eprintln!("traced at {} failed with {e:?}", spec.pos);
94 Err(e)
95 }
96 r => r,
97 };
98 if prev && !enable {
99 eprintln!("trace reenabled")
100 }
101 set_trace(prev);
102 r
103}
104
105#[allow(dead_code)]
106pub fn trace() -> bool {
107 TRACE.load(Ordering::Relaxed)
108}
109
110#[macro_export]
111macro_rules! tdbg {
112 ($e:expr) => {
113 if $crate::trace() {
114 dbg!($e)
115 } else {
116 $e
117 }
118 };
119}
120
121#[macro_export]
122macro_rules! err {
123 ($tag:expr, $err:literal) => {{
124 let e: Value = ($tag.clone(), ::arcstr::literal!($err)).into();
125 Value::Error(::triomphe::Arc::new(e))
126 }};
127}
128
129#[macro_export]
130macro_rules! errf {
131 ($tag:expr, $fmt:expr, $($args:expr),*) => {{
132 let msg: ArcStr = ::compact_str::format_compact!($fmt, $($args),*).as_str().into();
133 let e: Value = ($tag.clone(), msg).into();
134 Value::Error(::triomphe::Arc::new(e))
135 }};
136 ($tag:expr, $fmt:expr) => {{
137 let msg: ArcStr = ::compact_str::format_compact!($fmt).as_str().into();
138 let e: Value = ($tag.clone(), msg).into();
139 Value::Error(::triomphe::Arc::new(e))
140 }};
141}
142
143#[macro_export]
144macro_rules! defetyp {
145 ($name:ident, $tag_name:ident, $tag:literal, $typ:expr) => {
146 static $tag_name: ArcStr = ::arcstr::literal!($tag);
147 static $name: ::std::sync::LazyLock<$crate::typ::Type> =
148 ::std::sync::LazyLock::new(|| {
149 let scope = $crate::expr::ModPath::root();
150 $crate::expr::parser::parse_type(&format!($typ, $tag))
151 .expect("failed to parse type")
152 .scope_refs(&scope)
153 });
154 };
155}
156
157defetyp!(CAST_ERR, CAST_ERR_TAG, "InvalidCast", "Error<`{}(string)>");
158
159atomic_id!(LambdaId);
160
161impl From<u64> for LambdaId {
162 fn from(v: u64) -> Self {
163 LambdaId(v)
164 }
165}
166
167atomic_id!(BindId);
168
169impl From<u64> for BindId {
170 fn from(v: u64) -> Self {
171 BindId(v)
172 }
173}
174
175impl TryFrom<Value> for BindId {
176 type Error = anyhow::Error;
177
178 fn try_from(value: Value) -> Result<Self> {
179 match value {
180 Value::U64(id) => Ok(BindId(id)),
181 v => bail!("invalid bind id {v}"),
182 }
183 }
184}
185
186pub trait UserEvent: Clone + Debug + Any {
187 fn clear(&mut self);
188}
189
190pub trait CustomBuiltinType: Debug + Any + Send + Sync {}
191
192impl CustomBuiltinType for Value {}
193impl CustomBuiltinType for Option<Value> {}
194
195#[derive(Debug, Clone)]
196pub struct NoUserEvent;
197
198impl UserEvent for NoUserEvent {
199 fn clear(&mut self) {}
200}
201
202#[derive(Debug, Clone, Copy)]
203#[bitflags]
204#[repr(u64)]
205pub enum PrintFlag {
206 DerefTVars,
209 ReplacePrims,
212 NoSource,
214 NoParents,
216}
217
218thread_local! {
219 static PRINT_FLAGS: Cell<BitFlags<PrintFlag>> = Cell::new(PrintFlag::ReplacePrims.into());
220}
221
222pub static CBATCH_POOL: LazyLock<Pool<Vec<(BindId, Box<dyn CustomBuiltinType>)>>> =
224 LazyLock::new(|| Pool::new(10000, 1000));
225
226pub fn format_with_flags<G: Into<BitFlags<PrintFlag>>, R, F: FnOnce() -> R>(
230 flags: G,
231 f: F,
232) -> R {
233 let prev = PRINT_FLAGS.replace(flags.into());
234 let res = f();
235 PRINT_FLAGS.set(prev);
236 res
237}
238
239#[derive(Debug)]
245pub struct Event<E: UserEvent> {
246 pub init: bool,
247 pub variables: IntMap<BindId, Value>,
248 pub netidx: IntMap<SubId, subscriber::Event>,
249 pub writes: IntMap<Id, WriteRequest>,
250 pub rpc_calls: IntMap<BindId, RpcCall>,
251 pub custom: IntMap<BindId, Box<dyn CustomBuiltinType>>,
252 pub user: E,
253}
254
255impl<E: UserEvent> Event<E> {
256 pub fn new(user: E) -> Self {
257 Event {
258 init: false,
259 variables: IntMap::default(),
260 netidx: IntMap::default(),
261 writes: IntMap::default(),
262 rpc_calls: IntMap::default(),
263 custom: IntMap::default(),
264 user,
265 }
266 }
267
268 pub fn clear(&mut self) {
269 let Self { init, variables, netidx, rpc_calls, writes, custom, user } = self;
270 *init = false;
271 variables.clear();
272 netidx.clear();
273 rpc_calls.clear();
274 custom.clear();
275 writes.clear();
276 user.clear();
277 }
278}
279
280#[derive(Debug, Clone, Default)]
281pub struct Refs {
282 refed: LPooled<IntSet<BindId>>,
283 bound: LPooled<IntSet<BindId>>,
284}
285
286pub use combine::stream::position::SourcePosition;
287
288#[derive(Debug, Clone)]
300pub struct ReferenceSite {
301 pub pos: SourcePosition,
302 pub ori: Arc<expr::Origin>,
303 pub name: expr::ModPath,
304 pub bind_id: BindId,
305 pub def_pos: SourcePosition,
306 pub def_ori: Arc<expr::Origin>,
307}
308
309#[derive(Debug, Clone)]
314pub struct ModuleRefSite {
315 pub pos: SourcePosition,
316 pub ori: Arc<expr::Origin>,
317 pub name: expr::ModPath,
319 pub canonical: expr::ModPath,
321 pub def_ori: Option<Arc<expr::Origin>>,
325}
326
327#[derive(Debug, Clone)]
332pub struct ScopeMapEntry {
333 pub pos: SourcePosition,
334 pub ori: Arc<expr::Origin>,
335 pub scope: Scope,
336}
337
338#[derive(Debug, Clone)]
344pub struct TypeRefSite {
345 pub pos: SourcePosition,
346 pub ori: Arc<expr::Origin>,
347 pub name: expr::ModPath,
349 pub canonical_scope: expr::ModPath,
351 pub def_pos: SourcePosition,
352 pub def_ori: Arc<expr::Origin>,
353}
354
355#[derive(Debug, Clone)]
362pub struct SigImplLink {
363 pub scope: expr::ModPath,
364 pub name: compact_str::CompactString,
365 pub sig_id: BindId,
366 pub impl_id: BindId,
367}
368
369#[derive(Debug, Clone)]
375pub struct ModuleInternalView {
376 pub scope: expr::ModPath,
377 pub env: env::Env,
378}
379
380pub static REFERENCE_SITE_POOL: LazyLock<Pool<Vec<ReferenceSite>>> =
386 LazyLock::new(|| Pool::new(64, 65536));
387pub static MODULE_REF_SITE_POOL: LazyLock<Pool<Vec<ModuleRefSite>>> =
388 LazyLock::new(|| Pool::new(64, 65536));
389pub static SCOPE_MAP_ENTRY_POOL: LazyLock<Pool<Vec<ScopeMapEntry>>> =
390 LazyLock::new(|| Pool::new(64, 65536));
391impl Refs {
396 pub fn clear(&mut self) {
397 self.refed.clear();
398 self.bound.clear();
399 }
400
401 pub fn with_external_refs(&self, mut f: impl FnMut(BindId)) {
402 for id in &*self.refed {
403 if !self.bound.contains(id) {
404 f(*id);
405 }
406 }
407 }
408}
409
410pub type Node<R, E> = Box<dyn Update<R, E>>;
411
412#[derive(Debug)]
414pub enum TypecheckPhase<'a> {
415 Lambda,
417 CallSite(&'a FnType),
419}
420
421pub type InitFn<R, E> = sync::Arc<
422 dyn for<'a, 'b, 'c, 'd> Fn(
423 &'a Scope,
424 &'b mut ExecCtx<R, E>,
425 &'c mut [Node<R, E>],
426 Option<&'d FnType>,
427 ExprId,
428 ) -> Result<Box<dyn Apply<R, E>>>
429 + Send
430 + Sync
431 + 'static,
432>;
433
434pub trait Apply<R: Rt, E: UserEvent>: Debug + Send + Sync + Any {
439 fn update(
440 &mut self,
441 ctx: &mut ExecCtx<R, E>,
442 from: &mut [Node<R, E>],
443 event: &mut Event<E>,
444 ) -> Option<Value>;
445
446 fn delete(&mut self, _ctx: &mut ExecCtx<R, E>) {
449 ()
450 }
451
452 fn typecheck(
457 &mut self,
458 _ctx: &mut ExecCtx<R, E>,
459 _from: &mut [Node<R, E>],
460 _phase: TypecheckPhase<'_>,
461 ) -> Result<()> {
462 Ok(())
463 }
464
465 fn typ(&self) -> Arc<FnType> {
468 static EMPTY: LazyLock<Arc<FnType>> = LazyLock::new(|| {
469 Arc::new(FnType {
470 args: Arc::from_iter([]),
471 constraints: Arc::new(RwLock::new(LPooled::take())),
472 rtype: Type::Bottom,
473 throws: Type::Bottom,
474 vargs: None,
475 explicit_throws: false,
476 ..Default::default()
477 })
478 });
479 Arc::clone(&*EMPTY)
480 }
481
482 fn refs<'a>(&self, _refs: &mut Refs) {}
486
487 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>);
490}
491
492pub trait Update<R: Rt, E: UserEvent>: Debug + Send + Sync + Any + 'static {
496 fn update(&mut self, ctx: &mut ExecCtx<R, E>, event: &mut Event<E>) -> Option<Value>;
499
500 fn delete(&mut self, ctx: &mut ExecCtx<R, E>);
502
503 fn typecheck(&mut self, ctx: &mut ExecCtx<R, E>) -> Result<()>;
505
506 fn typ(&self) -> &Type;
508
509 fn refs(&self, refs: &mut Refs);
512
513 fn spec(&self) -> &Expr;
515
516 fn sleep(&mut self, ctx: &mut ExecCtx<R, E>);
518}
519
520pub type BuiltInInitFn<R, E> = for<'a, 'b, 'c, 'd> fn(
521 &'a mut ExecCtx<R, E>,
522 &'a FnType,
523 Option<&'d FnType>,
524 &'b Scope,
525 &'c [Node<R, E>],
526 ExprId,
527) -> Result<Box<dyn Apply<R, E>>>;
528
529pub trait BuiltIn<R: Rt, E: UserEvent> {
533 const NAME: &str;
534 const NEEDS_CALLSITE: bool;
535
536 fn init<'a, 'b, 'c, 'd>(
537 ctx: &'a mut ExecCtx<R, E>,
538 typ: &'a FnType,
539 resolved_type: Option<&'d FnType>,
540 scope: &'b Scope,
541 from: &'c [Node<R, E>],
542 top_id: ExprId,
543 ) -> Result<Box<dyn Apply<R, E>>>;
544}
545
546pub trait Abortable {
547 fn abort(&self);
548}
549
550impl Abortable for task::AbortHandle {
551 fn abort(&self) {
552 task::AbortHandle::abort(self)
553 }
554}
555
556pub trait Rt: Debug + Any {
557 type AbortHandle: Abortable;
558
559 fn clear(&mut self);
560
561 fn subscribe(&mut self, flags: UpdatesFlags, path: Path, ref_by: ExprId) -> Dval;
566
567 fn unsubscribe(&mut self, path: Path, dv: Dval, ref_by: ExprId);
569
570 fn list(&mut self, id: BindId, path: Path);
575
576 fn list_table(&mut self, id: BindId, path: Path);
579
580 fn stop_list(&mut self, id: BindId);
583
584 fn publish(&mut self, path: Path, value: Value, ref_by: ExprId) -> Result<Val>;
588
589 fn update(&mut self, id: &Val, value: Value);
591
592 fn unpublish(&mut self, id: Val, ref_by: ExprId);
594
595 fn ref_var(&mut self, id: BindId, ref_by: ExprId);
605 fn unref_var(&mut self, id: BindId, ref_by: ExprId);
606
607 fn set_var(&mut self, id: BindId, value: Value);
618
619 fn notify_set(&mut self, id: BindId);
626
627 fn call_rpc(&mut self, name: Path, args: Vec<(ArcStr, Value)>, id: BindId);
633
634 fn publish_rpc(
643 &mut self,
644 name: Path,
645 doc: Value,
646 spec: Vec<ArgSpec>,
647 id: BindId,
648 ) -> Result<()>;
649
650 fn unpublish_rpc(&mut self, name: Path);
652
653 fn set_timer(&mut self, id: BindId, timeout: Duration);
657
658 fn spawn<F: Future<Output = (BindId, Box<dyn CustomBuiltinType>)> + Send + 'static>(
666 &mut self,
667 f: F,
668 ) -> Self::AbortHandle;
669
670 fn spawn_var<F: Future<Output = (BindId, Value)> + Send + 'static>(
678 &mut self,
679 f: F,
680 ) -> Self::AbortHandle;
681
682 fn watch(
687 &mut self,
688 s: mpsc::Receiver<GPooled<Vec<(BindId, Box<dyn CustomBuiltinType>)>>>,
689 );
690
691 fn watch_var(&mut self, s: mpsc::Receiver<GPooled<Vec<(BindId, Value)>>>);
696}
697
698#[derive(Default)]
699pub struct LibState(AHashMap<TypeId, Box<dyn Any + Send + Sync>>);
700
701impl LibState {
702 pub fn get_or_default<T>(&mut self) -> &mut T
708 where
709 T: Default + Any + Send + Sync,
710 {
711 self.0
712 .entry(TypeId::of::<T>())
713 .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send + Sync>)
714 .downcast_mut::<T>()
715 .unwrap()
716 }
717
718 pub fn get_or_else<T, F>(&mut self, f: F) -> &mut T
724 where
725 T: Any + Send + Sync,
726 F: FnOnce() -> T,
727 {
728 self.0
729 .entry(TypeId::of::<T>())
730 .or_insert_with(|| Box::new(f()) as Box<dyn Any + Send + Sync>)
731 .downcast_mut::<T>()
732 .unwrap()
733 }
734
735 pub fn entry<'a, T>(
736 &'a mut self,
737 ) -> hash_map::Entry<'a, TypeId, Box<dyn Any + Send + Sync>>
738 where
739 T: Any + Send + Sync,
740 {
741 self.0.entry(TypeId::of::<T>())
742 }
743
744 pub fn contains<T>(&self) -> bool
746 where
747 T: Any + Send + Sync,
748 {
749 self.0.contains_key(&TypeId::of::<T>())
750 }
751
752 pub fn get<T>(&mut self) -> Option<&T>
757 where
758 T: Any + Send + Sync,
759 {
760 self.0.get(&TypeId::of::<T>()).map(|t| t.downcast_ref::<T>().unwrap())
761 }
762
763 pub fn get_mut<T>(&mut self) -> Option<&mut T>
768 where
769 T: Any + Send + Sync,
770 {
771 self.0.get_mut(&TypeId::of::<T>()).map(|t| t.downcast_mut::<T>().unwrap())
772 }
773
774 pub fn set<T>(&mut self, t: T) -> Option<Box<T>>
778 where
779 T: Any + Send + Sync,
780 {
781 self.0
782 .insert(TypeId::of::<T>(), Box::new(t) as Box<dyn Any + Send + Sync>)
783 .map(|t| t.downcast::<T>().unwrap())
784 }
785
786 pub fn remove<T>(&mut self) -> Option<Box<T>>
788 where
789 T: Any + Send + Sync,
790 {
791 self.0.remove(&TypeId::of::<T>()).map(|t| t.downcast::<T>().unwrap())
792 }
793}
794
795#[derive(Default)]
806pub struct AbstractTypeRegistry {
807 by_tid: AHashMap<TypeId, Uuid>,
808 by_uuid: AHashMap<Uuid, &'static str>,
809}
810
811impl AbstractTypeRegistry {
812 fn with<V, F: FnMut(&mut AbstractTypeRegistry) -> V>(mut f: F) -> V {
813 static REG: LazyLock<Mutex<AbstractTypeRegistry>> =
814 LazyLock::new(|| Mutex::new(AbstractTypeRegistry::default()));
815 let mut g = REG.lock();
816 f(&mut *g)
817 }
818
819 pub(crate) fn uuid<T: Any>(tag: &'static str) -> Uuid {
821 Self::with(|rg| {
822 *rg.by_tid.entry(TypeId::of::<T>()).or_insert_with(|| {
823 let id = Uuid::new_v4();
824 rg.by_uuid.insert(id, tag);
825 id
826 })
827 })
828 }
829
830 pub fn tag(a: &Abstract) -> Option<&'static str> {
832 Self::with(|rg| rg.by_uuid.get(&a.id()).map(|r| *r))
833 }
834
835 pub fn is_a(a: &Abstract, tag: &str) -> bool {
837 match Self::tag(a) {
838 Some(t) => t == tag,
839 None => false,
840 }
841 }
842}
843
844pub struct ExecCtx<R: Rt, E: UserEvent> {
845 lambdawrap: AbstractWrapper<LambdaDef<R, E>>,
847 builtins: AHashMap<&'static str, (BuiltInInitFn<R, E>, bool)>,
849 builtins_allowed: bool,
852 tags: AHashSet<ArcStr>,
854 pub libstate: LibState,
856 pub env: Env,
858 pub cached: IntMap<BindId, Value>,
860 pub rt: R,
862 pub lambda_defs: IntMap<LambdaId, Value>,
864 pub deferred_checks:
866 Vec<Box<dyn FnOnce(&mut ExecCtx<R, E>) -> Result<()> + Send + Sync>>,
867 pub references: GPooled<Vec<ReferenceSite>>,
874 pub module_references: GPooled<Vec<ModuleRefSite>>,
877 pub scope_map: GPooled<Vec<ScopeMapEntry>>,
881}
882
883impl<R: Rt, E: UserEvent> ExecCtx<R, E> {
884 pub fn clear(&mut self) {
885 self.env.clear();
886 self.rt.clear();
887 }
888
889 pub fn new(user: R) -> Result<Self> {
898 let id = AbstractTypeRegistry::uuid::<LambdaDef<R, E>>("lambda");
899 Ok(Self {
900 lambdawrap: Abstract::register(id)?,
901 env: Env::default(),
902 builtins: AHashMap::default(),
903 builtins_allowed: true,
904 libstate: LibState::default(),
905 tags: AHashSet::default(),
906 cached: IntMap::default(),
907 rt: user,
908 lambda_defs: IntMap::default(),
909 deferred_checks: Vec::new(),
910 references: REFERENCE_SITE_POOL.take(),
911 module_references: MODULE_REF_SITE_POOL.take(),
912 scope_map: SCOPE_MAP_ENTRY_POOL.take(),
913 })
914 }
915
916 pub fn register_builtin<T: BuiltIn<R, E>>(&mut self) -> Result<()> {
917 match self.builtins.entry(T::NAME) {
918 Entry::Vacant(e) => {
919 e.insert((T::init, T::NEEDS_CALLSITE));
920 }
921 Entry::Occupied(_) => bail!("builtin {} is already registered", T::NAME),
922 }
923 Ok(())
924 }
925
926 pub fn wrap_lambda(&mut self, def: LambdaDef<R, E>) -> Value {
932 let id = def.id;
933 let v = self.lambdawrap.wrap(def);
934 self.lambda_defs.insert(id, v.clone());
935 v
936 }
937
938 pub fn set_var(&mut self, id: BindId, v: Value) {
942 self.cached.insert(id, v.clone());
943 self.rt.set_var(id, v)
944 }
945
946 fn tag(&mut self, s: &ArcStr) -> ArcStr {
947 match self.tags.get(s) {
948 Some(s) => s.clone(),
949 None => {
950 self.tags.insert(s.clone());
951 s.clone()
952 }
953 }
954 }
955
956 pub fn with_restored<T, F: FnOnce(&mut Self) -> T>(&mut self, env: Env, f: F) -> T {
960 let snap = self.env.restore_lexical_env(env);
961 let orig = mem::replace(&mut self.env, snap);
962 let r = f(self);
963 self.env = self.env.restore_lexical_env(orig);
964 r
965 }
966
967 pub fn with_restored_mut<T, F: FnOnce(&mut Self) -> T>(
973 &mut self,
974 env: &mut Env,
975 f: F,
976 ) -> T {
977 let snap = self.env.restore_lexical_env_mut(env);
978 let orig = mem::replace(&mut self.env, snap);
979 let r = f(self);
980 *env = self.env.clone();
981 self.env = self.env.restore_lexical_env(orig);
982 r
983 }
984}
985
986#[derive(Debug, Clone)]
987pub struct Scope {
988 pub lexical: ModPath,
989 pub dynamic: ModPath,
990}
991
992impl Scope {
993 pub fn append<S: AsRef<str> + ?Sized>(&self, s: &S) -> Self {
994 Self {
995 lexical: ModPath(self.lexical.append(s)),
996 dynamic: ModPath(self.dynamic.append(s)),
997 }
998 }
999
1000 pub fn root() -> Self {
1001 Self { lexical: ModPath::root(), dynamic: ModPath::root() }
1002 }
1003}
1004
1005pub fn compile<R: Rt, E: UserEvent>(
1008 ctx: &mut ExecCtx<R, E>,
1009 flags: BitFlags<CFlag>,
1010 scope: &Scope,
1011 spec: Expr,
1012) -> Result<Node<R, E>> {
1013 let top_id = spec.id;
1014 let env = ctx.env.clone();
1015 let st = Instant::now();
1016 let mut node = match compiler::compile(ctx, flags, spec, scope, top_id) {
1017 Ok(n) => n,
1018 Err(e) => {
1019 ctx.env = env;
1020 return Err(e);
1021 }
1022 };
1023 info!("compile time {:?}", st.elapsed());
1024 let st = Instant::now();
1025 if let Err(e) = node.typecheck(ctx) {
1026 ctx.env = env;
1027 return Err(e);
1028 }
1029 while let Some(check) = ctx.deferred_checks.pop() {
1031 if let Err(e) = check(ctx) {
1032 ctx.env = env;
1033 return Err(e);
1034 }
1035 }
1036 info!("typecheck time {:?}", st.elapsed());
1037 Ok(node)
1038}