mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
it's alive!
This commit is contained in:
parent
5cfd3c5ea8
commit
aebb3a162e
5 changed files with 237 additions and 35 deletions
|
@ -309,7 +309,7 @@ impl Constraints {
|
|||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
Constraint::Let(let_index, Slice::default())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -335,7 +335,7 @@ impl Constraints {
|
|||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
Constraint::Let(let_index, Slice::default())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -368,7 +368,40 @@ impl Constraints {
|
|||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
Constraint::Let(let_index)
|
||||
Constraint::Let(let_index, Slice::default())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn let_import_constraint<I1, I2>(
|
||||
&mut self,
|
||||
rigid_vars: I1,
|
||||
def_types: I2,
|
||||
ret_constraint: Constraint,
|
||||
pool_variables: &[Variable],
|
||||
) -> Constraint
|
||||
where
|
||||
I1: IntoIterator<Item = Variable>,
|
||||
I2: IntoIterator<Item = (Symbol, Loc<Type>)>,
|
||||
I2::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||
|
||||
self.constraints.push(Constraint::True);
|
||||
self.constraints.push(ret_constraint);
|
||||
|
||||
let let_contraint = LetConstraint {
|
||||
rigid_vars: self.variable_slice(rigid_vars),
|
||||
flex_vars: Slice::default(),
|
||||
def_types: self.def_types_slice(def_types),
|
||||
defs_and_ret_constraint,
|
||||
};
|
||||
|
||||
let let_index = Index::new(self.let_constraints.len() as _);
|
||||
self.let_constraints.push(let_contraint);
|
||||
|
||||
let pool_slice = self.variable_slice(pool_variables.iter().copied());
|
||||
|
||||
Constraint::Let(let_index, pool_slice)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -416,7 +449,7 @@ impl Constraints {
|
|||
Constraint::Pattern(..) => false,
|
||||
Constraint::True => false,
|
||||
Constraint::SaveTheEnvironment => true,
|
||||
Constraint::Let(index) => {
|
||||
Constraint::Let(index, _) => {
|
||||
let let_constraint = &self.let_constraints[index.index()];
|
||||
|
||||
let offset = let_constraint.defs_and_ret_constraint.index();
|
||||
|
@ -455,7 +488,7 @@ impl Constraints {
|
|||
|
||||
static_assertions::assert_eq_size!([u8; 3 * 8], Constraint);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Constraint {
|
||||
Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
|
||||
Store(Index<Type>, Variable, Index<&'static str>, u32),
|
||||
|
@ -468,7 +501,7 @@ pub enum Constraint {
|
|||
),
|
||||
True, // Used for things that always unify, e.g. blanks and runtime errors
|
||||
SaveTheEnvironment,
|
||||
Let(Index<LetConstraint>),
|
||||
Let(Index<LetConstraint>, Slice<Variable>),
|
||||
And(Slice<Constraint>),
|
||||
/// Presence constraints
|
||||
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
|
||||
|
@ -503,3 +536,42 @@ pub struct IncludesTag {
|
|||
pub pattern_category: Index<PatternCategory>,
|
||||
pub region: Region,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Constraint {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Eq(arg0, arg1, arg2, arg3) => {
|
||||
write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
|
||||
}
|
||||
Self::Store(arg0, arg1, arg2, arg3) => {
|
||||
write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
|
||||
}
|
||||
Self::Lookup(arg0, arg1, arg2) => f
|
||||
.debug_tuple("Lookup")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.field(arg2)
|
||||
.finish(),
|
||||
Self::Pattern(arg0, arg1, arg2, arg3) => f
|
||||
.debug_tuple("Pattern")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.field(arg2)
|
||||
.field(arg3)
|
||||
.finish(),
|
||||
Self::True => write!(f, "True"),
|
||||
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
|
||||
Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(),
|
||||
Self::And(arg0) => f.debug_tuple("And").field(arg0).finish(),
|
||||
Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(),
|
||||
Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(),
|
||||
Self::PatternPresence(arg0, arg1, arg2, arg3) => f
|
||||
.debug_tuple("PatternPresence")
|
||||
.field(arg0)
|
||||
.field(arg1)
|
||||
.field(arg2)
|
||||
.field(arg3)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ pub struct Import {
|
|||
pub fn constrain_imported_values(
|
||||
constraints: &mut Constraints,
|
||||
imports: Vec<Import>,
|
||||
stored_imports: Vec<HackyImport>,
|
||||
body_con: Constraint,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Vec<Variable>, Constraint) {
|
||||
|
@ -96,17 +95,11 @@ pub fn constrain_imported_values(
|
|||
pub fn constrain_imports(
|
||||
constraints: &mut Constraints,
|
||||
imported_symbols: Vec<Import>,
|
||||
stored_imports: Vec<HackyImport>,
|
||||
constraint: Constraint,
|
||||
var_store: &mut VarStore,
|
||||
) -> Constraint {
|
||||
let (_introduced_rigids, constraint) = constrain_imported_values(
|
||||
constraints,
|
||||
imported_symbols,
|
||||
stored_imports,
|
||||
constraint,
|
||||
var_store,
|
||||
);
|
||||
let (_introduced_rigids, constraint) =
|
||||
constrain_imported_values(constraints, imported_symbols, constraint, var_store);
|
||||
|
||||
// TODO determine what to do with those rigids
|
||||
// for var in introduced_rigids {
|
||||
|
@ -116,6 +109,57 @@ pub fn constrain_imports(
|
|||
constraint
|
||||
}
|
||||
|
||||
pub fn constrain_imports2(
|
||||
imports: Vec<Import>,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Vec<Variable>, Vec<(Symbol, Loc<roc_types::types::Type>)>) {
|
||||
let mut def_types = Vec::new();
|
||||
let mut rigid_vars = Vec::new();
|
||||
|
||||
for import in imports {
|
||||
let mut free_vars = FreeVars::default();
|
||||
let loc_symbol = import.loc_symbol;
|
||||
|
||||
// an imported symbol can be either an alias or a value
|
||||
match import.solved_type {
|
||||
SolvedType::Alias(symbol, _, _, _, _) if symbol == loc_symbol.value => {
|
||||
// do nothing, in the future the alias definitions should not be in the list of imported values
|
||||
}
|
||||
_ => {
|
||||
let typ = roc_types::solved_types::to_type(
|
||||
&import.solved_type,
|
||||
&mut free_vars,
|
||||
var_store,
|
||||
);
|
||||
|
||||
def_types.push((
|
||||
loc_symbol.value,
|
||||
Loc {
|
||||
region: loc_symbol.region,
|
||||
value: typ,
|
||||
},
|
||||
));
|
||||
|
||||
for (_, var) in free_vars.named_vars {
|
||||
rigid_vars.push(var);
|
||||
}
|
||||
|
||||
for var in free_vars.wildcards {
|
||||
rigid_vars.push(var);
|
||||
}
|
||||
|
||||
// Variables can lose their name during type inference. But the unnamed
|
||||
// variables are still part of a signature, and thus must be treated as rigids here!
|
||||
for (_, var) in free_vars.unnamed_vars {
|
||||
rigid_vars.push(var);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(rigid_vars, def_types)
|
||||
}
|
||||
|
||||
pub struct ConstrainableImports {
|
||||
pub imported_symbols: Vec<Import>,
|
||||
pub hacky_symbols: Vec<HackyImport>,
|
||||
|
@ -123,11 +167,11 @@ pub struct ConstrainableImports {
|
|||
pub unused_imports: MutMap<ModuleId, Region>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HackyImport {
|
||||
storage_subs: StorageSubs,
|
||||
loc_symbol: Loc<Symbol>,
|
||||
variable: Variable,
|
||||
pub storage_subs: StorageSubs,
|
||||
pub loc_symbol: Loc<Symbol>,
|
||||
pub variable: Variable,
|
||||
}
|
||||
|
||||
/// Run this before constraining imports.
|
||||
|
|
|
@ -10,8 +10,8 @@ use roc_can::def::Declaration;
|
|||
use roc_can::module::{canonicalize_module_defs, Module};
|
||||
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
|
||||
use roc_constrain::module::{
|
||||
constrain_imports, constrain_module, pre_constrain_imports, ConstrainableImports,
|
||||
ExposedModuleTypes, HackyImport, Import, SubsByModule,
|
||||
constrain_imports, constrain_imports2, constrain_module, pre_constrain_imports,
|
||||
ConstrainableImports, ExposedModuleTypes, HackyImport, Import, SubsByModule,
|
||||
};
|
||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||
use roc_module::symbol::{
|
||||
|
@ -3098,15 +3098,20 @@ fn run_solve<'a>(
|
|||
// We have more constraining work to do now, so we'll add it to our timings.
|
||||
let constrain_start = SystemTime::now();
|
||||
|
||||
// Finish constraining the module by wrapping the existing Constraint
|
||||
// in the ones we just computed. We can do this off the main thread.
|
||||
let constraint = constrain_imports(
|
||||
&mut constraints,
|
||||
imported_symbols,
|
||||
imported_storage_subs,
|
||||
constraint,
|
||||
&mut var_store,
|
||||
);
|
||||
// only retain symbols that are not provided with a storage subs
|
||||
let mut imported_symbols = imported_symbols;
|
||||
|
||||
const NEW_TYPES: bool = true;
|
||||
|
||||
if NEW_TYPES {
|
||||
imported_symbols.retain(|k| {
|
||||
!imported_storage_subs
|
||||
.iter()
|
||||
.any(|i| k.loc_symbol.value == i.loc_symbol.value)
|
||||
});
|
||||
}
|
||||
|
||||
let (mut rigid_vars, mut def_types) = constrain_imports2(imported_symbols, &mut var_store);
|
||||
|
||||
let constrain_end = SystemTime::now();
|
||||
|
||||
|
@ -3123,8 +3128,43 @@ fn run_solve<'a>(
|
|||
// if false { debug_assert!(constraint.validate(), "{:?}", &constraint); }
|
||||
|
||||
let mut subs = Subs::new_from_varstore(var_store);
|
||||
|
||||
println!("new variables starting at {:?}", subs.len());
|
||||
|
||||
let mut import_variables = Vec::new();
|
||||
|
||||
if NEW_TYPES {
|
||||
for mut import in imported_storage_subs {
|
||||
// if format!("{:?}", import.loc_symbol.value).contains("after") {
|
||||
// dbg!(&import.storage_subs, import.variable, import.loc_symbol);
|
||||
// }
|
||||
let copied_import = import
|
||||
.storage_subs
|
||||
.export_variable_to(&mut subs, import.variable);
|
||||
|
||||
rigid_vars.extend(copied_import.rigid);
|
||||
// not a typo; rigids are turned into flex during type inference, but when imported we must
|
||||
// consider them rigid variables
|
||||
rigid_vars.extend(copied_import.flex);
|
||||
|
||||
import_variables.extend(copied_import.registered);
|
||||
|
||||
def_types.push((
|
||||
import.loc_symbol.value,
|
||||
Loc::at_zero(roc_types::types::Type::Variable(copied_import.variable)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let actual_constraint =
|
||||
constraints.let_import_constraint(rigid_vars, def_types, constraint, &import_variables);
|
||||
|
||||
dbg!(&subs);
|
||||
|
||||
dbg!(&import_variables);
|
||||
|
||||
let (solved_subs, solved_env, problems) =
|
||||
roc_solve::module::run_solve(&constraints, constraint, rigid_variables, subs);
|
||||
roc_solve::module::run_solve(&constraints, actual_constraint, rigid_variables, subs);
|
||||
|
||||
let exposed_vars_by_symbol: Vec<_> = solved_env
|
||||
.vars_by_symbol()
|
||||
|
|
|
@ -69,7 +69,7 @@ pub fn exposed_types_storage_subs(
|
|||
let mut stored_vars_by_symbol = Vec::with_capacity(exposed_vars_by_symbol.len());
|
||||
|
||||
for (symbol, var) in exposed_vars_by_symbol.iter() {
|
||||
let new_var = storage_subs.extend_with_variable(subs, *var);
|
||||
let new_var = storage_subs.import_variable_from(subs, *var).variable;
|
||||
stored_vars_by_symbol.push((*symbol, new_var));
|
||||
}
|
||||
|
||||
|
|
|
@ -189,6 +189,7 @@ pub fn run_in_place(
|
|||
constraint: &Constraint,
|
||||
) -> Env {
|
||||
let mut pools = Pools::default();
|
||||
|
||||
let state = State {
|
||||
env: env.clone(),
|
||||
mark: Mark::NONE.next(),
|
||||
|
@ -234,6 +235,9 @@ enum Work<'a> {
|
|||
env: &'a Env,
|
||||
rank: Rank,
|
||||
let_con: &'a LetConstraint,
|
||||
|
||||
/// Used for imports
|
||||
pool_variables: &'a [Variable],
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -294,6 +298,10 @@ fn solve(
|
|||
|
||||
let mut new_env = env.clone();
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
println!(
|
||||
"introducing {:?} at rank {:?} with no variables",
|
||||
symbol, rank,
|
||||
);
|
||||
new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
||||
}
|
||||
|
||||
|
@ -306,7 +314,12 @@ fn solve(
|
|||
|
||||
continue;
|
||||
}
|
||||
Work::LetConIntroducesVariables { env, rank, let_con } => {
|
||||
Work::LetConIntroducesVariables {
|
||||
env,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
} => {
|
||||
// NOTE be extremely careful with shadowing here
|
||||
let offset = let_con.defs_and_ret_constraint.index();
|
||||
let ret_constraint = &constraints.constraints[offset + 1];
|
||||
|
@ -320,6 +333,7 @@ fn solve(
|
|||
let visit_mark = young_mark.next();
|
||||
let final_mark = visit_mark.next();
|
||||
|
||||
dbg!(&pools);
|
||||
// Add a variable for each def to local_def_vars.
|
||||
let local_def_vars = LocalDefVarsVec::from_def_types(
|
||||
constraints,
|
||||
|
@ -330,6 +344,24 @@ fn solve(
|
|||
let_con.def_types,
|
||||
);
|
||||
|
||||
pools.get_mut(next_rank).extend(pool_variables);
|
||||
dbg!(&pools);
|
||||
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
let rigid = &constraints.variables[let_con.rigid_vars.indices()];
|
||||
let flex = &constraints.variables[let_con.flex_vars.indices()];
|
||||
|
||||
println!(
|
||||
"introducing {:?} at rank {:?} with variables {:?} {:?}",
|
||||
symbol, next_rank, rigid, flex
|
||||
);
|
||||
|
||||
// if rigid.len() > 6 && format!("{:?}", symbol).contains("after") {
|
||||
// dbg!(&subs, symbol, loc_var);
|
||||
// panic!();
|
||||
// }
|
||||
}
|
||||
|
||||
debug_assert_eq!(
|
||||
{
|
||||
let offenders = pools
|
||||
|
@ -510,6 +542,9 @@ fn solve(
|
|||
// then we copy from that module's Subs into our own. If the value
|
||||
// is being looked up in this module, then we use our Subs as both
|
||||
// the source and destination.
|
||||
|
||||
// dbg!(&subs, symbol, var, rank);
|
||||
|
||||
let actual = deep_copy_var_in(subs, rank, pools, var, arena);
|
||||
let expectation = &constraints.expectations[expectation_index.index()];
|
||||
|
||||
|
@ -619,7 +654,7 @@ fn solve(
|
|||
}
|
||||
}
|
||||
}
|
||||
Let(index) => {
|
||||
Let(index, pool_slice) => {
|
||||
let let_con = &constraints.let_constraints[index.index()];
|
||||
|
||||
let offset = let_con.defs_and_ret_constraint.index();
|
||||
|
@ -629,7 +664,11 @@ fn solve(
|
|||
let flex_vars = &constraints.variables[let_con.flex_vars.indices()];
|
||||
let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()];
|
||||
|
||||
let pool_variables = &constraints.variables[pool_slice.indices()];
|
||||
|
||||
if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() {
|
||||
debug_assert!(pool_variables.is_empty());
|
||||
|
||||
introduce(subs, rank, pools, flex_vars);
|
||||
|
||||
// If the return expression is guaranteed to solve,
|
||||
|
@ -642,6 +681,8 @@ fn solve(
|
|||
|
||||
state
|
||||
} else if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() {
|
||||
debug_assert!(pool_variables.is_empty());
|
||||
|
||||
// items are popped from the stack in reverse order. That means that we'll
|
||||
// first solve then defs_constraint, and then (eventually) the ret_constraint.
|
||||
//
|
||||
|
@ -689,7 +730,12 @@ fn solve(
|
|||
//
|
||||
// Note that the LetConSimple gets the current env and rank,
|
||||
// and not the env/rank from after solving the defs_constraint
|
||||
stack.push(Work::LetConIntroducesVariables { env, rank, let_con });
|
||||
stack.push(Work::LetConIntroducesVariables {
|
||||
env,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
});
|
||||
stack.push(Work::Constraint {
|
||||
env,
|
||||
rank: next_rank,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue