diff --git a/fire/src/lib.rs b/fire/src/lib.rs index 330fd754..27fb9235 100644 --- a/fire/src/lib.rs +++ b/fire/src/lib.rs @@ -36,11 +36,12 @@ impl Fire { // these two functions should probalby be part of a gc struct, which should be contained within the Fire fn allocate(&mut self, key: OriginIdx, value: Instance) { // if we allocate the same value twice, this is an interpreter error - assert!(self.values.insert(key, value).is_none()); + // FIXME: this is not specifically true - e.g. calling a function twice will allocate twice to the function's bindings, which is fine? + self.values.insert(key, value); } fn copy(&mut self, to_copy: &RefIdx, key: OriginIdx) { - if let Some(instance) = self.values.get(&to_copy.unwrap()) { + if let Some(instance) = self.values.get(&to_copy.expect_resolved()) { self.allocate(key, instance.clone()) } } @@ -59,7 +60,7 @@ impl Fire { _fir: &Fir>, node: &Node>, // allocate a value for this node's origin args: &[RefIdx], - ) { + ) -> Option { let ast = node.data.ast.node(); let name = match &ast.node { ast::Node::Function { @@ -74,7 +75,7 @@ impl Fire { if name.access() == "println" { args.iter().for_each(|arg| { - let value = self.values.get(&arg.unwrap()).unwrap(); + let value = self.values.get(&arg.expect_resolved()).unwrap(); // FIXME: Ugly as SIN println!("{}", unsafe { String::from_utf8_unchecked(value.data().into()) @@ -82,9 +83,7 @@ impl Fire { }) } - // this should allocate data - - // None + None } fn fire_block( @@ -95,12 +94,12 @@ impl Fire { ) { // How do we deal with returns in this system? stmts.iter().for_each(|node| { - let node = &fir.nodes[&node.unwrap()]; + let node = &fir.nodes[&node.expect_resolved()]; self.fire_node(fir, node); }); if let Some(last_stmt) = stmts.last() { - if let Kind::Return(_) = &fir.nodes[&last_stmt.unwrap()].kind { + if let Kind::Return(_) = &fir.nodes[&last_stmt.expect_resolved()].kind { self.transfer(last_stmt, node.origin) } } @@ -122,25 +121,33 @@ impl Fire { fn fire_call( &mut self, fir: &Fir>, - _node: &Node>, + node: &Node>, to: &RefIdx, args: &[RefIdx], ) { - let def = &fir.nodes[&to.unwrap()]; + let def = &fir.nodes[&to.expect_resolved()]; let (block, def_args) = match &def.kind { Kind::Function { block, args, .. } => (block, args), _ => unreachable!(), }; args.iter().enumerate().for_each(|(i, arg)| { - self.fire_node(fir, &fir.nodes[&arg.unwrap()]); - self.transfer(arg, def_args[i].unwrap()); + self.fire_node(fir, &fir.nodes[&arg.expect_resolved()]); + self.transfer(arg, def_args[i].expect_resolved()); }); // FIXME: We need to add bindings here between the function's variables and the arguments given to the call match block { - None => self.perform_extern_call(fir, def, args), - Some(block) => self.fire_node_ref(fir, block), // what to do here? + None => { + let result = self.perform_extern_call(fir, def, args); + if let Some(instance) = result { + self.allocate(node.origin, instance) + } + } + Some(block) => { + self.fire_node_ref(fir, block); // what to do here? + self.transfer(block, node.origin); + } } } @@ -159,30 +166,38 @@ impl Fire { &mut self, fir: &Fir>, node: &Node>, - _value: &RefIdx, + value: &RefIdx, ty: &RefIdx, ) { - let tyref = &fir.nodes[&ty.unwrap()]; - let fields = match &tyref.kind { - Kind::Type { fields, .. } => fields, - // FIXME: here we need to decide part of our copy/move semantics - Kind::TypeReference(_) => return, - other => { - dbg!(other); - unreachable!() + // what do we do here when we have a `value` but no `ty`? + match ty { + // this is a transfer + RefIdx::Unresolved => self.transfer(value, node.origin), + // this is an allocate? + RefIdx::Resolved(ty) => { + let tyref = &fir.nodes[ty]; + let fields = match &tyref.kind { + Kind::Type { fields, .. } => fields, + // FIXME: here we need to decide part of our copy/move semantics + Kind::TypeReference(_) => return, + other => { + dbg!(other); + unreachable!() + } + }; + + // TODO: can we just check if value == ty? + let instance = if fields.is_empty() { + Instance::empty() + } else { + unreachable!() + }; + + // FIXME: Handle result here + // FIXME: Should this be a transfer? + self.allocate(node.origin, instance); } - }; - - // TODO: can we just check if value == ty? - let instance = if fields.is_empty() { - Instance::empty() - } else { - unreachable!() - }; - - // FIXME: Handle result here - // FIXME: Should this be a transfer? - self.allocate(node.origin, instance); + } } fn fire_return( @@ -199,7 +214,7 @@ impl Fire { } fn fire_node_ref(&mut self, fir: &Fir>, node_ref: &RefIdx) { - let node = &fir.nodes[&node_ref.unwrap()]; + let node = &fir.nodes[&node_ref.expect_resolved()]; self.fire_node(fir, node) } diff --git a/interpreter/jinko.rs b/interpreter/jinko.rs index 52958a64..94d4d506 100644 --- a/interpreter/jinko.rs +++ b/interpreter/jinko.rs @@ -146,7 +146,9 @@ fn experimental_pipeline(input: &str, file: &Path) -> InteractResult { .display(&fir); let fir = x_try!(fir.type_check()); - let _result = fir.interpret(); + let result = fir.interpret(); + + dbg!(result); todo!("unfinished experimental pipeline: use result as exit code and display it") } diff --git a/typecheck/src/actual.rs b/typecheck/src/actual.rs index 7521b4f2..ac07bba9 100644 --- a/typecheck/src/actual.rs +++ b/typecheck/src/actual.rs @@ -21,6 +21,13 @@ fn innermost_type(fir: &Fir, linked_node: RefIdx) -> Option { // these are the *only* terminal branches Kind::Type { .. } => Some(Type::One(RefIdx::Resolved(linked_node.origin))), Kind::Assignment { .. } => None, + // if a typed value has no specific type, but points to a value, use this as the source of the type + Kind::TypedValue { + ty: RefIdx::Unresolved, + value, + // can we set `ty` here or not? probably not + // we need to :( + } => innermost_type(fir, *value), Kind::Constant(ty) | Kind::TypeReference(ty) | Kind::TypedValue { ty, .. } diff --git a/typecheck/src/typer.rs b/typecheck/src/typer.rs index db08bdda..b992c066 100644 --- a/typecheck/src/typer.rs +++ b/typecheck/src/typer.rs @@ -10,6 +10,12 @@ use crate::{Type, TypeCtx}; pub(crate) struct Typer<'ctx>(pub(crate) &'ctx mut TypeCtx); impl<'ctx> Typer<'ctx> { + fn assign_type(&mut self, node: OriginIdx, ty: Option) { + // Having non-unique ids in the Fir is an interpreter error + // Or should we return an error here? + assert!(self.0.types.insert(node, ty).is_none()); + } + /// Assign a type to a node. This type can either be void ([`None`]) in the case of a declaration or void /// statement, or may be a "type linked list": a reference to a type defined elsewhere in the [`Fir`]. /// Let's consider a block of multiple statements, the last of which being a function call. The type of a @@ -25,9 +31,7 @@ impl<'ctx> Typer<'ctx> { ) -> Result>, Error> { let ty = ty.map(Type::One); - // Having non-unique ids in the Fir is an interpreter error - // Or should we return an error here? - assert!(self.0.types.insert(node.origin, ty).is_none()); + self.assign_type(node.origin, ty); Ok(node) } @@ -80,10 +84,28 @@ impl<'ast> Mapper, FlattenData<'ast>, Error> for Typer<'_> { | fir::Kind::Function { .. } | fir::Kind::Binding { .. } | fir::Kind::Assignment { .. } => self.ty(node, None), + // // FIXME: This might be the wrong way to go about this + // // special case where we want to change the `ty` of a `TypedValue` + // fir::Kind::TypedValue { + // ty: RefIdx::Unresolved, + // value, + // } => { + // self.assign_type(node.origin, Some(Type::One(value))); + + // Ok(Node { + // // this seems dodgy at best + // kind: fir::Kind::TypedValue { value, ty: value }, + // ..node + // }) + // } // These nodes all refer to other nodes, type references or typed values. They will need // to be flattened later on. fir::Kind::TypeReference(ty) - | fir::Kind::TypedValue { value: ty, .. } + | fir::Kind::TypedValue { + ty: RefIdx::Unresolved, + value: ty, + } + | fir::Kind::TypedValue { ty, .. } | fir::Kind::Instantiation { to: ty, .. } | fir::Kind::Call { to: ty, .. } | fir::Kind::Conditional { true_block: ty, .. } => self.ty(node, Some(ty)),