Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions fir/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
mod mapper;
mod multi_mapper;
mod traverse;
mod tree_like;

pub use mapper::Mapper;
pub use multi_mapper::MultiMapper;
pub use traverse::Traversal;
pub use tree_like::TreeLike;

use crate::Fir;

Expand Down
186 changes: 186 additions & 0 deletions fir/src/iter/tree_like.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
use crate::{Fir, Kind, Node, OriginIdx, RefIdx};

// FIXME: Should we still be able to emit errors?
pub trait TreeLike<'ast, 'fir, T> {
fn visit_many(&mut self, fir: &Fir<T>, many: &[RefIdx]) {
many.iter().for_each(|r| self.visit_reference(fir, r))
}

fn visit_optional(&mut self, fir: &Fir<T>, reference: &Option<RefIdx>) {
if let Some(r) = reference {
self.visit_reference(fir, r)
}
}

fn visit_reference(&mut self, fir: &Fir<T>, reference: &RefIdx) {
self.visit(fir, &reference.expect_resolved())
}

fn visit_constant(&mut self, fir: &Fir<T>, _node: &Node<T>, c: &RefIdx) {
self.visit_reference(fir, c)
}

fn visit_type_reference(&mut self, fir: &Fir<T>, _node: &Node<T>, to: &RefIdx) {
self.visit_reference(fir, to)
}

fn visit_typed_value(&mut self, fir: &Fir<T>, _node: &Node<T>, value: &RefIdx, ty: &RefIdx) {
self.visit_reference(fir, value);
self.visit_reference(fir, ty);
}

fn visit_generic(&mut self, fir: &Fir<T>, _node: &Node<T>, default: &Option<RefIdx>) {
self.visit_optional(fir, default)
}

fn visit_record_type(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
generics: &[RefIdx],
fields: &[RefIdx],
) {
self.visit_many(fir, generics);
self.visit_many(fir, fields);
}

fn visit_union_type(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
generics: &[RefIdx],
variants: &[RefIdx],
) {
self.visit_many(fir, generics);
self.visit_many(fir, variants);
}

fn visit_function(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
generics: &[RefIdx],
args: &[RefIdx],
return_type: &Option<RefIdx>,
block: &Option<RefIdx>,
) {
self.visit_many(fir, generics);
self.visit_many(fir, args);
self.visit_optional(fir, return_type);
self.visit_optional(fir, block);
}

fn visit_binding(&mut self, fir: &Fir<T>, _node: &Node<T>, to: &RefIdx) {
self.visit_reference(fir, to)
}

fn visit_assignment(&mut self, fir: &Fir<T>, _node: &Node<T>, to: &RefIdx, from: &RefIdx) {
self.visit_reference(fir, to);
self.visit_reference(fir, from);
}

fn visit_instantiation(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
to: &RefIdx,
generics: &[RefIdx],
fields: &[RefIdx],
) {
self.visit_reference(fir, to);
self.visit_many(fir, generics);
self.visit_many(fir, fields);
}

fn visit_type_offset(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
instance: &RefIdx,
field: &RefIdx,
) {
self.visit_reference(fir, instance);
self.visit_reference(fir, field);
}

fn visit_call(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
to: &RefIdx,
generics: &[RefIdx],
args: &[RefIdx],
) {
self.visit_reference(fir, to);
self.visit_many(fir, generics);
self.visit_many(fir, args);
}

fn visit_conditional(
&mut self,
fir: &Fir<T>,
_node: &Node<T>,
condition: &RefIdx,
true_block: &RefIdx,
false_block: &Option<RefIdx>,
) {
self.visit_reference(fir, condition);
self.visit_reference(fir, true_block);
self.visit_optional(fir, false_block);
}

fn visit_loop(&mut self, fir: &Fir<T>, _node: &Node<T>, condition: &RefIdx, block: &RefIdx) {
self.visit_reference(fir, condition);
self.visit_reference(fir, block);
}

fn visit_statements(&mut self, fir: &Fir<T>, _node: &Node<T>, stmts: &[RefIdx]) {
self.visit_many(fir, stmts)
}

fn visit_return(&mut self, fir: &Fir<T>, _node: &Node<T>, value: &Option<RefIdx>) {
self.visit_optional(fir, value)
}

fn visit(&mut self, fir: &Fir<T>, start: &OriginIdx) {
let node = &fir[start];

match &node.kind {
Kind::Constant(c) => self.visit_constant(fir, node, c),
Kind::TypeReference(to) => self.visit_type_reference(fir, node, to),
Kind::TypedValue { value, ty } => self.visit_typed_value(fir, node, value, ty),
Kind::Generic { default } => self.visit_generic(fir, node, default),
Kind::RecordType { generics, fields } => {
self.visit_record_type(fir, node, generics, fields)
}
Kind::UnionType { generics, variants } => {
self.visit_union_type(fir, node, generics, variants)
}
Kind::Function {
generics,
args,
return_type,
block,
} => self.visit_function(fir, node, generics, args, return_type, block),
Kind::Binding { to } => self.visit_binding(fir, node, to),
Kind::Assignment { to, from } => self.visit_assignment(fir, node, to, from),
Kind::Instantiation {
to,
generics,
fields,
} => self.visit_instantiation(fir, node, to, generics, fields),
Kind::TypeOffset { instance, field } => {
self.visit_type_offset(fir, node, instance, field)
}
Kind::Call { to, generics, args } => self.visit_call(fir, node, to, generics, args),
Kind::Conditional {
condition,
true_block,
false_block,
} => self.visit_conditional(fir, node, condition, true_block, false_block),
Kind::Loop { condition, block } => self.visit_loop(fir, node, condition, block),
Kind::Statements(stmts) => self.visit_statements(fir, node, stmts),
Kind::Return(value) => self.visit_return(fir, node, value),
}
}
}
4 changes: 2 additions & 2 deletions fire/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ impl<'ast, 'fir> Fire<'ast, 'fir> {
// TODO: Rename? `node`? `node_from_ref`? `rnode`? `ref`? `view`?
// should this return an `AccessedNode` or w/ever which we can then `.fire()`?
fn access(&self, r: &RefIdx) -> &'fir Node<FlattenData<'ast>> {
&self.fir.nodes[&r.expect_resolved()]
&self.fir[r]
}

// TODO: Rename: `access_origin`?
fn access_resolved(&self, resolved: &OriginIdx) -> &'fir Node<FlattenData<'ast>> {
&self.fir.nodes[&resolved]
&self.fir[resolved]
}

#[must_use]
Expand Down
9 changes: 9 additions & 0 deletions name_resolve/src/declarator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ impl<'ast, 'ctx, 'enclosing> Traversal<FlattenData<'ast>, NameResolutionError>
) -> Fallible<NameResolutionError> {
self.define(DefinitionKind::Binding, node)
}

