This commit is contained in:
Folkert 2020-12-17 20:22:57 +01:00
parent 5b045f8a86
commit 2f0fe94182
7 changed files with 723 additions and 59 deletions

View file

@ -1,13 +1,12 @@
use crate::pattern::{Pattern2, PatternId};
use crate::pool::{NodeId, PoolStr, PoolVec};
use crate::types::{Type2, TypeId};
use arraystring::{typenum::U30, ArrayString};
use roc_can::def::Annotation;
use roc_can::expr::Recursive;
use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia;
use roc_module::symbol::Symbol;
use roc_types::subs::Variable;
use roc_types::types::Type;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Problem {
@ -197,39 +196,34 @@ pub enum Expr2 {
RuntimeError(/* TODO make a version of RuntimeError that fits in 15B */),
}
#[derive(Debug)]
pub struct Def {
pub pattern: NodeId<Pattern2>, // 3B
pub expr: NodeId<Expr2>, // 3B
// TODO maybe need to combine these vars behind a pointer?
pub expr_var: Variable, // 4B
pub pattern_vars: PoolVec<(Symbol, Variable)>, // 4B
// TODO how big is an annotation? What about an Option<Annotation>?
pub annotation: Option<Annotation>, // ???
}
#[derive(Debug)]
pub struct ValueDef {
pub pattern: PatternId,
pub expr_type: Option<Type>,
pub expr_var: Variable,
pattern: PatternId, // 4B
expr_type: Option<(Type2, Rigids)>, // ?
expr_var: Variable, // 4B
}
#[derive(Debug)]
pub enum FunctionDef {
WithAnnotation {
name: Symbol, // 8B
arguments: PoolVec<(Pattern2, Type, Variable)>, // 8B
return_type: Type, // ?
return_var: Variable, // 4B
arguments: PoolVec<(Pattern2, Type2)>, // 8B
rigids: NodeId<Rigids>, // 4B
return_type: TypeId, // 4B
},
NoAnnotation {
name: Symbol,
arguments: PoolVec<(Pattern2, Variable)>,
return_var: Variable,
name: Symbol, // 8B
arguments: PoolVec<(Pattern2, Variable)>, // 8B
return_var: Variable, // 4B
},
}
#[derive(Debug)]
pub struct Rigids {
named: PoolVec<(PoolStr, Variable)>, // 8B
unnamed: PoolVec<Variable>, // 8B
}
/// This is overflow data from a Closure variant, which needs to store
/// more than 32B of total data
#[derive(Debug)]
@ -247,17 +241,8 @@ pub struct WhenBranch {
pub guard: Option<NodeId<Expr2>>, // 4B
}
pub type PatternId = NodeId<Pattern2>;
pub type ExprId = NodeId<Expr2>;
// We have a maximum of 65K pages.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ExprPoolId(u16);
/// Each of these is the index of one 16B node inside a page's 4096B
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ExprPoolSlot(u8);
#[test]
fn size_of_expr() {
assert_eq!(std::mem::size_of::<Expr2>(), crate::pool::NODE_BYTES);

View file

@ -2,7 +2,11 @@
#![allow(dead_code)]
#![allow(unused_imports)]
use crate::ast::{Expr2, ExprId, FloatVal, IntStyle, IntVal};
use crate::pattern::to_pattern2;
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
use crate::types::{Type2, TypeId};
use bumpalo::Bump;
use inlinable_string::InlinableString;
use roc_can::expr::Output;
use roc_can::expr::Recursive;
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
@ -18,33 +22,37 @@ use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
pub struct Env<'a> {
home: ModuleId,
var_store: VarStore,
pool: &'a mut Pool,
pub home: ModuleId,
pub var_store: VarStore,
pub pool: &'a mut Pool,
pub arena: &'a Bump,
// module_ids: &'a ModuleIds,
ident_ids: IdentIds,
pub dep_idents: MutMap<ModuleId, IdentIds>,
pub module_ids: &'a ModuleIds,
pub ident_ids: IdentIds,
closures: MutMap<Symbol, References>,
pub closures: MutMap<Symbol, References>,
/// Symbols which were referenced by qualified lookups.
pub qualified_lookups: MutSet<Symbol>,
top_level_symbols: MutSet<Symbol>,
pub top_level_symbols: MutSet<Symbol>,
closure_name_symbol: Option<Symbol>,
pub closure_name_symbol: Option<Symbol>,
}
impl<'a> Env<'a> {
fn add<T>(&mut self, item: T, region: Region) -> NodeId<T> {
pub fn add<T>(&mut self, item: T, region: Region) -> NodeId<T> {
let id = self.pool.add(item);
self.set_region(id, region);
id
}
fn problem(&mut self, _problem: Problem) {
pub fn problem(&mut self, _problem: Problem) {
todo!();
}
fn set_region<T>(&mut self, _node_id: NodeId<T>, _region: Region) {
pub fn set_region<T>(&mut self, _node_id: NodeId<T>, _region: Region) {
todo!();
}
@ -62,16 +70,80 @@ impl<'a> Env<'a> {
Symbol::new(self.home, ident_id)
}
}
pub fn to_pattern2<'a>(
_env: &mut Env<'a>,
_scope: &mut Scope,
_pattern_type: roc_parse::pattern::PatternType,
_pattern: &roc_parse::ast::Pattern<'a>,
_region: Region,
) -> (Output, crate::ast::Pattern2) {
todo!()
/// Returns Err if the symbol resolved, but it was not exposed by the given module
pub fn qualified_lookup(
&mut self,
module_name: &str,
ident: &str,
region: Region,
) -> Result<Symbol, RuntimeError> {
debug_assert!(
!module_name.is_empty(),
"Called env.qualified_lookup with an unqualified ident: {:?}",
ident
);
let module_name: InlinableString = module_name.into();
match self.module_ids.get_id(&module_name) {
Some(&module_id) => {
let ident: InlinableString = ident.into();
// You can do qualified lookups on your own module, e.g.
// if I'm in the Foo module, I can do a `Foo.bar` lookup.
if module_id == self.home {
match self.ident_ids.get_id(&ident) {
Some(ident_id) => {
let symbol = Symbol::new(module_id, *ident_id);
self.qualified_lookups.insert(symbol);
Ok(symbol)
}
None => Err(RuntimeError::LookupNotInScope(
Located {
value: ident,
region,
},
self.ident_ids
.idents()
.map(|(_, string)| string.as_ref().into())
.collect(),
)),
}
} else {
match self
.dep_idents
.get(&module_id)
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
{
Some(ident_id) => {
let symbol = Symbol::new(module_id, *ident_id);
self.qualified_lookups.insert(symbol);
Ok(symbol)
}
None => Err(RuntimeError::ValueNotExposed {
module_name,
ident,
region,
}),
}
}
}
None => Err(RuntimeError::ModuleNotImported {
module_name,
imported_modules: self
.module_ids
.available_modules()
.map(|string| string.as_ref().into())
.collect(),
region,
}),
}
}
}
const ZERO: Region = Region::zero();

View file

@ -28,7 +28,9 @@ mod ortho;
mod pattern;
pub mod pool;
mod rect;
mod scope;
pub mod text;
mod types;
mod util;
mod vertex;

View file

@ -1,13 +1,13 @@
use crate::ast::{ExprId, FloatVal, IntVal};
use crate::expr::Env;
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
use arraystring::{typenum::U30, ArrayString};
use roc_can::def::Annotation;
use roc_can::expr::Recursive;
use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia;
use roc_can::expr::Output;
use roc_can::scope::Scope;
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_region::all::Region;
use roc_types::subs::Variable;
use roc_types::types::Type;
pub type PatternId = NodeId<Pattern2>;
#[derive(Debug)]
pub enum Pattern2 {
@ -66,6 +66,16 @@ pub enum MalformedPatternProblem {
QualifiedIdentifier,
}
pub fn to_pattern2<'a>(
_env: &mut Env<'a>,
_scope: &mut Scope,
_pattern_type: roc_parse::pattern::PatternType,
_pattern: &roc_parse::ast::Pattern<'a>,
_region: Region,
) -> (Output, Pattern2) {
todo!()
}
pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
use Pattern2::*;
let mut symbols = Vec::new();

View file

@ -244,6 +244,15 @@ impl PoolStr {
}
}
}
pub fn duplicate(&self) -> Self {
// Question: should this fully clone, or is a shallow copy
// (and the aliasing it entails) OK?
Self {
first_node_id: self.first_node_id,
len: self.len,
}
}
}
/// An array of at most 2^32 pool-allocated nodes.
@ -275,6 +284,14 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
}
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn new<I: ExactSizeIterator<Item = T>>(nodes: I, pool: &mut Pool) -> Self {
debug_assert!(nodes.len() <= u32::MAX as usize);
debug_assert!(size_of::<T>() <= NODE_BYTES);
@ -348,6 +365,15 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
}
}
pub fn duplicate(&self) -> Self {
// Question: should this fully clone, or is a shallow copy
// (and the aliasing it entails) OK?
Self {
first_node_id: self.first_node_id,
len: self.len,
}
}
pub fn free<S>(self, pool: &'a mut Pool) {
// zero out the memory
unsafe {
@ -525,3 +551,37 @@ impl<T> Iterator for PoolVecIterNodeIds<T> {
}
}
}
/// TODO
#[derive(Debug)]
pub struct PoolMap<K, V> {
first_node_id: NodeId<(K, V)>,
len: u32,
}
#[test]
fn pool_map_size() {
assert_eq!(size_of::<PoolMap<(), ()>>(), 8);
}
impl<K: Eq, V> PoolMap<K, V> {
pub fn empty(pool: &mut Pool) -> Self {
todo!()
}
pub fn with_capacity(pool: &mut Pool, size: u32) -> Self {
todo!()
}
pub fn insert(&mut self, pool: &mut Pool, k: K, v: V) -> Option<(K, V)> {
todo!()
}
pub fn remove(&mut self, pool: &mut Pool, k: &K) -> Option<(K, V)> {
todo!()
}
pub fn contains(&mut self, pool: &mut Pool, k: &K) -> bool {
todo!()
}
}

221
editor/src/scope.rs Normal file
View file

@ -0,0 +1,221 @@
use crate::pool::{Pool, PoolStr, PoolVec};
use crate::types::{Alias, TypeId};
use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::{Ident, Lowercase};
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_problem::can::RuntimeError;
use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
fn solved_type_to_type_id() -> TypeId {
todo!()
}
#[derive(Debug)]
pub struct Scope {
/// All the identifiers in scope, mapped to were they were defined and
/// the Symbol they resolve to.
idents: MutMap<Ident, (Symbol, Region)>,
/// A cache of all the symbols in scope. This makes lookups much
/// faster when checking for unused defs and unused arguments.
symbols: MutMap<Symbol, Region>,
/// The type aliases currently in scope
/// Uses BTreeMap because HashMap requires elements are Clone
aliases: MutMap<Symbol, Alias>,
/// The current module being processed. This will be used to turn
/// unqualified idents into Symbols.
home: ModuleId,
}
impl Scope {
pub fn new(home: ModuleId, pool: &mut Pool, var_store: &mut VarStore) -> Scope {
use roc_types::solved_types::{BuiltinAlias, FreeVars};
let solved_aliases = roc_types::builtin_aliases::aliases();
let mut aliases = MutMap::default();
for (symbol, builtin_alias) in solved_aliases {
let BuiltinAlias { region, vars, typ } = builtin_alias;
let mut free_vars = FreeVars::default();
let typ = solved_type_to_type_id();
// roc_types::solved_types::to_type(&typ, &mut free_vars, var_store);
// make sure to sort these variables to make them line up with the type arguments
let mut type_variables: Vec<_> = free_vars.unnamed_vars.into_iter().collect();
type_variables.sort();
debug_assert_eq!(vars.len(), type_variables.len());
let mut variables = PoolVec::with_capacity(vars.len() as u32, pool);
let it = variables
.iter_node_ids()
.zip(vars.iter())
.zip(type_variables);
for ((node_id, loc_name), (_, var)) in it {
// TODO region is ignored, but "fake" anyway. How to resolve?
let name = PoolStr::new(loc_name.value.as_str(), pool);
pool[node_id] = (name, var);
}
let alias = Alias {
actual: typ,
/// We know that builtin aliases have no hiddden variables (e.g. in closures)
hidden_variables: PoolVec::empty(pool),
targs: variables,
};
aliases.insert(symbol, alias);
}
let idents = Symbol::default_in_scope();
let idents: MutMap<_, _> = idents.into_iter().collect();
Scope {
home,
idents,
symbols: MutMap::default(),
aliases,
}
}
pub fn idents(&self) -> impl Iterator<Item = (&Ident, &(Symbol, Region))> {
self.idents.iter()
}
pub fn symbols(&self) -> impl Iterator<Item = (Symbol, Region)> + '_ {
self.symbols.iter().map(|(x, y)| (*x, *y))
}
pub fn contains_ident(&self, ident: &Ident) -> bool {
self.idents.contains_key(ident)
}
pub fn contains_symbol(&self, symbol: Symbol) -> bool {
self.symbols.contains_key(&symbol)
}
pub fn num_idents(&self) -> usize {
self.idents.len()
}
pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
match self.idents.get(ident) {
Some((symbol, _)) => Ok(*symbol),
None => Err(RuntimeError::LookupNotInScope(
Located {
region,
value: ident.clone().into(),
},
self.idents.keys().map(|v| v.as_ref().into()).collect(),
)),
}
}
pub fn lookup_alias(&self, symbol: Symbol) -> Option<&Alias> {
self.aliases.get(&symbol)
}
/// Introduce a new ident to scope.
///
/// Returns Err if this would shadow an existing ident, including the
/// Symbol and Region of the ident we already had in scope under that name.
pub fn introduce(
&mut self,
ident: Ident,
exposed_ident_ids: &IdentIds,
all_ident_ids: &mut IdentIds,
region: Region,
) -> Result<Symbol, (Region, Located<Ident>)> {
match self.idents.get(&ident) {
Some((_, original_region)) => {
let shadow = Located {
value: ident,
region,
};
Err((*original_region, shadow))
}
None => {
// If this IdentId was already added previously
// when the value was exposed in the module header,
// use that existing IdentId. Otherwise, create a fresh one.
let ident_id = match exposed_ident_ids.get_id(&ident.as_inline_str()) {
Some(ident_id) => *ident_id,
None => all_ident_ids.add(ident.clone().into()),
};
let symbol = Symbol::new(self.home, ident_id);
self.symbols.insert(symbol, region);
self.idents.insert(ident, (symbol, region));
Ok(symbol)
}
}
}
/// Ignore an identifier.
///
/// Used for record guards like { x: Just _ }
pub fn ignore(&mut self, ident: Ident, all_ident_ids: &mut IdentIds) -> Symbol {
let ident_id = all_ident_ids.add(ident.into());
Symbol::new(self.home, ident_id)
}
/// Import a Symbol from another module into this module's top-level scope.
///
/// Returns Err if this would shadow an existing ident, including the
/// Symbol and Region of the ident we already had in scope under that name.
pub fn import(
&mut self,
ident: Ident,
symbol: Symbol,
region: Region,
) -> Result<(), (Symbol, Region)> {
match self.idents.get(&ident) {
Some(shadowed) => Err(*shadowed),
None => {
self.symbols.insert(symbol, region);
self.idents.insert(ident, (symbol, region));
Ok(())
}
}
}
pub fn add_alias(
&mut self,
pool: &mut Pool,
name: Symbol,
vars: PoolVec<(PoolStr, Variable)>,
typ: TypeId,
) {
let mut hidden_variables = MutSet::default();
hidden_variables.extend(typ.variables(pool));
for loc_var in vars.iter(pool) {
hidden_variables.remove(&loc_var.1);
}
let hidden_variables_vec = PoolVec::with_capacity(hidden_variables.len() as u32, pool);
for (node_id, var) in hidden_variables_vec.iter_node_ids().zip(hidden_variables) {
pool[node_id] = var;
}
let alias = Alias {
targs: vars,
hidden_variables: hidden_variables_vec,
actual: typ,
};
self.aliases.insert(name, alias);
}
pub fn contains_alias(&mut self, name: Symbol) -> bool {
self.aliases.contains_key(&name)
}
}

