mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-17 02:20:16 +00:00
248 lines
8.8 KiB
Rust
248 lines
8.8 KiB
Rust
use crate::solve::RunSolveOutput;
|
|
use crate::FunctionKind;
|
|
use crate::{aliases::Aliases, solve};
|
|
use roc_can::abilities::{AbilitiesStore, ResolvedImpl};
|
|
use roc_can::constraint::{Constraint, Constraints};
|
|
use roc_can::expr::PendingDerives;
|
|
use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables};
|
|
use roc_collections::all::MutMap;
|
|
use roc_collections::VecMap;
|
|
use roc_derive::SharedDerivedModule;
|
|
use roc_error_macros::internal_error;
|
|
use roc_module::symbol::{ModuleId, Symbol};
|
|
use roc_solve_problem::TypeError;
|
|
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
|
|
use roc_types::types::{Alias, MemberImpl, Types};
|
|
|
|
/// A marker that a given Subs has been solved.
|
|
/// The only way to obtain a Solved<Subs> is by running the solver on it.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Solved<T>(pub T);
|
|
|
|
impl<T> Solved<T> {
|
|
pub fn inner(&self) -> &'_ T {
|
|
&self.0
|
|
}
|
|
|
|
pub fn inner_mut(&mut self) -> &'_ mut T {
|
|
&mut self.0
|
|
}
|
|
|
|
pub fn into_inner(self) -> T {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SolvedModule {
|
|
pub problems: Vec<TypeError>,
|
|
|
|
/// all aliases and their definitions. this has to include non-exposed aliases
|
|
/// because exposed aliases can depend on non-exposed ones)
|
|
pub aliases: MutMap<Symbol, (bool, Alias)>,
|
|
|
|
/// Used when the goal phase is TypeChecking, and
|
|
/// to create the types for HostExposed. This
|
|
/// has some overlap with the StorageSubs fields,
|
|
/// so maybe we can get rid of this at some point
|
|
///
|
|
/// Contains both variables of symbols that are explicitly exposed by the header,
|
|
/// and the variables of any solved ability specializations we have.
|
|
pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
|
|
|
/// Used when importing this module into another module
|
|
pub solved_implementations: ResolvedImplementations,
|
|
pub exposed_types: ExposedTypesStorageSubs,
|
|
}
|
|
|
|
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,
|
|
/// How functions should be kinded.
|
|
pub function_kind: FunctionKind,
|
|
/// 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,
|
|
|
|
#[cfg(debug_assertions)]
|
|
/// The checkmate collector for this module.
|
|
pub checkmate: Option<roc_checkmate::Collector>,
|
|
}
|
|
|
|
pub struct SolveOutput {
|
|
pub subs: Solved<Subs>,
|
|
pub scope: solve::Scope,
|
|
pub errors: Vec<TypeError>,
|
|
pub resolved_abilities_store: AbilitiesStore,
|
|
|
|
#[cfg(debug_assertions)]
|
|
pub checkmate: Option<roc_checkmate::Collector>,
|
|
}
|
|
|
|
pub fn run_solve(
|
|
config: SolveConfig<'_>,
|
|
rigid_variables: RigidVariables,
|
|
mut subs: Subs,
|
|
mut aliases: Aliases,
|
|
mut abilities_store: AbilitiesStore,
|
|
) -> SolveOutput {
|
|
for (var, name) in rigid_variables.named {
|
|
subs.rigid_var(var, name);
|
|
}
|
|
|
|
for (var, (name, abilities)) in rigid_variables.able {
|
|
subs.rigid_able_var(var, name, abilities);
|
|
}
|
|
|
|
for var in rigid_variables.wildcards {
|
|
subs.rigid_var(var, "*".into());
|
|
}
|
|
|
|
// Now that the module is parsed, canonicalized, and constrained,
|
|
// we need to type check it.
|
|
let mut problems = Vec::new();
|
|
|
|
// Run the solver to populate Subs.
|
|
let RunSolveOutput {
|
|
solved,
|
|
scope,
|
|
#[cfg(debug_assertions)]
|
|
checkmate,
|
|
} = solve::run(
|
|
config,
|
|
&mut problems,
|
|
subs,
|
|
&mut aliases,
|
|
&mut abilities_store,
|
|
);
|
|
|
|
SolveOutput {
|
|
subs: solved,
|
|
scope,
|
|
errors: problems,
|
|
resolved_abilities_store: abilities_store,
|
|
#[cfg(debug_assertions)]
|
|
checkmate,
|
|
}
|
|
}
|
|
|
|
/// Copies exposed types and all ability specializations, which may be implicitly exposed.
|
|
pub fn exposed_types_storage_subs(
|
|
home: ModuleId,
|
|
solved_subs: &mut Solved<Subs>,
|
|
exposed_vars_by_symbol: &[(Symbol, Variable)],
|
|
solved_implementations: &ResolvedImplementations,
|
|
abilities_store: &AbilitiesStore,
|
|
) -> ExposedTypesStorageSubs {
|
|
let subs = solved_subs.inner_mut();
|
|
let mut storage_subs = StorageSubs::new(Subs::new());
|
|
let mut stored_vars_by_symbol = VecMap::with_capacity(exposed_vars_by_symbol.len());
|
|
|
|
for (symbol, var) in exposed_vars_by_symbol.iter() {
|
|
let new_var = storage_subs.import_variable_from(subs, *var).variable;
|
|
stored_vars_by_symbol.insert(*symbol, new_var);
|
|
}
|
|
|
|
let mut stored_specialization_lambda_set_vars =
|
|
VecMap::with_capacity(solved_implementations.len());
|
|
|
|
for (_, member_impl) in solved_implementations.iter() {
|
|
match member_impl {
|
|
ResolvedImpl::Impl(member_specialization) => {
|
|
// Export all the lambda sets and their ambient functions.
|
|
for (_, &lset_var) in member_specialization.specialization_lambda_sets.iter() {
|
|
let specialization_lset_ambient_function_var =
|
|
subs.get_lambda_set(lset_var).ambient_function;
|
|
|
|
// Import the ambient function of this specialization lambda set; that will import the
|
|
// lambda set as well. The ambient function is needed for the lambda set compaction
|
|
// algorithm.
|
|
let imported_lset_ambient_function_var = storage_subs
|
|
.import_variable_from(subs, specialization_lset_ambient_function_var)
|
|
.variable;
|
|
|
|
let imported_lset_var = match storage_subs
|
|
.as_inner()
|
|
.get_content_without_compacting(imported_lset_ambient_function_var)
|
|
{
|
|
Content::Structure(FlatType::Func(_, lambda_set_var, _)) => *lambda_set_var,
|
|
content => internal_error!(
|
|
"ambient lambda set function import is not a function, found: {:?}",
|
|
roc_types::subs::SubsFmtContent(content, storage_subs.as_inner())
|
|
),
|
|
};
|
|
stored_specialization_lambda_set_vars.insert(lset_var, imported_lset_var);
|
|
}
|
|
}
|
|
ResolvedImpl::Error => {
|
|
// nothing to do
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store the regioned lambda sets of the ability members defined in this module.
|
|
let stored_ability_member_vars = abilities_store
|
|
.root_ability_members()
|
|
.iter()
|
|
.filter_map(|(member, data)| {
|
|
if member.module_id() == home {
|
|
let var = data.signature_var();
|
|
let imported_var = storage_subs.import_variable_from(subs, var).variable;
|
|
Some((var, imported_var))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
ExposedTypesStorageSubs {
|
|
storage_subs,
|
|
stored_vars_by_symbol,
|
|
stored_specialization_lambda_set_vars,
|
|
stored_ability_member_vars,
|
|
}
|
|
}
|
|
|
|
/// Extracts the ability member implementations owned by a solved module.
|
|
pub fn extract_module_owned_implementations(
|
|
module_id: ModuleId,
|
|
abilities_store: &AbilitiesStore,
|
|
) -> ResolvedImplementations {
|
|
abilities_store
|
|
.iter_declared_implementations()
|
|
.filter_map(|(impl_key, member_impl)| {
|
|
// This module solved this specialization if either the member or the type comes from the
|
|
// module.
|
|
if impl_key.ability_member.module_id() != module_id
|
|
&& impl_key.opaque.module_id() != module_id
|
|
{
|
|
return None;
|
|
}
|
|
|
|
let resolved_impl = match member_impl {
|
|
MemberImpl::Impl(impl_symbol) => {
|
|
let specialization = abilities_store.specialization_info(*impl_symbol).expect(
|
|
"declared implementations should be resolved conclusively after solving",
|
|
);
|
|
ResolvedImpl::Impl(specialization.clone())
|
|
}
|
|
MemberImpl::Error => ResolvedImpl::Error,
|
|
};
|
|
|
|
Some((impl_key, resolved_impl))
|
|
})
|
|
.collect()
|
|
}
|