fn traverse_generic(
&mut self,
_: &Fir<FlattenData<'ast>>,
node: &Node<FlattenData<'ast>>,
_: &Option<RefIdx>,
) -> Fallible<NameResolutionError> {
self.define(DefinitionKind::Type, node)
}
}
22 changes: 22 additions & 0 deletions typecheck/src/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// TODO: Typer could also be responsible for building the list of monomorphization requests (and constraints?)
// so something like a constraints: Map<TypeToMono, Constraints>?
// -> this is good but it needs to be per invocation - so per function call/type instantiation
// -> so is it more like a Map<GenericCall, Constraints>?
// -> Then Checker will be responsible for checking that T: Constraints?
// -> this seems annoying to deal with
// a good API would be something like - ConstraintBuilder -> Checker -> Monormorphizer
// with Checker checking the constraints? does that make sense?
// do we want to go with an easier first step where we have ConstraintBuilder -> Monomorphizer -> Checker? And we
// don't need to add anything to Checker? Since all functions and types will already be mono'd. But then the errors
// will be shit so we might as well build the constraints from the get-go.
// Now do we want to instead have a ConstraintChecker? who just takes care of checking constraints? then Mono and we
// can probably pass all of that to checker anyway. This is probably a better split. In that case, we should move
// this over to a new module.

// ConstraintBuilder -> ConstraintMap
// ConstraintChecker(ConstraintMap) -> Result<MonoRequests>
// Monomorphizer(MonoRequests) -> Result<Fir> // infaillible?

mod constraint_builder;

pub use constraint_builder::ConstraintBuilder;
Loading