314
editor/src/types.rs Normal file
View file

@ -0,0 +1,314 @@
use crate::expr::Env;
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
use crate::scope::Scope;
// use roc_can::expr::Output;
use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::Ident;
use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region};
use roc_types::subs::Variable;
use roc_types::types::{Problem, RecordField};
pub type TypeId = NodeId<Type2>;
#[derive(Debug)]
pub enum Type2 {
Variable(Variable),
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId), // 20B = 8B + 8B + 4B
HostExposedAlias {
name: Symbol, // 8B
arguments: PoolVec<(PoolStr, TypeId)>, // 8B
actual_var: Variable, // 4B
actual: TypeId, // 4B
},
EmptyTagUnion,
TagUnion(PoolVec<(PoolStr, PoolVec<Type2>)>, TypeId),
RecursiveTagUnion(Variable, PoolVec<(PoolStr, PoolVec<Type2>)>, TypeId),
EmptyRec,
Record(PoolVec<(PoolStr, RecordField<Type2>)>, TypeId),
Function(PoolVec<Type2>, TypeId, TypeId), // 16B = 8B + 4B + 4B
Apply(Symbol, PoolVec<Type2>), // 16B = 8B + 8B
Erroneous(roc_types::types::Problem),
}
impl Type2 {
fn substitute(_pool: &mut Pool, _subs: &MutMap<Variable, TypeId>, _type_id: TypeId) {
todo!()
}
pub fn variables(&self, _pool: &mut Pool) -> MutSet<Variable> {
todo!()
}
}
impl NodeId<Type2> {
pub fn variables(&self, _pool: &mut Pool) -> MutSet<Variable> {
todo!()
}
}
/// A temporary data structure to return a bunch of values to Def construction
pub enum Annotation2<'a> {
FunctionWithAliases {
annotation: Type2,
arguments: PoolVec<Type2>,
closure_type_id: TypeId,
return_type_id: TypeId,
named_rigids: MutMap<&'a str, Variable>,
unnamed_rigids: MutSet<Variable>,
},
Function {
arguments: PoolVec<Type2>,
closure_type_id: TypeId,
return_type_id: TypeId,
named_rigids: MutMap<&'a str, Variable>,
unnamed_rigids: MutSet<Variable>,
},
Value {
annotation: Type2,
named_rigids: MutMap<&'a str, Variable>,
unnamed_rigids: MutSet<Variable>,
},
Erroneous(roc_types::types::Problem),
}
pub fn to_annotation2<'a>(
env: &mut Env,
scope: &mut Scope,
annotation: &'a roc_parse::ast::TypeAnnotation<'a>,
_region: Region,
) -> Annotation2<'a> {
use roc_parse::ast::TypeAnnotation::*;
let mut rigids = Rigids::default();
let mut alias_stack = Vec::new();
// let mut as_alias_stack = Vec::new();
// we dealias until we hit a non-alias, then we either hit a function type (and produce a
// function annotation) or anything else (and produce a value annotation)
match annotation {
Function(argument_types, return_type) => {
//
let arguments = PoolVec::with_capacity(argument_types.len() as u32, env.pool);
for (type_id, loc_arg) in arguments.iter_node_ids().zip(argument_types.iter()) {
as_type_id(
env,
scope,
&mut rigids,
type_id,
&loc_arg.value,
loc_arg.region,
);
}
let return_type_id = to_type_id(
env,
scope,
&mut rigids,
&return_type.value,
return_type.region,
);
let closure_type = Type2::Variable(env.var_store.fresh());
let closure_type_id = env.pool.add(closure_type);
let Rigids { named, unnamed, .. } = rigids;
// if alias_stack.is_empty() && as_alias_stack.is_empty() {
if alias_stack.is_empty() {
Annotation2::Function {
arguments,
closure_type_id,
return_type_id,
named_rigids: named,
unnamed_rigids: unnamed,
}
} else {
let mut annotation =
Type2::Function(arguments.duplicate(), closure_type_id, return_type_id);
for (name, targs) in alias_stack.into_iter().rev().peekable() {
let type_id = env.pool.add(annotation);
annotation = Type2::Alias(name, targs, type_id);
}
Annotation2::FunctionWithAliases {
annotation,
arguments,
closure_type_id,
return_type_id,
named_rigids: named,
unnamed_rigids: unnamed,
}
}
}
Apply(module_name, ident, targs) => {
todo!()
}
}
}
#[derive(Default)]
struct Rigids<'a> {
named: MutMap<&'a str, Variable>,
unnamed: MutSet<Variable>,
hidden: MutSet<Variable>,
}
pub fn to_type_id(
env: &mut Env,
scope: &mut Scope,
rigids: &mut Rigids,
annotation: &roc_parse::ast::TypeAnnotation,
region: Region,
) -> TypeId {
let type2 = to_type2(env, scope, rigids, annotation);
env.add(type2, region)
}
pub fn as_type_id(
env: &mut Env,
scope: &mut Scope,
rigids: &mut Rigids,
type_id: TypeId,
annotation: &roc_parse::ast::TypeAnnotation,
region: Region,
) {
let type2 = to_type2(env, scope, rigids, annotation);
env.pool[type_id] = type2;
env.set_region(type_id, region);
}
pub fn to_type2(
env: &mut Env,
scope: &mut Scope,
rigids: &mut Rigids,
annotation: &roc_parse::ast::TypeAnnotation,
) -> Type2 {
todo!()
}
enum TypeApply {
Apply(Symbol, PoolVec<Type2>),
Alias(Symbol, PoolVec<(PoolStr, TypeId)>, TypeId),
Erroneous(roc_types::types::Problem),
}
fn to_type_apply(
env: &mut Env,
scope: &mut Scope,
rigids: &mut Rigids,
module_name: &str,
ident: &str,
type_arguments: &[Located<roc_parse::ast::TypeAnnotation>],
region: Region,
) -> TypeApply {
let symbol = if module_name.is_empty() {
// Since module_name was empty, this is an unqualified type.
// Look it up in scope!
let ident: Ident = (*ident).into();
match scope.lookup(&ident, region) {
Ok(symbol) => symbol,
Err(problem) => {
env.problem(roc_problem::can::Problem::RuntimeError(problem));
return TypeApply::Erroneous(Problem::UnrecognizedIdent(ident.into()));
}
}
} else {
match env.qualified_lookup(module_name, ident, region) {
Ok(symbol) => symbol,
Err(problem) => {
// Either the module wasn't imported, or
// it was imported but it doesn't expose this ident.
env.problem(roc_problem::can::Problem::RuntimeError(problem));
return TypeApply::Erroneous(Problem::UnrecognizedIdent((*ident).into()));
}
}
};
let argument_type_ids = PoolVec::with_capacity(type_arguments.len() as u32, env.pool);
for (type_id, loc_arg) in argument_type_ids.iter_node_ids().zip(type_arguments.iter()) {
as_type_id(env, scope, rigids, type_id, &loc_arg.value, loc_arg.region);
}
let args = type_arguments;
let opt_alias = scope.lookup_alias(symbol);
match opt_alias {
Some(ref alias) => {
// use a known alias
let actual = alias.actual;
let mut substitutions: MutMap<Variable, TypeId> = MutMap::default();
if alias.targs.len() != args.len() {
let error = TypeApply::Erroneous(Problem::BadTypeArguments {
symbol,
region,
alias_needs: alias.targs.len() as u8,
type_got: args.len() as u8,
});
return error;
}
let arguments = PoolVec::with_capacity(type_arguments.len() as u32, env.pool);
let it = arguments.iter_node_ids().zip(
argument_type_ids
.iter_node_ids()
.zip(alias.targs.iter_node_ids()),
);
for (node_id, (type_id, loc_var_id)) in it {
let loc_var = &env.pool[loc_var_id];
let name = loc_var.0.duplicate();
let var = loc_var.1;
env.pool[node_id] = (name, type_id);
substitutions.insert(var, type_id);
}
// make sure the recursion variable is freshly instantiated
// have to allocate these outside of the if for lifetime reasons...
let new = env.var_store.fresh();
let fresh = env.pool.add(Type2::Variable(new));
if let Type2::RecursiveTagUnion(rvar, ref tags, ext) = &mut env.pool[actual] {
substitutions.insert(*rvar, fresh);
env.pool[actual] = Type2::RecursiveTagUnion(new, tags.duplicate(), *ext);
}
// make sure hidden variables are freshly instantiated
for var_id in alias.hidden_variables.iter_node_ids() {
let var = env.pool[var_id];
let fresh = env.pool.add(Type2::Variable(env.var_store.fresh()));
substitutions.insert(var, fresh);
}
// instantiate variables
Type2::substitute(env.pool, &substitutions, actual);
TypeApply::Alias(symbol, arguments, actual)
}
None => TypeApply::Apply(symbol, argument_type_ids),
}
}
#[derive(Debug)]
pub struct Alias {
pub targs: PoolVec<(PoolStr, Variable)>,
pub actual: TypeId,
/// hidden type variables, like the closure variable in `a -> b`
pub hidden_variables: PoolVec<Variable>,
}