mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
snapshot
This commit is contained in:
parent
5b045f8a86
commit
2f0fe94182
7 changed files with 723 additions and 59 deletions
|
@ -1,13 +1,12 @@
|
||||||
use crate::pattern::{Pattern2, PatternId};
|
use crate::pattern::{Pattern2, PatternId};
|
||||||
use crate::pool::{NodeId, PoolStr, PoolVec};
|
use crate::pool::{NodeId, PoolStr, PoolVec};
|
||||||
|
use crate::types::{Type2, TypeId};
|
||||||
use arraystring::{typenum::U30, ArrayString};
|
use arraystring::{typenum::U30, ArrayString};
|
||||||
use roc_can::def::Annotation;
|
|
||||||
use roc_can::expr::Recursive;
|
use roc_can::expr::Recursive;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
use roc_module::operator::CalledVia;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Type;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
|
@ -197,39 +196,34 @@ pub enum Expr2 {
|
||||||
RuntimeError(/* TODO make a version of RuntimeError that fits in 15B */),
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct ValueDef {
|
pub struct ValueDef {
|
||||||
pub pattern: PatternId,
|
pattern: PatternId, // 4B
|
||||||
pub expr_type: Option<Type>,
|
expr_type: Option<(Type2, Rigids)>, // ?
|
||||||
pub expr_var: Variable,
|
expr_var: Variable, // 4B
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FunctionDef {
|
pub enum FunctionDef {
|
||||||
WithAnnotation {
|
WithAnnotation {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(Pattern2, Type, Variable)>, // 8B
|
arguments: PoolVec<(Pattern2, Type2)>, // 8B
|
||||||
return_type: Type, // ?
|
rigids: NodeId<Rigids>, // 4B
|
||||||
return_var: Variable, // 4B
|
return_type: TypeId, // 4B
|
||||||
},
|
},
|
||||||
NoAnnotation {
|
NoAnnotation {
|
||||||
name: Symbol,
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(Pattern2, Variable)>,
|
arguments: PoolVec<(Pattern2, Variable)>, // 8B
|
||||||
return_var: Variable,
|
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
|
/// This is overflow data from a Closure variant, which needs to store
|
||||||
/// more than 32B of total data
|
/// more than 32B of total data
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -247,17 +241,8 @@ pub struct WhenBranch {
|
||||||
pub guard: Option<NodeId<Expr2>>, // 4B
|
pub guard: Option<NodeId<Expr2>>, // 4B
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PatternId = NodeId<Pattern2>;
|
|
||||||
pub type ExprId = NodeId<Expr2>;
|
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]
|
#[test]
|
||||||
fn size_of_expr() {
|
fn size_of_expr() {
|
||||||
assert_eq!(std::mem::size_of::<Expr2>(), crate::pool::NODE_BYTES);
|
assert_eq!(std::mem::size_of::<Expr2>(), crate::pool::NODE_BYTES);
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
use crate::ast::{Expr2, ExprId, FloatVal, IntStyle, IntVal};
|
use crate::ast::{Expr2, ExprId, FloatVal, IntStyle, IntVal};
|
||||||
|
use crate::pattern::to_pattern2;
|
||||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
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::Output;
|
||||||
use roc_can::expr::Recursive;
|
use roc_can::expr::Recursive;
|
||||||
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
|
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};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
|
||||||
pub struct Env<'a> {
|
pub struct Env<'a> {
|
||||||
home: ModuleId,
|
pub home: ModuleId,
|
||||||
var_store: VarStore,
|
pub var_store: VarStore,
|
||||||
pool: &'a mut Pool,
|
pub pool: &'a mut Pool,
|
||||||
|
pub arena: &'a Bump,
|
||||||
|
|
||||||
// module_ids: &'a ModuleIds,
|
pub dep_idents: MutMap<ModuleId, IdentIds>,
|
||||||
ident_ids: 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> {
|
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);
|
let id = self.pool.add(item);
|
||||||
self.set_region(id, region);
|
self.set_region(id, region);
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn problem(&mut self, _problem: Problem) {
|
pub fn problem(&mut self, _problem: Problem) {
|
||||||
todo!();
|
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!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,16 +70,80 @@ impl<'a> Env<'a> {
|
||||||
|
|
||||||
Symbol::new(self.home, ident_id)
|
Symbol::new(self.home, ident_id)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_pattern2<'a>(
|
/// Returns Err if the symbol resolved, but it was not exposed by the given module
|
||||||
_env: &mut Env<'a>,
|
pub fn qualified_lookup(
|
||||||
_scope: &mut Scope,
|
&mut self,
|
||||||
_pattern_type: roc_parse::pattern::PatternType,
|
module_name: &str,
|
||||||
_pattern: &roc_parse::ast::Pattern<'a>,
|
ident: &str,
|
||||||
_region: Region,
|
region: Region,
|
||||||
) -> (Output, crate::ast::Pattern2) {
|
) -> Result<Symbol, RuntimeError> {
|
||||||
todo!()
|
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();
|
const ZERO: Region = Region::zero();
|
||||||
|
|
|
@ -28,7 +28,9 @@ mod ortho;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
mod rect;
|
mod rect;
|
||||||
|
mod scope;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
mod vertex;
|
mod vertex;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
use crate::ast::{ExprId, FloatVal, IntVal};
|
||||||
|
use crate::expr::Env;
|
||||||
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
use crate::pool::{NodeId, Pool, PoolStr, PoolVec};
|
||||||
use arraystring::{typenum::U30, ArrayString};
|
use roc_can::expr::Output;
|
||||||
use roc_can::def::Annotation;
|
use roc_can::scope::Scope;
|
||||||
use roc_can::expr::Recursive;
|
|
||||||
use roc_module::low_level::LowLevel;
|
|
||||||
use roc_module::operator::CalledVia;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::Region;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::Type;
|
|
||||||
|
pub type PatternId = NodeId<Pattern2>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Pattern2 {
|
pub enum Pattern2 {
|
||||||
|
@ -66,6 +66,16 @@ pub enum MalformedPatternProblem {
|
||||||
QualifiedIdentifier,
|
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> {
|
pub fn symbols_from_pattern(pool: &Pool, initial: &Pattern2) -> Vec<Symbol> {
|
||||||
use Pattern2::*;
|
use Pattern2::*;
|
||||||
let mut symbols = Vec::new();
|
let mut symbols = Vec::new();
|
||||||
|
|
|
@ -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.
|
/// 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 {
|
pub fn new<I: ExactSizeIterator<Item = T>>(nodes: I, pool: &mut Pool) -> Self {
|
||||||
debug_assert!(nodes.len() <= u32::MAX as usize);
|
debug_assert!(nodes.len() <= u32::MAX as usize);
|
||||||
debug_assert!(size_of::<T>() <= NODE_BYTES);
|
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) {
|
pub fn free<S>(self, pool: &'a mut Pool) {
|
||||||
// zero out the memory
|
// zero out the memory
|
||||||
unsafe {
|
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
221
editor/src/scope.rs
Normal 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
314
editor/src/types.rs
Normal 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>,
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue