it's alive!

This commit is contained in:
Folkert 2022-03-11 17:27:44 +01:00
parent 5cfd3c5ea8
commit aebb3a162e
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
5 changed files with 237 additions and 35 deletions

View file

@ -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(),
}
}
}

View file

@ -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.

View file

@ -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()

View file

@ -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));
}

View file

@ -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,