mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge pull request #5592 from roc-lang/simplify-solving-spike
Break up solving and put common values in a shared struct
This commit is contained in:
commit
addd513528
17 changed files with 2666 additions and 2610 deletions
|
@ -64,6 +64,26 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
|
|||
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
|
||||
|
||||
impl Constraints {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
constraints: Default::default(),
|
||||
type_slices: Default::default(),
|
||||
variables: Default::default(),
|
||||
loc_symbols: Default::default(),
|
||||
let_constraints: Default::default(),
|
||||
categories: Default::default(),
|
||||
pattern_categories: Default::default(),
|
||||
expectations: Default::default(),
|
||||
pattern_expectations: Default::default(),
|
||||
includes_tags: Default::default(),
|
||||
strings: Default::default(),
|
||||
sketched_rows: Default::default(),
|
||||
eq: Default::default(),
|
||||
pattern_eq: Default::default(),
|
||||
cycles: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let constraints = Vec::new();
|
||||
let type_slices = Vec::with_capacity(16);
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock};
|
|||
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::constraint::Constraints;
|
||||
use roc_can::module::ExposedByModule;
|
||||
use roc_collections::MutMap;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
|
@ -12,13 +13,14 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::ModuleId;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_solve::ability::AbilityResolver;
|
||||
use roc_solve::solve::Pools;
|
||||
use roc_solve::specialize::{compact_lambda_sets_of_vars, DerivedEnv, Phase};
|
||||
use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase};
|
||||
use roc_solve::Pools;
|
||||
use roc_solve::{DerivedEnv, Env};
|
||||
use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet};
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
||||
use roc_types::types::Polarity;
|
||||
use roc_unify::unify::MetaCollector;
|
||||
use roc_unify::unify::{Env, Mode, Unified};
|
||||
use roc_unify::unify::{Env as UEnv, Mode, Unified};
|
||||
|
||||
pub use roc_solve::ability::{ResolveError, Resolved};
|
||||
pub use roc_types::subs::instantiate_rigids;
|
||||
|
@ -340,6 +342,19 @@ impl MetaCollector for ChangedVariableCollector {
|
|||
}
|
||||
}
|
||||
|
||||
std::thread_local! {
|
||||
static SCRATCHPAD_FOR_OCCURS: std::cell::RefCell<Option<Constraints>> = std::cell::RefCell::new(Some(Constraints::empty()));
|
||||
}
|
||||
|
||||
fn with_empty_solve_constraints<T>(f: impl FnOnce(&Constraints) -> T) -> T {
|
||||
SCRATCHPAD_FOR_OCCURS.with(|cell| {
|
||||
let constr = cell.take().unwrap();
|
||||
let result = f(&constr);
|
||||
cell.replace(Some(constr));
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Unifies two variables and performs lambda set compaction.
|
||||
/// Ranks and other ability demands are disregarded.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -359,7 +374,7 @@ pub fn unify(
|
|||
"derived module can only unify its subs in its own context!"
|
||||
);
|
||||
let unified = roc_unify::unify::unify_with_collector::<ChangedVariableCollector>(
|
||||
&mut Env::new(subs),
|
||||
&mut UEnv::new(subs),
|
||||
left,
|
||||
right,
|
||||
Mode::EQ,
|
||||
|
@ -381,14 +396,17 @@ pub fn unify(
|
|||
exposed_types: exposed_by_module,
|
||||
};
|
||||
|
||||
let must_implement_constraints = compact_lambda_sets_of_vars(
|
||||
subs,
|
||||
&derived_env,
|
||||
arena,
|
||||
&mut pools,
|
||||
lambda_sets_to_specialize,
|
||||
&late_phase,
|
||||
);
|
||||
let must_implement_constraints = with_empty_solve_constraints(|c| {
|
||||
let mut env = Env {
|
||||
constraints: c,
|
||||
subs,
|
||||
derived_env: &derived_env,
|
||||
arena,
|
||||
pools: &mut pools,
|
||||
};
|
||||
|
||||
compact_lambda_sets_of_vars(&mut env, lambda_sets_to_specialize, &late_phase)
|
||||
});
|
||||
// At this point we can't do anything with must-implement constraints, since we're no
|
||||
// longer solving. We must assume that they were totally caught during solving.
|
||||
// After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally
|
||||
|
|
|
@ -56,7 +56,7 @@ use roc_region::all::{LineInfo, Loc, Region};
|
|||
#[cfg(not(target_family = "wasm"))]
|
||||
use roc_reporting::report::to_https_problem_report_string;
|
||||
use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget};
|
||||
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
|
||||
use roc_solve::module::{extract_module_owned_implementations, SolveConfig, Solved, SolvedModule};
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
|
@ -5061,6 +5061,14 @@ fn import_variable_for_symbol(
|
|||
}
|
||||
}
|
||||
|
||||
struct SolveResult {
|
||||
solved: Solved<Subs>,
|
||||
solved_implementations: ResolvedImplementations,
|
||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||
problems: Vec<TypeError>,
|
||||
abilities_store: AbilitiesStore,
|
||||
}
|
||||
|
||||
#[allow(clippy::complexity)]
|
||||
fn run_solve_solve(
|
||||
exposed_for_module: ExposedForModule,
|
||||
|
@ -5071,13 +5079,7 @@ fn run_solve_solve(
|
|||
var_store: VarStore,
|
||||
module: Module,
|
||||
derived_module: SharedDerivedModule,
|
||||
) -> (
|
||||
Solved<Subs>,
|
||||
ResolvedImplementations,
|
||||
Vec<(Symbol, Variable)>,
|
||||
Vec<TypeError>,
|
||||
AbilitiesStore,
|
||||
) {
|
||||
) -> SolveResult {
|
||||
let Module {
|
||||
exposed_symbols,
|
||||
aliases,
|
||||
|
@ -5111,30 +5113,34 @@ fn run_solve_solve(
|
|||
&import_variables,
|
||||
);
|
||||
|
||||
let mut solve_aliases = roc_solve::solve::Aliases::with_capacity(aliases.len());
|
||||
let mut solve_aliases = roc_solve::Aliases::with_capacity(aliases.len());
|
||||
for (name, (_, alias)) in aliases.iter() {
|
||||
solve_aliases.insert(&mut types, *name, alias.clone());
|
||||
}
|
||||
|
||||
let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = {
|
||||
let (solve_output, solved_implementations, exposed_vars_by_symbol) = {
|
||||
let module_id = module.module_id;
|
||||
|
||||
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
|
||||
module_id,
|
||||
let solve_config = SolveConfig {
|
||||
home: module_id,
|
||||
types,
|
||||
&constraints,
|
||||
actual_constraint,
|
||||
constraints: &constraints,
|
||||
root_constraint: actual_constraint,
|
||||
pending_derives,
|
||||
exposed_by_module: &exposed_for_module.exposed_by_module,
|
||||
derived_module,
|
||||
};
|
||||
|
||||
let solve_output = roc_solve::module::run_solve(
|
||||
solve_config,
|
||||
rigid_variables,
|
||||
subs,
|
||||
solve_aliases,
|
||||
abilities_store,
|
||||
pending_derives,
|
||||
&exposed_for_module.exposed_by_module,
|
||||
derived_module,
|
||||
);
|
||||
|
||||
let solved_implementations =
|
||||
extract_module_owned_implementations(module_id, &abilities_store);
|
||||
extract_module_owned_implementations(module_id, &solve_output.resolved_abilities_store);
|
||||
|
||||
let is_specialization_symbol = |sym| {
|
||||
solved_implementations
|
||||
|
@ -5147,7 +5153,8 @@ fn run_solve_solve(
|
|||
|
||||
// Expose anything that is explicitly exposed by the header, or is a specialization of an
|
||||
// ability.
|
||||
let exposed_vars_by_symbol: Vec<_> = solved_env
|
||||
let exposed_vars_by_symbol: Vec<_> = solve_output
|
||||
.scope
|
||||
.vars_by_symbol()
|
||||
.filter(|(k, _)| {
|
||||
exposed_symbols.contains(k)
|
||||
|
@ -5156,22 +5163,23 @@ fn run_solve_solve(
|
|||
})
|
||||
.collect();
|
||||
|
||||
(
|
||||
solved_subs,
|
||||
solved_implementations,
|
||||
exposed_vars_by_symbol,
|
||||
problems,
|
||||
abilities_store,
|
||||
)
|
||||
(solve_output, solved_implementations, exposed_vars_by_symbol)
|
||||
};
|
||||
|
||||
(
|
||||
solved_subs,
|
||||
let roc_solve::module::SolveOutput {
|
||||
subs,
|
||||
scope: _,
|
||||
errors,
|
||||
resolved_abilities_store,
|
||||
} = solve_output;
|
||||
|
||||
SolveResult {
|
||||
solved: subs,
|
||||
solved_implementations,
|
||||
exposed_vars_by_symbol,
|
||||
problems,
|
||||
abilities_store,
|
||||
)
|
||||
problems: errors,
|
||||
abilities_store: resolved_abilities_store,
|
||||
}
|
||||
}
|
||||
|
||||
fn run_solve<'a>(
|
||||
|
@ -5201,7 +5209,7 @@ fn run_solve<'a>(
|
|||
let loc_dbgs = std::mem::take(&mut module.loc_dbgs);
|
||||
let module = module;
|
||||
|
||||
let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = {
|
||||
let solve_result = {
|
||||
if module_id.is_builtin() {
|
||||
match cached_types.lock().remove(&module_id) {
|
||||
None => run_solve_solve(
|
||||
|
@ -5219,13 +5227,13 @@ fn run_solve<'a>(
|
|||
exposed_vars_by_symbol,
|
||||
abilities,
|
||||
solved_implementations,
|
||||
}) => (
|
||||
Solved(subs),
|
||||
}) => SolveResult {
|
||||
solved: Solved(subs),
|
||||
solved_implementations,
|
||||
exposed_vars_by_symbol,
|
||||
vec![],
|
||||
abilities,
|
||||
),
|
||||
problems: vec![],
|
||||
abilities_store: abilities,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
run_solve_solve(
|
||||
|
@ -5241,7 +5249,14 @@ fn run_solve<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let mut solved_subs = solved_subs;
|
||||
let SolveResult {
|
||||
solved: mut solved_subs,
|
||||
solved_implementations,
|
||||
exposed_vars_by_symbol,
|
||||
problems,
|
||||
abilities_store,
|
||||
} = solve_result;
|
||||
|
||||
let exposed_types = roc_solve::module::exposed_types_storage_subs(
|
||||
module_id,
|
||||
&mut solved_subs,
|
||||
|
|
|
@ -18,11 +18,11 @@ use roc_types::subs::{
|
|||
TupleElems, Variable,
|
||||
};
|
||||
use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory, Polarity, Types};
|
||||
use roc_unify::unify::{Env, MustImplementConstraints};
|
||||
use roc_unify::unify::{Env as UEnv, MustImplementConstraints};
|
||||
use roc_unify::unify::{MustImplementAbility, Obligated};
|
||||
|
||||
use crate::solve::type_to_var;
|
||||
use crate::solve::{Aliases, Pools};
|
||||
use crate::env::Env;
|
||||
use crate::{aliases::Aliases, to_var::type_to_var};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AbilityImplError {
|
||||
|
@ -56,7 +56,7 @@ pub struct PendingDerivesTable(
|
|||
|
||||
impl PendingDerivesTable {
|
||||
pub fn new(
|
||||
subs: &mut Subs,
|
||||
env: &mut Env,
|
||||
types: &mut Types,
|
||||
aliases: &mut Aliases,
|
||||
pending_derives: PendingDerives,
|
||||
|
@ -81,17 +81,16 @@ impl PendingDerivesTable {
|
|||
// Neither rank nor pools should matter here.
|
||||
let typ = types.from_old_type(&typ);
|
||||
let opaque_var = type_to_var(
|
||||
subs,
|
||||
env,
|
||||
Rank::toplevel(),
|
||||
problems,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
&mut Pools::default(),
|
||||
types,
|
||||
aliases,
|
||||
typ,
|
||||
);
|
||||
let real_var = match subs.get_content_without_compacting(opaque_var) {
|
||||
let real_var = match env.subs.get_content_without_compacting(opaque_var) {
|
||||
Content::Alias(_, _, real_var, AliasKind::Opaque) => real_var,
|
||||
_ => internal_error!("Non-opaque in derives table"),
|
||||
};
|
||||
|
@ -1284,7 +1283,7 @@ impl DerivableVisitor for DeriveEq {
|
|||
|
||||
// Of the floating-point types,
|
||||
// only Dec implements Eq.
|
||||
let mut env = Env::new(subs);
|
||||
let mut env = UEnv::new(subs);
|
||||
let unified = unify(
|
||||
&mut env,
|
||||
content_var,
|
||||
|
@ -1419,7 +1418,7 @@ pub fn resolve_ability_specialization<R: AbilityResolver>(
|
|||
|
||||
instantiate_rigids(subs, signature_var);
|
||||
let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) = unify(
|
||||
&mut Env::new(subs),
|
||||
&mut UEnv::new(subs),
|
||||
specialization_var,
|
||||
signature_var,
|
||||
Mode::EQ,
|
||||
|
|
329
crates/compiler/solve/src/aliases.rs
Normal file
329
crates/compiler/solve/src/aliases.rs
Normal file
|
@ -0,0 +1,329 @@
|
|||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_collections::{soa::Index, MutMap};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::{
|
||||
subs::{AliasVariables, Content, FlatType, Rank, Subs, SubsSlice, TagExt, UnionTags, Variable},
|
||||
types::{Alias, AliasKind, OptAbleVar, Type, TypeTag, Types},
|
||||
};
|
||||
|
||||
use crate::to_var::type_to_var_help;
|
||||
use crate::{ability::ObligationCache, env::Env};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct DelayedAliasVariables {
|
||||
start: u32,
|
||||
type_variables_len: u8,
|
||||
lambda_set_variables_len: u8,
|
||||
recursion_variables_len: u8,
|
||||
infer_ext_in_output_variables_len: u8,
|
||||
}
|
||||
|
||||
impl DelayedAliasVariables {
|
||||
fn recursion_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||
let start = self.start as usize
|
||||
+ (self.type_variables_len + self.lambda_set_variables_len) as usize;
|
||||
let length = self.recursion_variables_len as usize;
|
||||
|
||||
&mut variables[start..][..length]
|
||||
}
|
||||
|
||||
fn lambda_set_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||
let start = self.start as usize + self.type_variables_len as usize;
|
||||
let length = self.lambda_set_variables_len as usize;
|
||||
|
||||
&mut variables[start..][..length]
|
||||
}
|
||||
|
||||
fn type_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||
let start = self.start as usize;
|
||||
let length = self.type_variables_len as usize;
|
||||
|
||||
&mut variables[start..][..length]
|
||||
}
|
||||
|
||||
fn infer_ext_in_output_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] {
|
||||
let start = self.start as usize
|
||||
+ (self.type_variables_len
|
||||
+ self.lambda_set_variables_len
|
||||
+ self.recursion_variables_len) as usize;
|
||||
let length = self.infer_ext_in_output_variables_len as usize;
|
||||
|
||||
&mut variables[start..][..length]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Aliases {
|
||||
aliases: Vec<(Symbol, Index<TypeTag>, DelayedAliasVariables, AliasKind)>,
|
||||
variables: Vec<OptAbleVar>,
|
||||
}
|
||||
|
||||
impl Aliases {
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self {
|
||||
aliases: Vec::with_capacity(cap),
|
||||
variables: Vec::with_capacity(cap * 2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, types: &mut Types, symbol: Symbol, alias: Alias) {
|
||||
let alias_variables =
|
||||
{
|
||||
let start = self.variables.len() as _;
|
||||
|
||||
self.variables.extend(
|
||||
alias
|
||||
.type_variables
|
||||
.iter()
|
||||
.map(|x| OptAbleVar::from(&x.value)),
|
||||
);
|
||||
|
||||
self.variables.extend(alias.lambda_set_variables.iter().map(
|
||||
|x| match x.as_inner() {
|
||||
Type::Variable(v) => OptAbleVar::unbound(*v),
|
||||
_ => unreachable!("lambda set type is not a variable"),
|
||||
},
|
||||
));
|
||||
|
||||
let recursion_variables_len = alias.recursion_variables.len() as _;
|
||||
self.variables.extend(
|
||||
alias
|
||||
.recursion_variables
|
||||
.iter()
|
||||
.copied()
|
||||
.map(OptAbleVar::unbound),
|
||||
);
|
||||
|
||||
self.variables.extend(
|
||||
alias
|
||||
.infer_ext_in_output_variables
|
||||
.iter()
|
||||
.map(|v| OptAbleVar::unbound(*v)),
|
||||
);
|
||||
|
||||
DelayedAliasVariables {
|
||||
start,
|
||||
type_variables_len: alias.type_variables.len() as _,
|
||||
lambda_set_variables_len: alias.lambda_set_variables.len() as _,
|
||||
recursion_variables_len,
|
||||
infer_ext_in_output_variables_len: alias.infer_ext_in_output_variables.len()
|
||||
as _,
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: can we construct Aliases from TypeTag directly?
|
||||
let alias_typ = types.from_old_type(&alias.typ);
|
||||
|
||||
self.aliases
|
||||
.push((symbol, alias_typ, alias_variables, alias.kind));
|
||||
}
|
||||
|
||||
fn instantiate_result_result(
|
||||
env: &mut Env,
|
||||
rank: Rank,
|
||||
alias_variables: AliasVariables,
|
||||
) -> Variable {
|
||||
let tag_names_slice = Subs::RESULT_TAG_NAMES;
|
||||
|
||||
let err_slice = SubsSlice::new(alias_variables.variables_start + 1, 1);
|
||||
let ok_slice = SubsSlice::new(alias_variables.variables_start, 1);
|
||||
|
||||
let variable_slices =
|
||||
SubsSlice::extend_new(&mut env.subs.variable_slices, [err_slice, ok_slice]);
|
||||
|
||||
let union_tags = UnionTags::from_slices(tag_names_slice, variable_slices);
|
||||
let ext_var = TagExt::Any(Variable::EMPTY_TAG_UNION);
|
||||
let flat_type = FlatType::TagUnion(union_tags, ext_var);
|
||||
let content = Content::Structure(flat_type);
|
||||
|
||||
env.register(rank, content)
|
||||
}
|
||||
|
||||
/// Build an alias of the form `Num range := range`
|
||||
fn build_num_opaque(
|
||||
env: &mut Env,
|
||||
rank: Rank,
|
||||
symbol: Symbol,
|
||||
range_var: Variable,
|
||||
) -> Variable {
|
||||
let content = Content::Alias(
|
||||
symbol,
|
||||
AliasVariables::insert_into_subs(env.subs, [range_var], [], []),
|
||||
range_var,
|
||||
AliasKind::Opaque,
|
||||
);
|
||||
|
||||
env.register(rank, content)
|
||||
}
|
||||
|
||||
fn instantiate_builtin_aliases_real_var(
|
||||
&mut self,
|
||||
env: &mut Env,
|
||||
rank: Rank,
|
||||
symbol: Symbol,
|
||||
alias_variables: AliasVariables,
|
||||
) -> Option<(Variable, AliasKind)> {
|
||||
match symbol {
|
||||
Symbol::RESULT_RESULT => {
|
||||
let var = Self::instantiate_result_result(env, rank, alias_variables);
|
||||
|
||||
Some((var, AliasKind::Structural))
|
||||
}
|
||||
Symbol::NUM_NUM | Symbol::NUM_INTEGER | Symbol::NUM_FLOATINGPOINT => {
|
||||
// Num range := range | Integer range := range | FloatingPoint range := range
|
||||
let range_var = env.subs.variables[alias_variables.variables_start as usize];
|
||||
Some((range_var, AliasKind::Opaque))
|
||||
}
|
||||
Symbol::NUM_INT => {
|
||||
// Int range : Num (Integer range)
|
||||
//
|
||||
// build `Integer range := range`
|
||||
let integer_content_var = Self::build_num_opaque(
|
||||
env,
|
||||
rank,
|
||||
Symbol::NUM_INTEGER,
|
||||
env.subs.variables[alias_variables.variables_start as usize],
|
||||
);
|
||||
|
||||
// build `Num (Integer range) := Integer range`
|
||||
let num_content_var =
|
||||
Self::build_num_opaque(env, rank, Symbol::NUM_NUM, integer_content_var);
|
||||
|
||||
Some((num_content_var, AliasKind::Structural))
|
||||
}
|
||||
Symbol::NUM_FRAC => {
|
||||
// Frac range : Num (FloatingPoint range)
|
||||
//
|
||||
// build `FloatingPoint range := range`
|
||||
let fpoint_content_var = Self::build_num_opaque(
|
||||
env,
|
||||
rank,
|
||||
Symbol::NUM_FLOATINGPOINT,
|
||||
env.subs.variables[alias_variables.variables_start as usize],
|
||||
);
|
||||
|
||||
// build `Num (FloatingPoint range) := FloatingPoint range`
|
||||
let num_content_var =
|
||||
Self::build_num_opaque(env, rank, Symbol::NUM_NUM, fpoint_content_var);
|
||||
|
||||
Some((num_content_var, AliasKind::Structural))
|
||||
}
|
||||
Symbol::NUM_SIGNED8 => Some((Variable::SIGNED8, AliasKind::Opaque)),
|
||||
Symbol::NUM_SIGNED16 => Some((Variable::SIGNED16, AliasKind::Opaque)),
|
||||
Symbol::NUM_SIGNED32 => Some((Variable::SIGNED32, AliasKind::Opaque)),
|
||||
Symbol::NUM_SIGNED64 => Some((Variable::SIGNED64, AliasKind::Opaque)),
|
||||
Symbol::NUM_SIGNED128 => Some((Variable::SIGNED128, AliasKind::Opaque)),
|
||||
Symbol::NUM_UNSIGNED8 => Some((Variable::UNSIGNED8, AliasKind::Opaque)),
|
||||
Symbol::NUM_UNSIGNED16 => Some((Variable::UNSIGNED16, AliasKind::Opaque)),
|
||||
Symbol::NUM_UNSIGNED32 => Some((Variable::UNSIGNED32, AliasKind::Opaque)),
|
||||
Symbol::NUM_UNSIGNED64 => Some((Variable::UNSIGNED64, AliasKind::Opaque)),
|
||||
Symbol::NUM_UNSIGNED128 => Some((Variable::UNSIGNED128, AliasKind::Opaque)),
|
||||
Symbol::NUM_BINARY32 => Some((Variable::BINARY32, AliasKind::Opaque)),
|
||||
Symbol::NUM_BINARY64 => Some((Variable::BINARY64, AliasKind::Opaque)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_real_var(
|
||||
&mut self,
|
||||
env: &mut Env,
|
||||
rank: Rank,
|
||||
problems: &mut Vec<TypeError>,
|
||||
abilities_store: &AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
arena: &bumpalo::Bump,
|
||||
types: &mut Types,
|
||||
symbol: Symbol,
|
||||
alias_variables: AliasVariables,
|
||||
) -> (Variable, AliasKind) {
|
||||
// hardcoded instantiations for builtin aliases
|
||||
if let Some((var, kind)) =
|
||||
self.instantiate_builtin_aliases_real_var(env, rank, symbol, alias_variables)
|
||||
{
|
||||
return (var, kind);
|
||||
}
|
||||
|
||||
let (typ, delayed_variables, kind) =
|
||||
match self.aliases.iter().find(|(s, _, _, _)| *s == symbol) {
|
||||
None => internal_error!(
|
||||
"Alias {:?} not registered in delayed aliases! {:?}",
|
||||
symbol,
|
||||
&self.aliases
|
||||
),
|
||||
Some(&(_, typ, delayed_variables, kind)) => (typ, delayed_variables, kind),
|
||||
};
|
||||
|
||||
let mut substitutions: MutMap<_, _> = Default::default();
|
||||
|
||||
let old_type_variables = delayed_variables.type_variables(&mut self.variables);
|
||||
let new_type_variables = &env.subs.variables[alias_variables.type_variables().indices()];
|
||||
|
||||
for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) {
|
||||
// if constraint gen duplicated a type these variables could be the same
|
||||
// (happens very often in practice)
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
}
|
||||
}
|
||||
|
||||
for OptAbleVar {
|
||||
var: rec_var,
|
||||
opt_abilities,
|
||||
} in delayed_variables
|
||||
.recursion_variables(&mut self.variables)
|
||||
.iter_mut()
|
||||
{
|
||||
debug_assert!(opt_abilities.is_none());
|
||||
let new_var = env.subs.fresh_unnamed_flex_var();
|
||||
substitutions.insert(*rec_var, new_var);
|
||||
}
|
||||
|
||||
let old_lambda_set_variables = delayed_variables.lambda_set_variables(&mut self.variables);
|
||||
let new_lambda_set_variables =
|
||||
&env.subs.variables[alias_variables.lambda_set_variables().indices()];
|
||||
|
||||
for (old, new) in old_lambda_set_variables
|
||||
.iter_mut()
|
||||
.zip(new_lambda_set_variables)
|
||||
{
|
||||
debug_assert!(old.opt_abilities.is_none());
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
}
|
||||
}
|
||||
|
||||
let old_infer_ext_vars =
|
||||
delayed_variables.infer_ext_in_output_variables(&mut self.variables);
|
||||
let new_infer_ext_vars =
|
||||
&env.subs.variables[alias_variables.infer_ext_in_output_variables().indices()];
|
||||
|
||||
for (old, new) in old_infer_ext_vars.iter_mut().zip(new_infer_ext_vars) {
|
||||
debug_assert!(old.opt_abilities.is_none());
|
||||
if old.var != *new {
|
||||
substitutions.insert(old.var, *new);
|
||||
}
|
||||
}
|
||||
|
||||
let typ = if !substitutions.is_empty() {
|
||||
types.clone_with_variable_substitutions(typ, &substitutions)
|
||||
} else {
|
||||
typ
|
||||
};
|
||||
|
||||
let alias_variable = type_to_var_help(
|
||||
env,
|
||||
rank,
|
||||
problems,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
arena,
|
||||
self,
|
||||
types,
|
||||
typ,
|
||||
false,
|
||||
);
|
||||
(alias_variable, kind)
|
||||
}
|
||||
}
|
370
crates/compiler/solve/src/deep_copy.rs
Normal file
370
crates/compiler/solve/src/deep_copy.rs
Normal file
|
@ -0,0 +1,370 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_types::{
|
||||
subs::{
|
||||
self, AliasVariables, Content, Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, Rank,
|
||||
RecordFields, Subs, SubsSlice, TagExt, TupleElems, UnionLabels, Variable,
|
||||
},
|
||||
types::{RecordField, Uls},
|
||||
};
|
||||
|
||||
use crate::env::Env;
|
||||
|
||||
// TODO: eventually, we could possibly use the arena in Env instead.
|
||||
pub(crate) fn deep_copy_var_in(env: &mut Env, rank: Rank, var: Variable, arena: &Bump) -> Variable {
|
||||
let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena);
|
||||
|
||||
let pool = env.pools.get_mut(rank);
|
||||
|
||||
let var = env.subs.get_root_key(var);
|
||||
match deep_copy_var_decision(env.subs, rank, var) {
|
||||
ControlFlow::Break(copy) => copy,
|
||||
ControlFlow::Continue(copy) => {
|
||||
deep_copy_var_help(env.subs, rank, pool, &mut visited, var, copy);
|
||||
|
||||
// we have tracked all visited variables, and can now traverse them
|
||||
// in one go (without looking at the UnificationTable) and clear the copy field
|
||||
for var in visited {
|
||||
env.subs.set_copy_unchecked(var, OptVariable::NONE);
|
||||
}
|
||||
|
||||
copy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_trivial_copy(subs: &Subs, root_var: Variable) -> Option<Variable> {
|
||||
let existing_copy = subs.get_copy_unchecked(root_var);
|
||||
|
||||
if let Some(copy) = existing_copy.into_variable() {
|
||||
Some(copy)
|
||||
} else if subs.get_rank_unchecked(root_var) != Rank::GENERALIZED {
|
||||
Some(root_var)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deep_copy_var_decision(
|
||||
subs: &mut Subs,
|
||||
max_rank: Rank,
|
||||
var: Variable,
|
||||
) -> ControlFlow<Variable, Variable> {
|
||||
let var = subs.get_root_key(var);
|
||||
if let Some(copy) = has_trivial_copy(subs, var) {
|
||||
ControlFlow::Break(copy)
|
||||
} else {
|
||||
let copy_descriptor = Descriptor {
|
||||
content: Content::Structure(FlatType::EmptyTagUnion),
|
||||
rank: max_rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
|
||||
let copy = subs.fresh(copy_descriptor);
|
||||
|
||||
// Link the original variable to the new variable. This lets us
|
||||
// avoid making multiple copies of the variable we are instantiating.
|
||||
//
|
||||
// Need to do this before recursively copying to avoid looping.
|
||||
subs.set_mark_unchecked(var, Mark::NONE);
|
||||
subs.set_copy_unchecked(var, copy.into());
|
||||
|
||||
ControlFlow::Continue(copy)
|
||||
}
|
||||
}
|
||||
|
||||
fn deep_copy_var_help(
|
||||
subs: &mut Subs,
|
||||
max_rank: Rank,
|
||||
pool: &mut Vec<Variable>,
|
||||
visited: &mut bumpalo::collections::Vec<'_, Variable>,
|
||||
initial_source: Variable,
|
||||
initial_copy: Variable,
|
||||
) -> Variable {
|
||||
use roc_types::subs::Content::*;
|
||||
use roc_types::subs::FlatType::*;
|
||||
|
||||
struct DeepCopyVarWork {
|
||||
source: Variable,
|
||||
copy: Variable,
|
||||
}
|
||||
|
||||
let initial = DeepCopyVarWork {
|
||||
source: initial_source,
|
||||
copy: initial_copy,
|
||||
};
|
||||
let mut stack = vec![initial];
|
||||
|
||||
macro_rules! work {
|
||||
($variable:expr) => {{
|
||||
let var = subs.get_root_key($variable);
|
||||
match deep_copy_var_decision(subs, max_rank, var) {
|
||||
ControlFlow::Break(copy) => copy,
|
||||
ControlFlow::Continue(copy) => {
|
||||
stack.push(DeepCopyVarWork { source: var, copy });
|
||||
|
||||
copy
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! copy_sequence {
|
||||
($length:expr, $variables:expr) => {{
|
||||
let new_variables = SubsSlice::reserve_into_subs(subs, $length as _);
|
||||
for (target_index, var_index) in (new_variables.indices()).zip($variables) {
|
||||
let var = subs[var_index];
|
||||
let copy_var = work!(var);
|
||||
subs.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
new_variables
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! copy_union {
|
||||
($tags:expr) => {{
|
||||
let new_variable_slices = SubsSlice::reserve_variable_slices(subs, $tags.len());
|
||||
|
||||
let it = (new_variable_slices.indices()).zip($tags.variables());
|
||||
for (target_index, index) in it {
|
||||
let slice = subs[index];
|
||||
|
||||
let new_variables = copy_sequence!(slice.len(), slice);
|
||||
subs.variable_slices[target_index] = new_variables;
|
||||
}
|
||||
|
||||
UnionLabels::from_slices($tags.labels(), new_variable_slices)
|
||||
}};
|
||||
}
|
||||
|
||||
// When generalizing annotations with `Openness` extensions
|
||||
// we want to promote them to `Any`, so that usages at
|
||||
// specialized sites can grow unboundedly and are not bound to
|
||||
// openness-polymorphism.
|
||||
macro_rules! copy_tag_ext {
|
||||
($ext:expr) => {
|
||||
TagExt::Any(work!($ext.var()))
|
||||
};
|
||||
}
|
||||
|
||||
while let Some(DeepCopyVarWork { source: var, copy }) = stack.pop() {
|
||||
visited.push(var);
|
||||
pool.push(copy);
|
||||
|
||||
let content = *subs.get_content_unchecked(var);
|
||||
|
||||
// Now we recursively copy the content of the variable.
|
||||
// We have already marked the variable as copied, so we
|
||||
// will not repeat this work or crawl this variable again.
|
||||
match content {
|
||||
Structure(flat_type) => {
|
||||
let new_flat_type = match flat_type {
|
||||
Apply(symbol, arguments) => {
|
||||
let new_arguments = copy_sequence!(arguments.len(), arguments);
|
||||
|
||||
Apply(symbol, new_arguments)
|
||||
}
|
||||
|
||||
Func(arguments, closure_var, ret_var) => {
|
||||
let new_ret_var = work!(ret_var);
|
||||
let new_closure_var = work!(closure_var);
|
||||
|
||||
let new_arguments = copy_sequence!(arguments.len(), arguments);
|
||||
|
||||
Func(new_arguments, new_closure_var, new_ret_var)
|
||||
}
|
||||
|
||||
same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same,
|
||||
|
||||
Record(fields, ext_var) => {
|
||||
let record_fields = {
|
||||
let new_variables =
|
||||
copy_sequence!(fields.len(), fields.iter_variables());
|
||||
|
||||
// When copying a let-generalized record to a specialized region, rigid
|
||||
// optionals just become optionals.
|
||||
let field_types = subs.get_subs_slice(fields.record_fields());
|
||||
let has_rigid_optional_field = field_types
|
||||
.iter()
|
||||
.any(|f| matches!(f, RecordField::RigidOptional(..)));
|
||||
|
||||
let new_field_types_start = if has_rigid_optional_field {
|
||||
let field_types = field_types.to_vec();
|
||||
let slice = SubsSlice::extend_new(
|
||||
&mut subs.record_fields,
|
||||
field_types.into_iter().map(|f| match f {
|
||||
RecordField::RigidOptional(())
|
||||
| RecordField::RigidRequired(()) => internal_error!("Rigid optional/required should be generalized to non-rigid by this point"),
|
||||
|
||||
RecordField::Demanded(_)
|
||||
| RecordField::Required(_)
|
||||
| RecordField::Optional(_) => f,
|
||||
}),
|
||||
);
|
||||
slice.start
|
||||
} else {
|
||||
fields.field_types_start
|
||||
};
|
||||
|
||||
RecordFields {
|
||||
length: fields.length,
|
||||
field_names_start: fields.field_names_start,
|
||||
variables_start: new_variables.start,
|
||||
field_types_start: new_field_types_start,
|
||||
}
|
||||
};
|
||||
|
||||
Record(record_fields, work!(ext_var))
|
||||
}
|
||||
|
||||
Tuple(elems, ext_var) => {
|
||||
let tuple_elems = {
|
||||
let new_variables = copy_sequence!(elems.len(), elems.iter_variables());
|
||||
|
||||
TupleElems {
|
||||
length: elems.length,
|
||||
variables_start: new_variables.start,
|
||||
elem_index_start: elems.elem_index_start,
|
||||
}
|
||||
};
|
||||
|
||||
Tuple(tuple_elems, work!(ext_var))
|
||||
}
|
||||
|
||||
TagUnion(tags, ext_var) => {
|
||||
let union_tags = copy_union!(tags);
|
||||
|
||||
TagUnion(union_tags, copy_tag_ext!(ext_var))
|
||||
}
|
||||
|
||||
FunctionOrTagUnion(tag_name, symbol, ext_var) => {
|
||||
FunctionOrTagUnion(tag_name, symbol, copy_tag_ext!(ext_var))
|
||||
}
|
||||
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
let union_tags = copy_union!(tags);
|
||||
|
||||
RecursiveTagUnion(work!(rec_var), union_tags, copy_tag_ext!(ext_var))
|
||||
}
|
||||
};
|
||||
|
||||
subs.set_content_unchecked(copy, Structure(new_flat_type));
|
||||
}
|
||||
|
||||
FlexVar(_) | FlexAbleVar(_, _) | Error => {
|
||||
subs.set_content_unchecked(copy, content);
|
||||
}
|
||||
|
||||
RecursionVar {
|
||||
opt_name,
|
||||
structure,
|
||||
} => {
|
||||
let content = RecursionVar {
|
||||
opt_name,
|
||||
structure: work!(structure),
|
||||
};
|
||||
|
||||
subs.set_content_unchecked(copy, content);
|
||||
}
|
||||
|
||||
RigidVar(name) => {
|
||||
subs.set_content_unchecked(copy, FlexVar(Some(name)));
|
||||
}
|
||||
|
||||
RigidAbleVar(name, ability) => {
|
||||
subs.set_content_unchecked(copy, FlexAbleVar(Some(name), ability));
|
||||
}
|
||||
|
||||
Alias(symbol, arguments, real_type_var, kind) => {
|
||||
let new_variables =
|
||||
copy_sequence!(arguments.all_variables_len, arguments.all_variables());
|
||||
|
||||
let new_arguments = AliasVariables {
|
||||
variables_start: new_variables.start,
|
||||
..arguments
|
||||
};
|
||||
|
||||
let new_real_type_var = work!(real_type_var);
|
||||
let new_content = Alias(symbol, new_arguments, new_real_type_var, kind);
|
||||
|
||||
subs.set_content_unchecked(copy, new_content);
|
||||
}
|
||||
|
||||
LambdaSet(subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var,
|
||||
unspecialized,
|
||||
ambient_function: ambient_function_var,
|
||||
}) => {
|
||||
let lambda_set_var = copy;
|
||||
|
||||
let new_solved = copy_union!(solved);
|
||||
let new_rec_var = recursion_var.map(|v| work!(v));
|
||||
let new_unspecialized = SubsSlice::reserve_uls_slice(subs, unspecialized.len());
|
||||
|
||||
for (new_uls_index, uls_index) in
|
||||
(new_unspecialized.into_iter()).zip(unspecialized.into_iter())
|
||||
{
|
||||
let Uls(var, sym, region) = subs[uls_index];
|
||||
let new_var = work!(var);
|
||||
|
||||
deep_copy_uls_precondition(subs, var, new_var);
|
||||
|
||||
subs[new_uls_index] = Uls(new_var, sym, region);
|
||||
|
||||
subs.uls_of_var.add(new_var, lambda_set_var);
|
||||
}
|
||||
|
||||
let new_ambient_function_var = work!(ambient_function_var);
|
||||
debug_assert_ne!(
|
||||
ambient_function_var, new_ambient_function_var,
|
||||
"lambda set cloned but its ambient function wasn't?"
|
||||
);
|
||||
|
||||
subs.set_content_unchecked(
|
||||
lambda_set_var,
|
||||
LambdaSet(subs::LambdaSet {
|
||||
solved: new_solved,
|
||||
recursion_var: new_rec_var,
|
||||
unspecialized: new_unspecialized,
|
||||
ambient_function: new_ambient_function_var,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
RangedNumber(range) => {
|
||||
let new_content = RangedNumber(range);
|
||||
|
||||
subs.set_content_unchecked(copy, new_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initial_copy
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn deep_copy_uls_precondition(subs: &Subs, original_var: Variable, new_var: Variable) {
|
||||
if cfg!(debug_assertions) {
|
||||
let content = subs.get_content_without_compacting(original_var);
|
||||
|
||||
debug_assert!(
|
||||
matches!(
|
||||
content,
|
||||
Content::FlexAbleVar(..) | Content::RigidAbleVar(..)
|
||||
),
|
||||
"var in unspecialized lamba set is not bound to an ability, it is {:?}",
|
||||
roc_types::subs::SubsFmtContent(content, subs)
|
||||
);
|
||||
debug_assert!(
|
||||
original_var != new_var,
|
||||
"unspecialized lamba set var was not instantiated"
|
||||
);
|
||||
}
|
||||
}
|
81
crates/compiler/solve/src/env.rs
Normal file
81
crates/compiler/solve/src/env.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_can::{constraint::Constraints, module::ExposedByModule};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_types::subs::{Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable};
|
||||
use roc_unify::unify::Env as UEnv;
|
||||
|
||||
use crate::Pools;
|
||||
|
||||
pub struct DerivedEnv<'a> {
|
||||
pub derived_module: &'a SharedDerivedModule,
|
||||
/// Exposed types needed by the derived module.
|
||||
pub exposed_types: &'a ExposedByModule,
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub constraints: &'a Constraints,
|
||||
pub derived_env: &'a DerivedEnv<'a>,
|
||||
pub subs: &'a mut Subs,
|
||||
pub pools: &'a mut Pools,
|
||||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
#[inline(always)]
|
||||
pub fn register(&mut self, rank: Rank, content: Content) -> Variable {
|
||||
let descriptor = Descriptor {
|
||||
content,
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
|
||||
let var = self.subs.fresh(descriptor);
|
||||
|
||||
self.pools.get_mut(rank).push(var);
|
||||
|
||||
var
|
||||
}
|
||||
|
||||
/// Introduce some variables to Pools at the given rank.
|
||||
/// Also, set each of their ranks in Subs to be the given rank.
|
||||
pub fn introduce(&mut self, rank: Rank, vars: &[Variable]) {
|
||||
let pool: &mut Vec<Variable> = self.pools.get_mut(rank);
|
||||
|
||||
for &var in vars.iter() {
|
||||
self.subs.set_rank(var, rank);
|
||||
}
|
||||
|
||||
pool.extend(vars);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn register_existing_var(&mut self, var: Variable) {
|
||||
self.pools.get_mut(self.subs.get_rank(var)).push(var);
|
||||
}
|
||||
|
||||
pub fn register_with_known_var(
|
||||
&mut self,
|
||||
var: Variable,
|
||||
rank: Rank,
|
||||
content: Content,
|
||||
) -> Variable {
|
||||
let descriptor = Descriptor {
|
||||
content,
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
|
||||
self.subs.set(var, descriptor);
|
||||
|
||||
self.pools.get_mut(rank).push(var);
|
||||
|
||||
var
|
||||
}
|
||||
|
||||
/// Retrieves an environment for unification.
|
||||
pub fn uenv(&mut self) -> UEnv {
|
||||
UEnv::new(self.subs)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,20 @@
|
|||
#![warn(clippy::dbg_macro)]
|
||||
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
// TODO to be removed
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
pub mod ability;
|
||||
pub mod module;
|
||||
pub mod solve;
|
||||
pub mod specialize;
|
||||
|
||||
mod aliases;
|
||||
mod deep_copy;
|
||||
mod env;
|
||||
mod pools;
|
||||
mod to_var;
|
||||
|
||||
pub use aliases::Aliases;
|
||||
pub use env::{DerivedEnv, Env};
|
||||
pub use pools::Pools;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::solve::{self, Aliases};
|
||||
use crate::{aliases::Aliases, solve};
|
||||
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
|
||||
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables};
|
||||
use roc_collections::all::MutMap;
|
||||
|
@ -53,20 +53,41 @@ pub struct SolvedModule {
|
|||
pub exposed_types: ExposedTypesStorageSubs,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
|
||||
pub struct SolveConfig<'a> {
|
||||
/// The module we are solving.
|
||||
pub home: ModuleId,
|
||||
pub constraints: &'a Constraints,
|
||||
pub root_constraint: Constraint,
|
||||
/// All types introduced in the module. Canonicalized, but not necessarily yet associated with
|
||||
/// a variable substitution.
|
||||
pub types: Types,
|
||||
/// Table of types introduced in this module that claim to derive an ability implementation.
|
||||
/// Due for checking and instantiation after the solver runs over the module.
|
||||
pub pending_derives: PendingDerives,
|
||||
/// Types exposed by other modules.
|
||||
/// Available for late instantiation of imports, lambda sets, or ability types.
|
||||
pub exposed_by_module: &'a ExposedByModule,
|
||||
/// The unique `#Derived` module, used to generate and retrieve derived ability
|
||||
/// implementations.
|
||||
/// Needed during solving to resolve lambda sets from derived implementations that escape into
|
||||
/// the user module.
|
||||
pub derived_module: SharedDerivedModule,
|
||||
}
|
||||
|
||||
pub struct SolveOutput {
|
||||
pub subs: Solved<Subs>,
|
||||
pub scope: solve::Scope,
|
||||
pub errors: Vec<TypeError>,
|
||||
pub resolved_abilities_store: AbilitiesStore,
|
||||
}
|
||||
|
||||
pub fn run_solve(
|
||||
home: ModuleId,
|
||||
types: Types,
|
||||
constraints: &Constraints,
|
||||
constraint: ConstraintSoa,
|
||||
config: SolveConfig<'_>,
|
||||
rigid_variables: RigidVariables,
|
||||
mut subs: Subs,
|
||||
mut aliases: Aliases,
|
||||
mut abilities_store: AbilitiesStore,
|
||||
pending_derives: PendingDerives,
|
||||
exposed_by_module: &ExposedByModule,
|
||||
derived_module: SharedDerivedModule,
|
||||
) -> (Solved<Subs>, solve::Env, Vec<TypeError>, AbilitiesStore) {
|
||||
) -> SolveOutput {
|
||||
for (var, name) in rigid_variables.named {
|
||||
subs.rigid_var(var, name);
|
||||
}
|
||||
|
@ -84,21 +105,20 @@ pub fn run_solve(
|
|||
let mut problems = Vec::new();
|
||||
|
||||
// Run the solver to populate Subs.
|
||||
let (solved_subs, solved_env) = solve::run(
|
||||
home,
|
||||
types,
|
||||
constraints,
|
||||
let (solved_subs, solved_scope) = solve::run(
|
||||
config,
|
||||
&mut problems,
|
||||
subs,
|
||||
&mut aliases,
|
||||
&constraint,
|
||||
pending_derives,
|
||||
&mut abilities_store,
|
||||
exposed_by_module,
|
||||
derived_module,
|
||||
);
|
||||
|
||||
(solved_subs, solved_env, problems, abilities_store)
|
||||
SolveOutput {
|
||||
subs: solved_subs,
|
||||
scope: solved_scope,
|
||||
errors: problems,
|
||||
resolved_abilities_store: abilities_store,
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies exposed types and all ability specializations, which may be implicitly exposed.
|
||||
|
|
59
crates/compiler/solve/src/pools.rs
Normal file
59
crates/compiler/solve/src/pools.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use roc_types::subs::{Rank, Variable};
|
||||
|
||||
const DEFAULT_POOLS: usize = 8;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Pools(Vec<Vec<Variable>>);
|
||||
|
||||
impl Default for Pools {
|
||||
fn default() -> Self {
|
||||
Pools::new(DEFAULT_POOLS)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pools {
|
||||
pub fn new(num_pools: usize) -> Self {
|
||||
Pools(vec![Vec::new(); num_pools])
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, rank: Rank) -> &mut Vec<Variable> {
|
||||
match self.0.get_mut(rank.into_usize()) {
|
||||
Some(reference) => reference,
|
||||
None => panic!("Compiler bug: could not find pool at rank {}", rank),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, rank: Rank) -> &Vec<Variable> {
|
||||
match self.0.get(rank.into_usize()) {
|
||||
Some(reference) => reference,
|
||||
None => panic!("Compiler bug: could not find pool at rank {}", rank),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Vec<Variable>> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn split_last(mut self) -> (Vec<Variable>, Vec<Vec<Variable>>) {
|
||||
let last = self
|
||||
.0
|
||||
.pop()
|
||||
.unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools"));
|
||||
|
||||
(last, self.0)
|
||||
}
|
||||
|
||||
pub fn extend_to(&mut self, n: usize) {
|
||||
for _ in self.len()..n {
|
||||
self.0.push(Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
40
crates/compiler/solve/src/solve/scope.rs
Normal file
40
crates/compiler/solve/src/solve/scope.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_types::subs::Variable;
|
||||
|
||||
/// The scope of the solver, as symbols are introduced.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Scope {
|
||||
symbols: Vec<Symbol>,
|
||||
variables: Vec<Variable>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn vars_by_symbol(&self) -> impl Iterator<Item = (Symbol, Variable)> + '_ {
|
||||
let it1 = self.symbols.iter().copied();
|
||||
let it2 = self.variables.iter().copied();
|
||||
|
||||
it1.zip(it2)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_var_by_symbol(&self, symbol: &Symbol) -> Option<Variable> {
|
||||
self.symbols
|
||||
.iter()
|
||||
.position(|s| s == symbol)
|
||||
.map(|index| self.variables[index])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) {
|
||||
match self.symbols.iter().position(|s| *s == symbol) {
|
||||
None => {
|
||||
// symbol is not in vars_by_symbol yet; insert it
|
||||
self.symbols.push(symbol);
|
||||
self.variables.push(var);
|
||||
}
|
||||
Some(_) => {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,16 +2,11 @@
|
|||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_can::{
|
||||
abilities::{AbilitiesStore, ImplKey},
|
||||
module::ExposedByModule,
|
||||
};
|
||||
use roc_can::abilities::{AbilitiesStore, ImplKey};
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_TRACE_COMPACTION;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_derive_key::{DeriveError, DeriveKey};
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
|
@ -22,11 +17,12 @@ use roc_types::{
|
|||
},
|
||||
types::{AliasKind, MemberImpl, Polarity, Uls},
|
||||
};
|
||||
use roc_unify::unify::{unify, Env as UEnv, Mode, MustImplementConstraints};
|
||||
use roc_unify::unify::{unify, Mode, MustImplementConstraints};
|
||||
|
||||
use crate::{
|
||||
ability::builtin_module_with_unlisted_ability_impl,
|
||||
solve::{deep_copy_var_in, introduce, Pools},
|
||||
deep_copy::deep_copy_var_in,
|
||||
env::{DerivedEnv, Env},
|
||||
};
|
||||
|
||||
/// What phase in the compiler is reaching out to specialize lambda sets?
|
||||
|
@ -121,12 +117,6 @@ impl Phase for SolvePhase<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct DerivedEnv<'a> {
|
||||
pub derived_module: &'a SharedDerivedModule,
|
||||
/// Exposed types needed by the derived module.
|
||||
pub exposed_types: &'a ExposedByModule,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AwaitingSpecializations {
|
||||
// What variables' specialized lambda sets in `uls_of_var` will be unlocked for specialization
|
||||
|
@ -305,10 +295,7 @@ fn unique_unspecialized_lambda(subs: &Subs, c_a: Variable, uls: &[Uls]) -> Optio
|
|||
|
||||
#[must_use]
|
||||
pub fn compact_lambda_sets_of_vars<P: Phase>(
|
||||
subs: &mut Subs,
|
||||
derived_env: &DerivedEnv,
|
||||
arena: &Bump,
|
||||
pools: &mut Pools,
|
||||
env: &mut Env,
|
||||
uls_of_var: UlsOfVar,
|
||||
phase: &P,
|
||||
) -> CompactionResult {
|
||||
|
@ -320,7 +307,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
|
||||
// Suppose a type variable `a` with `uls_of_var` mapping `uls_a = {l1, ... ln}` has been instantiated to a concrete type `C_a`.
|
||||
while let Some((c_a, uls_a)) = uls_of_var_queue.pop_front() {
|
||||
let c_a = subs.get_root_key_without_compacting(c_a);
|
||||
let c_a = env.subs.get_root_key_without_compacting(c_a);
|
||||
// 1. Let each `l` in `uls_a` be of form `[solved_lambdas + ... + C:f:r + ...]`.
|
||||
// NB: There may be multiple unspecialized lambdas of form `C:f:r, C:f1:r1, ..., C:fn:rn` in `l`.
|
||||
// In this case, let `t1, ... tm` be the other unspecialized lambdas not of form `C:_:_`,
|
||||
|
@ -332,13 +319,13 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
let mut uls = uls_a.into_vec();
|
||||
|
||||
// De-duplicate lambdas by root key.
|
||||
uls.iter_mut().for_each(|v| *v = subs.get_root_key(*v));
|
||||
uls.iter_mut().for_each(|v| *v = env.subs.get_root_key(*v));
|
||||
uls.sort();
|
||||
uls.dedup();
|
||||
uls
|
||||
};
|
||||
|
||||
trace_compact!(1. subs, c_a, &uls_a);
|
||||
trace_compact!(1. env.subs, c_a, &uls_a);
|
||||
|
||||
// The flattening step - remove lambda sets that don't reference the concrete var, and for
|
||||
// flatten lambda sets that reference it more than once.
|
||||
|
@ -350,15 +337,15 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
recursion_var,
|
||||
unspecialized,
|
||||
ambient_function,
|
||||
} = subs.get_lambda_set(lambda_set);
|
||||
let lambda_set_rank = subs.get_rank(lambda_set);
|
||||
let unspecialized = subs.get_subs_slice(unspecialized);
|
||||
} = env.subs.get_lambda_set(lambda_set);
|
||||
let lambda_set_rank = env.subs.get_rank(lambda_set);
|
||||
let unspecialized = env.subs.get_subs_slice(unspecialized);
|
||||
// TODO: is it faster to traverse once, see if we only have one concrete lambda, and
|
||||
// bail in that happy-path, rather than always splitting?
|
||||
let (concrete, mut not_concrete): (Vec<_>, Vec<_>) = unspecialized
|
||||
.iter()
|
||||
.copied()
|
||||
.partition(|Uls(var, _, _)| subs.equivalent_without_compacting(*var, c_a));
|
||||
.partition(|Uls(var, _, _)| env.subs.equivalent_without_compacting(*var, c_a));
|
||||
if concrete.len() == 1 {
|
||||
// No flattening needs to be done, just return the lambda set as-is
|
||||
return vec![lambda_set];
|
||||
|
@ -373,7 +360,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
// lambdas, plus all other unspecialized lambdas.
|
||||
// l' = [solved_lambdas + t1 + ... + tm + C:f:r]
|
||||
let unspecialized = SubsSlice::extend_new(
|
||||
&mut subs.unspecialized_lambda_sets,
|
||||
&mut env.subs.unspecialized_lambda_sets,
|
||||
not_concrete
|
||||
.drain(..)
|
||||
.chain(std::iter::once(concrete_lambda)),
|
||||
|
@ -384,10 +371,10 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
// lambdas.
|
||||
// ln = [[] + C:fn:rn]
|
||||
let unspecialized = SubsSlice::extend_new(
|
||||
&mut subs.unspecialized_lambda_sets,
|
||||
&mut env.subs.unspecialized_lambda_sets,
|
||||
[concrete_lambda],
|
||||
);
|
||||
let var = subs.fresh(Descriptor {
|
||||
let var = env.subs.fresh(Descriptor {
|
||||
content: Content::Error,
|
||||
rank: lambda_set_rank,
|
||||
mark: Mark::NONE,
|
||||
|
@ -396,7 +383,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
(var, unspecialized)
|
||||
};
|
||||
|
||||
subs.set_content(
|
||||
env.subs.set_content(
|
||||
var,
|
||||
Content::LambdaSet(LambdaSet {
|
||||
solved,
|
||||
|
@ -414,11 +401,15 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
// 2. Now, each `l` in `uls_a` has a unique unspecialized lambda of form `C:f:r`.
|
||||
// Sort `uls_a` primarily by `f` (arbitrary order), and secondarily by `r` in descending order.
|
||||
uls_a.sort_by(|v1, v2| {
|
||||
let unspec_1 = subs.get_subs_slice(subs.get_lambda_set(*v1).unspecialized);
|
||||
let unspec_2 = subs.get_subs_slice(subs.get_lambda_set(*v2).unspecialized);
|
||||
let unspec_1 = env
|
||||
.subs
|
||||
.get_subs_slice(env.subs.get_lambda_set(*v1).unspecialized);
|
||||
let unspec_2 = env
|
||||
.subs
|
||||
.get_subs_slice(env.subs.get_lambda_set(*v2).unspecialized);
|
||||
|
||||
let Uls(_, f1, r1) = unique_unspecialized_lambda(subs, c_a, unspec_1).unwrap();
|
||||
let Uls(_, f2, r2) = unique_unspecialized_lambda(subs, c_a, unspec_2).unwrap();
|
||||
let Uls(_, f1, r1) = unique_unspecialized_lambda(env.subs, c_a, unspec_1).unwrap();
|
||||
let Uls(_, f2, r2) = unique_unspecialized_lambda(env.subs, c_a, unspec_2).unwrap();
|
||||
|
||||
match f1.cmp(&f2) {
|
||||
std::cmp::Ordering::Equal => {
|
||||
|
@ -429,7 +420,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
}
|
||||
});
|
||||
|
||||
trace_compact!(2. subs, &uls_a);
|
||||
trace_compact!(2. env.subs, &uls_a);
|
||||
|
||||
// 3. For each `l` in `uls_a` with unique unspecialized lambda `C:f:r`:
|
||||
// 1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`. Remove `C:f:r` from `t_f1`'s lambda set.
|
||||
|
@ -439,8 +430,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
// 3. Unify `t_f1 ~ t_f2`.
|
||||
trace_compact!(3start.);
|
||||
for l in uls_a {
|
||||
let compaction_result =
|
||||
compact_lambda_set(subs, derived_env, arena, pools, c_a, l, phase);
|
||||
let compaction_result = compact_lambda_set(env, c_a, l, phase);
|
||||
|
||||
match compaction_result {
|
||||
OneCompactionResult::Compacted {
|
||||
|
@ -474,10 +464,7 @@ enum OneCompactionResult {
|
|||
#[must_use]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compact_lambda_set<P: Phase>(
|
||||
subs: &mut Subs,
|
||||
derived_env: &DerivedEnv,
|
||||
arena: &Bump,
|
||||
pools: &mut Pools,
|
||||
env: &mut Env,
|
||||
resolved_concrete: Variable,
|
||||
this_lambda_set: Variable,
|
||||
phase: &P,
|
||||
|
@ -493,23 +480,24 @@ fn compact_lambda_set<P: Phase>(
|
|||
recursion_var,
|
||||
unspecialized,
|
||||
ambient_function: t_f1,
|
||||
} = subs.get_lambda_set(this_lambda_set);
|
||||
let target_rank = subs.get_rank(this_lambda_set);
|
||||
} = env.subs.get_lambda_set(this_lambda_set);
|
||||
let target_rank = env.subs.get_rank(this_lambda_set);
|
||||
|
||||
debug_assert!(!unspecialized.is_empty());
|
||||
|
||||
let unspecialized = subs.get_subs_slice(unspecialized);
|
||||
let unspecialized = env.subs.get_subs_slice(unspecialized);
|
||||
|
||||
// 1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`.
|
||||
let Uls(c, f, r) = unique_unspecialized_lambda(subs, resolved_concrete, unspecialized).unwrap();
|
||||
let Uls(c, f, r) =
|
||||
unique_unspecialized_lambda(env.subs, resolved_concrete, unspecialized).unwrap();
|
||||
|
||||
debug_assert!(subs.equivalent_without_compacting(c, resolved_concrete));
|
||||
debug_assert!(env.subs.equivalent_without_compacting(c, resolved_concrete));
|
||||
|
||||
// Now decide: do we
|
||||
// - proceed with specialization
|
||||
// - simply drop the specialization lambda set (due to an error)
|
||||
// - or do we need to wait, because we don't know enough information for the specialization yet?
|
||||
let specialization_decision = make_specialization_decision(subs, phase, c, f);
|
||||
let specialization_decision = make_specialization_decision(env.subs, phase, c, f);
|
||||
let specialization_key_or_drop = match specialization_decision {
|
||||
SpecializeDecision::Specialize(key) => Ok(key),
|
||||
SpecializeDecision::Drop => Err(()),
|
||||
|
@ -522,7 +510,10 @@ fn compact_lambda_set<P: Phase>(
|
|||
// 1b. Remove `C:f:r` from `t_f1`'s lambda set.
|
||||
let new_unspecialized: Vec<_> = unspecialized
|
||||
.iter()
|
||||
.filter(|Uls(v, _, _)| !subs.equivalent_without_compacting(*v, resolved_concrete))
|
||||
.filter(|Uls(v, _, _)| {
|
||||
!env.subs
|
||||
.equivalent_without_compacting(*v, resolved_concrete)
|
||||
})
|
||||
.copied()
|
||||
.collect();
|
||||
debug_assert_eq!(new_unspecialized.len(), unspecialized.len() - 1);
|
||||
|
@ -530,12 +521,12 @@ fn compact_lambda_set<P: Phase>(
|
|||
solved,
|
||||
recursion_var,
|
||||
unspecialized: SubsSlice::extend_new(
|
||||
&mut subs.unspecialized_lambda_sets,
|
||||
&mut env.subs.unspecialized_lambda_sets,
|
||||
new_unspecialized,
|
||||
),
|
||||
ambient_function: t_f1,
|
||||
};
|
||||
subs.set_content(
|
||||
env.subs.set_content(
|
||||
this_lambda_set,
|
||||
Content::LambdaSet(t_f1_lambda_set_without_concrete),
|
||||
);
|
||||
|
@ -545,7 +536,7 @@ fn compact_lambda_set<P: Phase>(
|
|||
Err(()) => {
|
||||
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
|
||||
// which we already did in 1b above.
|
||||
trace_compact!(3iter_end_skipped.subs, t_f1);
|
||||
trace_compact!(3iter_end_skipped. env.subs, t_f1);
|
||||
return OneCompactionResult::Compacted {
|
||||
new_obligations: Default::default(),
|
||||
new_lambda_sets_to_specialize: Default::default(),
|
||||
|
@ -554,8 +545,8 @@ fn compact_lambda_set<P: Phase>(
|
|||
};
|
||||
|
||||
let specialization_ambient_function_var = get_specialization_lambda_set_ambient_function(
|
||||
subs,
|
||||
derived_env,
|
||||
env.subs,
|
||||
env.derived_env,
|
||||
phase,
|
||||
f,
|
||||
r,
|
||||
|
@ -568,7 +559,7 @@ fn compact_lambda_set<P: Phase>(
|
|||
Err(()) => {
|
||||
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
|
||||
// which we already did in 1b above.
|
||||
trace_compact!(3iter_end_skipped.subs, t_f1);
|
||||
trace_compact!(3iter_end_skipped. env.subs, t_f1);
|
||||
return OneCompactionResult::Compacted {
|
||||
new_obligations: Default::default(),
|
||||
new_lambda_sets_to_specialize: Default::default(),
|
||||
|
@ -578,21 +569,21 @@ fn compact_lambda_set<P: Phase>(
|
|||
|
||||
// Ensure the specialized ambient function we'll unify with is not a generalized one, but one
|
||||
// at the rank of the lambda set being compacted.
|
||||
let t_f2 = deep_copy_var_in(subs, target_rank, pools, t_f2, arena);
|
||||
let t_f2 = deep_copy_var_in(env, target_rank, t_f2, env.arena);
|
||||
|
||||
// 3. Unify `t_f1 ~ t_f2`.
|
||||
trace_compact!(3iter_start.subs, this_lambda_set, t_f1, t_f2);
|
||||
trace_compact!(3iter_start. env.subs, this_lambda_set, t_f1, t_f2);
|
||||
let (vars, new_obligations, new_lambda_sets_to_specialize, _meta) = unify(
|
||||
&mut UEnv::new(subs),
|
||||
&mut env.uenv(),
|
||||
t_f1,
|
||||
t_f2,
|
||||
Mode::LAMBDA_SET_SPECIALIZATION,
|
||||
Polarity::Pos,
|
||||
)
|
||||
.expect_success("ambient functions don't unify");
|
||||
trace_compact!(3iter_end.subs, t_f1);
|
||||
trace_compact!(3iter_end. env.subs, t_f1);
|
||||
|
||||
introduce(subs, target_rank, pools, &vars);
|
||||
env.introduce(target_rank, &vars);
|
||||
|
||||
OneCompactionResult::Compacted {
|
||||
new_obligations,
|
||||
|
|
1292
crates/compiler/solve/src/to_var.rs
Normal file
1292
crates/compiler/solve/src/to_var.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|||
|
||||
use bumpalo::Bump;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_solve::module::{SolveConfig, SolveOutput};
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
use roc_can::{
|
||||
|
@ -418,18 +419,27 @@ fn check_derived_typechecks_and_golden(
|
|||
roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED,
|
||||
std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "1")
|
||||
);
|
||||
let (mut solved_subs, _, problems, _) = roc_solve::module::run_solve(
|
||||
test_module,
|
||||
|
||||
let solve_config = SolveConfig {
|
||||
home: test_module,
|
||||
constraints: &constraints,
|
||||
root_constraint: constr,
|
||||
types,
|
||||
&constraints,
|
||||
constr,
|
||||
pending_derives: Default::default(),
|
||||
exposed_by_module: &exposed_for_module.exposed_by_module,
|
||||
derived_module: Default::default(),
|
||||
};
|
||||
|
||||
let SolveOutput {
|
||||
subs: mut solved_subs,
|
||||
errors: problems,
|
||||
..
|
||||
} = roc_solve::module::run_solve(
|
||||
solve_config,
|
||||
RigidVariables::default(),
|
||||
test_subs,
|
||||
Default::default(),
|
||||
abilities_store,
|
||||
Default::default(),
|
||||
&exposed_for_module.exposed_by_module,
|
||||
Default::default(),
|
||||
);
|
||||
dbg_do!(
|
||||
roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED,
|
||||
|
|
|
@ -15,7 +15,8 @@ use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
|
|||
use roc_parse::parser::{SourceError, SyntaxError};
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::Loc;
|
||||
use roc_solve::solve::{self, Aliases};
|
||||
use roc_solve::module::SolveConfig;
|
||||
use roc_solve::{solve, Aliases};
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{Content, Subs, VarStore, Variable};
|
||||
use roc_types::types::Types;
|
||||
|
@ -33,26 +34,24 @@ pub fn infer_expr(
|
|||
problems: &mut Vec<TypeError>,
|
||||
types: Types,
|
||||
constraints: &Constraints,
|
||||
constraint: &Constraint,
|
||||
constraint: Constraint,
|
||||
pending_derives: PendingDerives,
|
||||
aliases: &mut Aliases,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
derived_module: SharedDerivedModule,
|
||||
expr_var: Variable,
|
||||
) -> (Content, Subs) {
|
||||
let (solved, _) = solve::run(
|
||||
ModuleId::ATTR,
|
||||
let config = SolveConfig {
|
||||
types,
|
||||
constraints,
|
||||
problems,
|
||||
subs,
|
||||
aliases,
|
||||
constraint,
|
||||
root_constraint: constraint,
|
||||
home: ModuleId::ATTR,
|
||||
pending_derives,
|
||||
abilities_store,
|
||||
&Default::default(),
|
||||
exposed_by_module: &Default::default(),
|
||||
derived_module,
|
||||
);
|
||||
};
|
||||
|
||||
let (solved, _) = solve::run(config, problems, subs, aliases, abilities_store);
|
||||
|
||||
let content = *solved.inner().get_content_without_compacting(expr_var);
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ mod test_reporting {
|
|||
subs.rigid_var(var.value, "*".into());
|
||||
}
|
||||
|
||||
let mut solve_aliases = roc_solve::solve::Aliases::default();
|
||||
let mut solve_aliases = roc_solve::Aliases::default();
|
||||
|
||||
for (name, alias) in output.aliases {
|
||||
solve_aliases.insert(&mut types, name, alias);
|
||||
|
@ -271,7 +271,7 @@ mod test_reporting {
|
|||
&mut unify_problems,
|
||||
types,
|
||||
&constraints,
|
||||
&constraint,
|
||||
constraint,
|
||||
// Use `new_report_problem_as` in order to get proper derives.
|
||||
// TODO: remove the non-new reporting test infra.
|
||||
PendingDerives::default(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue