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 anyhow::{bail, Result};
24use arcstr::ArcStr;
25use enumflags2::{bitflags, BitFlags};
26use expr::Expr;
27use futures::channel::mpsc;
28use fxhash::{FxHashMap, FxHashSet};
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 parking_lot::{Mutex, RwLock};
39use poolshark::{
40 global::{GPooled, Pool},
41 local::LPooled,
42};
43use std::{
44 any::{Any, TypeId},
45 cell::Cell,
46 collections::{
47 hash_map::{self, Entry},
48 HashMap,
49 },
50 fmt::Debug,
51 mem,
52 sync::{
53 self,
54 atomic::{AtomicBool, Ordering},
55 LazyLock,
56 },
57 time::Duration,
58};
59use tokio::{task, time::Instant};
60use triomphe::Arc;
61use uuid::Uuid;
62
63#[derive(Debug, Clone, Copy)]
64#[bitflags]
65#[repr(u64)]
66pub enum CFlag {
67 WarnUnhandled,
68 WarnUnused,
69 WarningsAreErrors,
70}
71
72#[allow(dead_code)]
73static TRACE: AtomicBool = AtomicBool::new(false);
74
75#[allow(dead_code)]
76pub fn set_trace(b: bool) {
77 TRACE.store(b, Ordering::Relaxed)
78}
79
80#[allow(dead_code)]
81pub fn with_trace<F: FnOnce() -> Result<R>, R>(
82 enable: bool,
83 spec: &Expr,
84 f: F,
85) -> Result<R> {
86 let prev = trace();
87 set_trace(enable);
88 if !prev && enable {
89 eprintln!("trace enabled at {}, spec: {}", spec.pos, spec);
90 } else if prev && !enable {
91 eprintln!("trace disabled at {}, spec: {}", spec.pos, spec);
92 }
93 let r = match f() {
94 Err(e) => {
95 eprintln!("traced at {} failed with {e:?}", spec.pos);
96 Err(e)
97 }
98 r => r,
99 };
100 if prev && !enable {
101 eprintln!("trace reenabled")
102 }
103 set_trace(prev);
104 r
105}
106
107#[allow(dead_code)]
108pub fn trace() -> bool {
109 TRACE.load(Ordering::Relaxed)
110}
111
112#[macro_export]
113macro_rules! tdbg {
114 ($e:expr) => {
115 if $crate::trace() {
116 dbg!($e)
117 } else {
118 $e
119 }
120 };
121}
122
123#[macro_export]
124macro_rules! err {
125 ($tag:expr, $err:literal) => {{
126 let e: Value = ($tag.clone(), ::arcstr::literal!($err)).into();
127 Value::Error(::triomphe::Arc::new(e))
128 }};
129}
130
131#[macro_export]
132macro_rules! errf {
133 ($tag:expr, $fmt:expr, $($args:expr),*) => {{
134 let msg: ArcStr = ::compact_str::format_compact!($fmt, $($args),*).as_str().into();
135 let e: Value = ($tag.clone(), msg).into();
136 Value::Error(::triomphe::Arc::new(e))
137 }};
138 ($tag:expr, $fmt:expr) => {{
139 let msg: ArcStr = ::compact_str::format_compact!($fmt).as_str().into();
140 let e: Value = ($tag.clone(), msg).into();
141 Value::Error(::triomphe::Arc::new(e))
142 }};
143}
144
145#[macro_export]
146macro_rules! defetyp {
147 ($name:ident, $tag_name:ident, $tag:literal, $typ:expr) => {
148 static $tag_name: ArcStr = ::arcstr::literal!($tag);
149 static $name: ::std::sync::LazyLock<$crate::typ::Type> =
150 ::std::sync::LazyLock::new(|| {
151 let scope = $crate::expr::ModPath::root();
152 $crate::expr::parser::parse_type(&format!($typ, $tag))
153 .expect("failed to parse type")
154 .scope_refs(&scope)
155 });
156 };
157}
158
159defetyp!(CAST_ERR, CAST_ERR_TAG, "InvalidCast", "Error<`{}(string)>");
160
161atomic_id!(LambdaId);
162
163impl From<u64> for LambdaId {
164 fn from(v: u64) -> Self {
165 LambdaId(v)
166 }
167}
168
169atomic_id!(BindId);
170
171impl From<u64> for BindId {
172 fn from(v: u64) -> Self {
173 BindId(v)
174 }
175}
176
177impl TryFrom<Value> for BindId {
178 type Error = anyhow::Error;
179
180 fn try_from(value: Value) -> Result<Self> {
181 match value {
182 Value::U64(id) => Ok(BindId(id)),
183 v => bail!("invalid bind id {v}"),
184 }
185 }
186}
187
188pub trait UserEvent: Clone + Debug + Any {
189 fn clear(&mut self);
190}
191
192pub trait CustomBuiltinType: Debug + Any + Send + Sync {}
193
194impl CustomBuiltinType for Value {}
195impl CustomBuiltinType for Option<Value> {}
196
197#[derive(Debug, Clone)]
198pub struct NoUserEvent;
199
200impl UserEvent for NoUserEvent {
201 fn clear(&mut self) {}
202}
203
204#[derive(Debug, Clone, Copy)]
205#[bitflags]
206#[repr(u64)]
207pub enum PrintFlag {
208 DerefTVars,
211 ReplacePrims,
214 NoSource,
216 NoParents,
218}
219
220thread_local! {
221 static PRINT_FLAGS: Cell<BitFlags<PrintFlag>> = Cell::new(PrintFlag::ReplacePrims.into());
222}
223
224pub static CBATCH_POOL: LazyLock<Pool<Vec<(BindId, Box<dyn CustomBuiltinType>)>>> =
226 LazyLock::new(|| Pool::new(10000, 1000));
227
228pub fn format_with_flags<G: Into<BitFlags<PrintFlag>>, R, F: FnOnce() -> R>(
232 flags: G,
233 f: F,
234) -> R {
235 let prev = PRINT_FLAGS.replace(flags.into());
236 let res = f();
237 PRINT_FLAGS.set(prev);
238 res
239}
240
241#[derive(Debug)]
247pub struct Event<E: UserEvent> {
248 pub init: bool,
249 pub variables: FxHashMap<BindId, Value>,
250 pub netidx: FxHashMap<SubId, subscriber::Event>,
251 pub writes: FxHashMap<Id, WriteRequest>,
252 pub rpc_calls: FxHashMap<BindId, RpcCall>,
253 pub custom: FxHashMap<BindId, Box<dyn CustomBuiltinType>>,
254 pub user: E,
255}
256
257impl<E: UserEvent> Event<E> {
258 pub fn new(user: E) -> Self {
259 Event {
260 init: false,
261 variables: HashMap::default(),
262 netidx: HashMap::default(),
263 writes: HashMap::default(),
264 rpc_calls: HashMap::default(),
265 custom: HashMap::default(),
266 user,
267 }
268 }
269
270 pub fn clear(&mut self) {
271 let Self { init, variables, netidx, rpc_calls, writes, custom, user } = self;
272 *init = false;
273 variables.clear();
274 netidx.clear();
275 rpc_calls.clear();
276 custom.clear();
277 writes.clear();
278 user.clear();
279 }
280}
281
282#[derive(Debug, Clone, Default)]
283pub struct Refs {
284 refed: LPooled<FxHashSet<BindId>>,
285 bound: LPooled<FxHashSet<BindId>>,
286}
287
288pub use combine::stream::position::SourcePosition;
289
290#[derive(Debug, Clone)]
302pub struct ReferenceSite {
303 pub pos: SourcePosition,
304 pub ori: Arc<expr::Origin>,
305 pub name: expr::ModPath,
306 pub bind_id: BindId,
307 pub def_pos: SourcePosition,
308 pub def_ori: Arc<expr::Origin>,
309}
310
311#[derive(Debug, Clone)]
316pub struct ModuleRefSite {
317 pub pos: SourcePosition,
318 pub ori: Arc<expr::Origin>,
319 pub name: expr::ModPath,
321 pub canonical: expr::ModPath,
323 pub def_ori: Option<Arc<expr::Origin>>,
327}
328
329#[derive(Debug, Clone)]
334pub struct ScopeMapEntry {
335 pub pos: SourcePosition,
336 pub ori: Arc<expr::Origin>,
337 pub scope: Scope,
338}
339
340#[derive(Debug, Clone)]
346pub struct TypeRefSite {
347 pub pos: SourcePosition,
348 pub ori: Arc<expr::Origin>,
349 pub name: expr::ModPath,
351 pub canonical_scope: expr::ModPath,
353 pub def_pos: SourcePosition,
354 pub def_ori: Arc<expr::Origin>,
355}
356
357#[derive(Debug, Clone)]
364pub struct SigImplLink {
365 pub scope: expr::ModPath,
366 pub name: compact_str::CompactString,
367 pub sig_id: BindId,
368 pub impl_id: BindId,
369}
370
371#[derive(Debug, Clone)]
377pub struct ModuleInternalView {
378 pub scope: expr::ModPath,
379 pub env: env::Env,
380}
381
382pub static REFERENCE_SITE_POOL: LazyLock<Pool<Vec<ReferenceSite>>> =
388 LazyLock::new(|| Pool::new(64, 65536));
389pub static MODULE_REF_SITE_POOL: LazyLock<Pool<Vec<ModuleRefSite>>> =
390 LazyLock::new(|| Pool::new(64, 65536));
391pub static SCOPE_MAP_ENTRY_POOL: LazyLock<Pool<Vec<ScopeMapEntry>>> =
392 LazyLock::new(|| Pool::new(64, 65536));
393impl Refs {
398 pub fn clear(&mut self) {
399 self.refed.clear();
400 self.bound.clear();
401 }
402
403 pub fn with_external_refs(&self, mut f: impl FnMut(BindId)) {
404 for id in &*self.refed {
405 if !self.bound.contains(id) {
406 f(*id);
407 }
408 }
409 }
410}
411
412pub type Node<R, E> = Box<dyn Update<R, E>>;
413
414#[derive(Debug)]
416pub enum TypecheckPhase<'a> {
417 Lambda,
419 CallSite(&'a FnType),
421}
422
423pub type InitFn<R, E> = sync::Arc<
424 dyn for<'a, 'b, 'c, 'd> Fn(
425 &'a Scope,
426 &'b mut ExecCtx<R, E>,
427 &'c mut [Node<R, E>],
428 Option<&'d FnType>,
429 ExprId,
430 ) -> Result<Box<dyn Apply<R, E>>>
431 + Send
432 + Sync
433 + 'static,
434>;
435
436pub trait Apply<R: Rt, E: UserEvent>: Debug + Send + Sync + Any {
441 fn update(
442 &mut self,
443 ctx: &mut ExecCtx<R, E>,
444 from: &mut [Node<R, E>],
445 event: &mut Event<E>,
446 ) -> Option<Value>;
447
448 fn delete(&mut self, _ctx: &mut ExecCtx<R, E>) {
451 ()
452 }
453
454 fn typecheck(
459 &mut self,
460 _ctx: &mut ExecCtx<R, E>,
461 _from: &mut [Node<R, E>],
462 _phase: TypecheckPhase<'_>,
463 ) -> Result<()> {
464 Ok(())
465 }
466
467 fn typ(&self) -> Arc<FnType> {
470 static EMPTY: LazyLock<Arc<FnType>> = LazyLock::new(|| {
471 Arc::new(FnType {
472 args: Arc::from_iter([]),
473 constraints: Arc::new(RwLock::new(LPooled::take())),
474 rtype: Type::Bottom,
475 throws: Type::Bottom,
476 vargs: None,
477 explicit_throws: false,
478 ..Default::default()
479 })
480 });
481 Arc::clone(&*EMPTY)
482 }
483
484 fn refs<'a>(&self, _refs: &mut Refs) {}
488
489 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>);
492}
493
494pub trait Update<R: Rt, E: UserEvent>: Debug + Send + Sync + Any + 'static {
498 fn update(&mut self, ctx: &mut ExecCtx<R, E>, event: &mut Event<E>) -> Option<Value>;
501
502 fn delete(&mut self, ctx: &mut ExecCtx<R, E>);
504
505 fn typecheck(&mut self, ctx: &mut ExecCtx<R, E>) -> Result<()>;
507
508 fn typ(&self) -> &Type;
510
511 fn refs(&self, refs: &mut Refs);
514
515 fn spec(&self) -> &Expr;
517
518 fn sleep(&mut self, ctx: &mut ExecCtx<R, E>);
520}
521
522pub type BuiltInInitFn<R, E> = for<'a, 'b, 'c, 'd> fn(
523 &'a mut ExecCtx<R, E>,
524 &'a FnType,
525 Option<&'d FnType>,
526 &'b Scope,
527 &'c [Node<R, E>],
528 ExprId,
529) -> Result<Box<dyn Apply<R, E>>>;
530
531pub trait BuiltIn<R: Rt, E: UserEvent> {
535 const NAME: &str;
536 const NEEDS_CALLSITE: bool;
537
538 fn init<'a, 'b, 'c, 'd>(
539 ctx: &'a mut ExecCtx<R, E>,
540 typ: &'a FnType,
541 resolved_type: Option<&'d FnType>,
542 scope: &'b Scope,
543 from: &'c [Node<R, E>],
544 top_id: ExprId,
545 ) -> Result<Box<dyn Apply<R, E>>>;
546}
547
548pub trait Abortable {
549 fn abort(&self);
550}
551
552impl Abortable for task::AbortHandle {
553 fn abort(&self) {
554 task::AbortHandle::abort(self)
555 }
556}
557
558pub trait Rt: Debug + Any {
559 type AbortHandle: Abortable;
560
561 fn clear(&mut self);
562
563 fn subscribe(&mut self, flags: UpdatesFlags, path: Path, ref_by: ExprId) -> Dval;
568
569 fn unsubscribe(&mut self, path: Path, dv: Dval, ref_by: ExprId);
571
572 fn list(&mut self, id: BindId, path: Path);
577
578 fn list_table(&mut self, id: BindId, path: Path);
581
582 fn stop_list(&mut self, id: BindId);
585
586 fn publish(&mut self, path: Path, value: Value, ref_by: ExprId) -> Result<Val>;
590
591 fn update(&mut self, id: &Val, value: Value);
593
594 fn unpublish(&mut self, id: Val, ref_by: ExprId);
596
597 fn ref_var(&mut self, id: BindId, ref_by: ExprId);
607 fn unref_var(&mut self, id: BindId, ref_by: ExprId);
608
609 fn set_var(&mut self, id: BindId, value: Value);
620
621 fn notify_set(&mut self, id: BindId);
628
629 fn call_rpc(&mut self, name: Path, args: Vec<(ArcStr, Value)>, id: BindId);
635
636 fn publish_rpc(
645 &mut self,
646 name: Path,
647 doc: Value,
648 spec: Vec<ArgSpec>,
649 id: BindId,
650 ) -> Result<()>;
651
652 fn unpublish_rpc(&mut self, name: Path);
654
655 fn set_timer(&mut self, id: BindId, timeout: Duration);
659
660 fn spawn<F: Future<Output = (BindId, Box<dyn CustomBuiltinType>)> + Send + 'static>(
668 &mut self,
669 f: F,
670 ) -> Self::AbortHandle;
671
672 fn spawn_var<F: Future<Output = (BindId, Value)> + Send + 'static>(
680 &mut self,
681 f: F,
682 ) -> Self::AbortHandle;
683
684 fn watch(
689 &mut self,
690 s: mpsc::Receiver<GPooled<Vec<(BindId, Box<dyn CustomBuiltinType>)>>>,
691 );
692
693 fn watch_var(&mut self, s: mpsc::Receiver<GPooled<Vec<(BindId, Value)>>>);
698}
699
700#[derive(Default)]
701pub struct LibState(FxHashMap<TypeId, Box<dyn Any + Send + Sync>>);
702
703impl LibState {
704 pub fn get_or_default<T>(&mut self) -> &mut T
710 where
711 T: Default + Any + Send + Sync,
712 {
713 self.0
714 .entry(TypeId::of::<T>())
715 .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send + Sync>)
716 .downcast_mut::<T>()
717 .unwrap()
718 }
719
720 pub fn get_or_else<T, F>(&mut self, f: F) -> &mut T
726 where
727 T: Any + Send + Sync,
728 F: FnOnce() -> T,
729 {
730 self.0
731 .entry(TypeId::of::<T>())
732 .or_insert_with(|| Box::new(f()) as Box<dyn Any + Send + Sync>)
733 .downcast_mut::<T>()
734 .unwrap()
735 }
736
737 pub fn entry<'a, T>(
738 &'a mut self,
739 ) -> hash_map::Entry<'a, TypeId, Box<dyn Any + Send + Sync>>
740 where
741 T: Any + Send + Sync,
742 {
743 self.0.entry(TypeId::of::<T>())
744 }
745
746 pub fn contains<T>(&self) -> bool
748 where
749 T: Any + Send + Sync,
750 {
751 self.0.contains_key(&TypeId::of::<T>())
752 }
753
754 pub fn get<T>(&mut self) -> Option<&T>
759 where
760 T: Any + Send + Sync,
761 {
762 self.0.get(&TypeId::of::<T>()).map(|t| t.downcast_ref::<T>().unwrap())
763 }
764
765 pub fn get_mut<T>(&mut self) -> Option<&mut T>
770 where
771 T: Any + Send + Sync,
772 {
773 self.0.get_mut(&TypeId::of::<T>()).map(|t| t.downcast_mut::<T>().unwrap())
774 }
775
776 pub fn set<T>(&mut self, t: T) -> Option<Box<T>>
780 where
781 T: Any + Send + Sync,
782 {
783 self.0
784 .insert(TypeId::of::<T>(), Box::new(t) as Box<dyn Any + Send + Sync>)
785 .map(|t| t.downcast::<T>().unwrap())
786 }
787
788 pub fn remove<T>(&mut self) -> Option<Box<T>>
790 where
791 T: Any + Send + Sync,
792 {
793 self.0.remove(&TypeId::of::<T>()).map(|t| t.downcast::<T>().unwrap())
794 }
795}
796
797#[derive(Default)]
808pub struct AbstractTypeRegistry {
809 by_tid: FxHashMap<TypeId, Uuid>,
810 by_uuid: FxHashMap<Uuid, &'static str>,
811}
812
813impl AbstractTypeRegistry {
814 fn with<V, F: FnMut(&mut AbstractTypeRegistry) -> V>(mut f: F) -> V {
815 static REG: LazyLock<Mutex<AbstractTypeRegistry>> =
816 LazyLock::new(|| Mutex::new(AbstractTypeRegistry::default()));
817 let mut g = REG.lock();
818 f(&mut *g)
819 }
820
821 pub(crate) fn uuid<T: Any>(tag: &'static str) -> Uuid {
823 Self::with(|rg| {
824 *rg.by_tid.entry(TypeId::of::<T>()).or_insert_with(|| {
825 let id = Uuid::new_v4();
826 rg.by_uuid.insert(id, tag);
827 id
828 })
829 })
830 }
831
832 pub fn tag(a: &Abstract) -> Option<&'static str> {
834 Self::with(|rg| rg.by_uuid.get(&a.id()).map(|r| *r))
835 }
836
837 pub fn is_a(a: &Abstract, tag: &str) -> bool {
839 match Self::tag(a) {
840 Some(t) => t == tag,
841 None => false,
842 }
843 }
844}
845
846pub struct ExecCtx<R: Rt, E: UserEvent> {
847 lambdawrap: AbstractWrapper<LambdaDef<R, E>>,
849 builtins: FxHashMap<&'static str, (BuiltInInitFn<R, E>, bool)>,
851 builtins_allowed: bool,
854 tags: FxHashSet<ArcStr>,
856 pub libstate: LibState,
858 pub env: Env,
860 pub cached: FxHashMap<BindId, Value>,
862 pub rt: R,
864 pub lambda_defs: FxHashMap<LambdaId, Value>,
866 pub deferred_checks:
868 Vec<Box<dyn FnOnce(&mut ExecCtx<R, E>) -> Result<()> + Send + Sync>>,
869 pub references: GPooled<Vec<ReferenceSite>>,
876 pub module_references: GPooled<Vec<ModuleRefSite>>,
879 pub scope_map: GPooled<Vec<ScopeMapEntry>>,
883}
884
885impl<R: Rt, E: UserEvent> ExecCtx<R, E> {
886 pub fn clear(&mut self) {
887 self.env.clear();
888 self.rt.clear();
889 }
890
891 pub fn new(user: R) -> Result<Self> {
900 let id = AbstractTypeRegistry::uuid::<LambdaDef<R, E>>("lambda");
901 Ok(Self {
902 lambdawrap: Abstract::register(id)?,
903 env: Env::default(),
904 builtins: FxHashMap::default(),
905 builtins_allowed: true,
906 libstate: LibState::default(),
907 tags: FxHashSet::default(),
908 cached: HashMap::default(),
909 rt: user,
910 lambda_defs: FxHashMap::default(),
911 deferred_checks: Vec::new(),
912 references: REFERENCE_SITE_POOL.take(),
913 module_references: MODULE_REF_SITE_POOL.take(),
914 scope_map: SCOPE_MAP_ENTRY_POOL.take(),
915 })
916 }
917
918 pub fn register_builtin<T: BuiltIn<R, E>>(&mut self) -> Result<()> {
919 match self.builtins.entry(T::NAME) {
920 Entry::Vacant(e) => {
921 e.insert((T::init, T::NEEDS_CALLSITE));
922 }
923 Entry::Occupied(_) => bail!("builtin {} is already registered", T::NAME),
924 }
925 Ok(())
926 }
927
928 pub fn wrap_lambda(&mut self, def: LambdaDef<R, E>) -> Value {
934 let id = def.id;
935 let v = self.lambdawrap.wrap(def);
936 self.lambda_defs.insert(id, v.clone());
937 v
938 }
939
940 pub fn set_var(&mut self, id: BindId, v: Value) {
944 self.cached.insert(id, v.clone());
945 self.rt.set_var(id, v)
946 }
947
948 fn tag(&mut self, s: &ArcStr) -> ArcStr {
949 match self.tags.get(s) {
950 Some(s) => s.clone(),
951 None => {
952 self.tags.insert(s.clone());
953 s.clone()
954 }
955 }
956 }
957
958 pub fn with_restored<T, F: FnOnce(&mut Self) -> T>(&mut self, env: Env, f: F) -> T {
962 let snap = self.env.restore_lexical_env(env);
963 let orig = mem::replace(&mut self.env, snap);
964 let r = f(self);
965 self.env = self.env.restore_lexical_env(orig);
966 r
967 }
968
969 pub fn with_restored_mut<T, F: FnOnce(&mut Self) -> T>(
975 &mut self,
976 env: &mut Env,
977 f: F,
978 ) -> T {
979 let snap = self.env.restore_lexical_env_mut(env);
980 let orig = mem::replace(&mut self.env, snap);
981 let r = f(self);
982 *env = self.env.clone();
983 self.env = self.env.restore_lexical_env(orig);
984 r
985 }
986}
987
988#[derive(Debug, Clone)]
989pub struct Scope {
990 pub lexical: ModPath,
991 pub dynamic: ModPath,
992}
993
994impl Scope {
995 pub fn append<S: AsRef<str> + ?Sized>(&self, s: &S) -> Self {
996 Self {
997 lexical: ModPath(self.lexical.append(s)),
998 dynamic: ModPath(self.dynamic.append(s)),
999 }
1000 }
1001
1002 pub fn root() -> Self {
1003 Self { lexical: ModPath::root(), dynamic: ModPath::root() }
1004 }
1005}
1006
1007pub fn compile<R: Rt, E: UserEvent>(
1010 ctx: &mut ExecCtx<R, E>,
1011 flags: BitFlags<CFlag>,
1012 scope: &Scope,
1013 spec: Expr,
1014) -> Result<Node<R, E>> {
1015 let top_id = spec.id;
1016 let env = ctx.env.clone();
1017 let st = Instant::now();
1018 let mut node = match compiler::compile(ctx, flags, spec, scope, top_id) {
1019 Ok(n) => n,
1020 Err(e) => {
1021 ctx.env = env;
1022 return Err(e);
1023 }
1024 };
1025 info!("compile time {:?}", st.elapsed());
1026 let st = Instant::now();
1027 if let Err(e) = node.typecheck(ctx) {
1028 ctx.env = env;
1029 return Err(e);
1030 }
1031 while let Some(check) = ctx.deferred_checks.pop() {
1033 if let Err(e) = check(ctx) {
1034 ctx.env = env;
1035 return Err(e);
1036 }
1037 }
1038 info!("typecheck time {:?}", st.elapsed());
1039 Ok(node)
1040}