mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge pull request #2857 from rtfeldman/abilities-mono
Codegen for abilities
This commit is contained in:
commit
718b999751
15 changed files with 442 additions and 103 deletions
|
@ -16,6 +16,7 @@ pub struct MemberVariables {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct AbilityMemberData {
|
pub struct AbilityMemberData {
|
||||||
pub parent_ability: Symbol,
|
pub parent_ability: Symbol,
|
||||||
|
pub signature_var: Variable,
|
||||||
pub signature: Type,
|
pub signature: Type,
|
||||||
pub variables: MemberVariables,
|
pub variables: MemberVariables,
|
||||||
pub region: Region,
|
pub region: Region,
|
||||||
|
@ -60,15 +61,16 @@ impl AbilitiesStore {
|
||||||
pub fn register_ability(
|
pub fn register_ability(
|
||||||
&mut self,
|
&mut self,
|
||||||
ability: Symbol,
|
ability: Symbol,
|
||||||
members: Vec<(Symbol, Region, Type, MemberVariables)>,
|
members: Vec<(Symbol, Region, Variable, Type, MemberVariables)>,
|
||||||
) {
|
) {
|
||||||
let mut members_vec = Vec::with_capacity(members.len());
|
let mut members_vec = Vec::with_capacity(members.len());
|
||||||
for (member, region, signature, variables) in members.into_iter() {
|
for (member, region, signature_var, signature, variables) in members.into_iter() {
|
||||||
members_vec.push(member);
|
members_vec.push(member);
|
||||||
let old_member = self.ability_members.insert(
|
let old_member = self.ability_members.insert(
|
||||||
member,
|
member,
|
||||||
AbilityMemberData {
|
AbilityMemberData {
|
||||||
parent_ability: ability,
|
parent_ability: ability,
|
||||||
|
signature_var,
|
||||||
signature,
|
signature,
|
||||||
region,
|
region,
|
||||||
variables,
|
variables,
|
||||||
|
|
|
@ -542,7 +542,13 @@ pub fn canonicalize_defs<'a>(
|
||||||
flex_vars: iv.collect_flex(),
|
flex_vars: iv.collect_flex(),
|
||||||
};
|
};
|
||||||
|
|
||||||
can_members.push((member_sym, name_region, member_annot.typ, variables));
|
can_members.push((
|
||||||
|
member_sym,
|
||||||
|
name_region,
|
||||||
|
var_store.fresh(),
|
||||||
|
member_annot.typ,
|
||||||
|
variables,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store what symbols a type must define implementations for to have this ability.
|
// Store what symbols a type must define implementations for to have this ability.
|
||||||
|
|
|
@ -2,12 +2,14 @@ use roc_builtins::std::StdLib;
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::constraint::{Constraint, Constraints};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::def::Declaration;
|
use roc_can::def::Declaration;
|
||||||
|
use roc_can::expected::Expected;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::solved_types::{FreeVars, SolvedType};
|
use roc_types::solved_types::{FreeVars, SolvedType};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
use roc_types::types::{Category, Type};
|
||||||
|
|
||||||
/// The types of all exposed values/functions of a collection of modules
|
/// The types of all exposed values/functions of a collection of modules
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -105,27 +107,53 @@ pub fn constrain_module(
|
||||||
declarations: &[Declaration],
|
declarations: &[Declaration],
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let mut constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
let constraint = crate::expr::constrain_decls(constraints, home, declarations);
|
||||||
|
|
||||||
|
let constraint = frontload_ability_constraints(constraints, abilities_store, constraint);
|
||||||
|
|
||||||
|
// The module constraint should always save the environment at the end.
|
||||||
|
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
||||||
|
|
||||||
|
constraint
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frontload_ability_constraints(
|
||||||
|
constraints: &mut Constraints,
|
||||||
|
abilities_store: &AbilitiesStore,
|
||||||
|
mut constraint: Constraint,
|
||||||
|
) -> Constraint {
|
||||||
for (member_name, member_data) in abilities_store.root_ability_members().iter() {
|
for (member_name, member_data) in abilities_store.root_ability_members().iter() {
|
||||||
|
// 1. Attach the type of member signature to the reserved signature_var. This is
|
||||||
|
// infallible.
|
||||||
|
let unify_with_signature_var = constraints.equal_types_var(
|
||||||
|
member_data.signature_var,
|
||||||
|
Expected::NoExpectation(member_data.signature.clone()),
|
||||||
|
Category::Storage(std::file!(), std::column!()),
|
||||||
|
Region::zero(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Store the member signature on the member symbol. This makes sure we generalize it on
|
||||||
|
// the toplevel, as appropriate.
|
||||||
let vars = &member_data.variables;
|
let vars = &member_data.variables;
|
||||||
let rigids = (vars.rigid_vars.iter())
|
let rigids = (vars.rigid_vars.iter())
|
||||||
// For our purposes, in the let constraint, able vars are treated like rigids.
|
// For our purposes, in the let constraint, able vars are treated like rigids.
|
||||||
.chain(vars.able_vars.iter())
|
.chain(vars.able_vars.iter())
|
||||||
.copied();
|
.copied();
|
||||||
let flex = vars.flex_vars.iter().copied();
|
let flex = vars.flex_vars.iter().copied();
|
||||||
constraint = constraints.let_constraint(
|
|
||||||
|
let let_constr = constraints.let_constraint(
|
||||||
rigids,
|
rigids,
|
||||||
flex,
|
flex,
|
||||||
[(*member_name, Loc::at_zero(member_data.signature.clone()))],
|
[(
|
||||||
|
*member_name,
|
||||||
|
Loc::at_zero(Type::Variable(member_data.signature_var)),
|
||||||
|
)],
|
||||||
Constraint::True,
|
Constraint::True,
|
||||||
constraint,
|
constraint,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
constraint = constraints.and_constraint([unify_with_signature_var, let_constr]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The module constraint should always save the environment at the end.
|
|
||||||
debug_assert!(constraints.contains_save_the_environment(&constraint));
|
|
||||||
|
|
||||||
constraint
|
constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,6 +271,7 @@ fn start_phase<'a>(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
decls,
|
decls,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
abilities_store,
|
||||||
} = typechecked;
|
} = typechecked;
|
||||||
|
|
||||||
let mut imported_module_thunks = bumpalo::collections::Vec::new_in(arena);
|
let mut imported_module_thunks = bumpalo::collections::Vec::new_in(arena);
|
||||||
|
@ -294,6 +295,7 @@ fn start_phase<'a>(
|
||||||
decls,
|
decls,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
exposed_to_host: state.exposed_to_host.clone(),
|
exposed_to_host: state.exposed_to_host.clone(),
|
||||||
|
abilities_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Phase::MakeSpecializations => {
|
Phase::MakeSpecializations => {
|
||||||
|
@ -316,6 +318,7 @@ fn start_phase<'a>(
|
||||||
procs_base,
|
procs_base,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
} = found_specializations;
|
} = found_specializations;
|
||||||
|
|
||||||
BuildTask::MakeSpecializations {
|
BuildTask::MakeSpecializations {
|
||||||
|
@ -326,6 +329,7 @@ fn start_phase<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,6 +423,7 @@ pub struct TypeCheckedModule<'a> {
|
||||||
pub solved_subs: Solved<Subs>,
|
pub solved_subs: Solved<Subs>,
|
||||||
pub decls: Vec<Declaration>,
|
pub decls: Vec<Declaration>,
|
||||||
pub ident_ids: IdentIds,
|
pub ident_ids: IdentIds,
|
||||||
|
pub abilities_store: AbilitiesStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -429,6 +434,7 @@ struct FoundSpecializationsModule<'a> {
|
||||||
procs_base: ProcsBase<'a>,
|
procs_base: ProcsBase<'a>,
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -529,6 +535,7 @@ enum Msg<'a> {
|
||||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||||
solved_subs: Solved<Subs>,
|
solved_subs: Solved<Subs>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
},
|
},
|
||||||
MadeSpecializations {
|
MadeSpecializations {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
|
@ -767,6 +774,7 @@ enum BuildTask<'a> {
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
decls: Vec<Declaration>,
|
decls: Vec<Declaration>,
|
||||||
exposed_to_host: ExposedToHost,
|
exposed_to_host: ExposedToHost,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
},
|
},
|
||||||
MakeSpecializations {
|
MakeSpecializations {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
|
@ -776,6 +784,7 @@ enum BuildTask<'a> {
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1880,6 +1889,7 @@ fn update<'a>(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
decls,
|
decls,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
|
@ -1904,6 +1914,7 @@ fn update<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
problems,
|
problems,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
} => {
|
} => {
|
||||||
log!("found specializations for {:?}", module_id);
|
log!("found specializations for {:?}", module_id);
|
||||||
|
|
||||||
|
@ -1925,6 +1936,7 @@ fn update<'a>(
|
||||||
procs_base,
|
procs_base,
|
||||||
subs,
|
subs,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
|
@ -3642,6 +3654,7 @@ fn make_specializations<'a>(
|
||||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
mut module_timing: ModuleTiming,
|
mut module_timing: ModuleTiming,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let make_specializations_start = SystemTime::now();
|
let make_specializations_start = SystemTime::now();
|
||||||
let mut mono_problems = Vec::new();
|
let mut mono_problems = Vec::new();
|
||||||
|
@ -3657,6 +3670,7 @@ fn make_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
|
abilities_store: &abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut procs = Procs::new_in(arena);
|
let mut procs = Procs::new_in(arena);
|
||||||
|
@ -3727,6 +3741,7 @@ fn build_pending_specializations<'a>(
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
// TODO remove
|
// TODO remove
|
||||||
exposed_to_host: ExposedToHost,
|
exposed_to_host: ExposedToHost,
|
||||||
|
abilities_store: AbilitiesStore,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let find_specializations_start = SystemTime::now();
|
let find_specializations_start = SystemTime::now();
|
||||||
|
|
||||||
|
@ -3753,6 +3768,7 @@ fn build_pending_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
|
abilities_store: &abilities_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add modules' decls to Procs
|
// Add modules' decls to Procs
|
||||||
|
@ -3806,6 +3822,7 @@ fn build_pending_specializations<'a>(
|
||||||
procs_base,
|
procs_base,
|
||||||
problems,
|
problems,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3823,7 +3840,11 @@ fn add_def_to_module<'a>(
|
||||||
use roc_can::pattern::Pattern::*;
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
match def.loc_pattern.value {
|
match def.loc_pattern.value {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol)
|
||||||
|
| AbilityMemberSpecialization {
|
||||||
|
ident: symbol,
|
||||||
|
specializes: _,
|
||||||
|
} => {
|
||||||
let is_host_exposed = exposed_to_host.contains_key(&symbol);
|
let is_host_exposed = exposed_to_host.contains_key(&symbol);
|
||||||
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
|
@ -4026,6 +4047,7 @@ fn run_task<'a>(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
imported_module_thunks,
|
imported_module_thunks,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
|
abilities_store,
|
||||||
} => Ok(build_pending_specializations(
|
} => Ok(build_pending_specializations(
|
||||||
arena,
|
arena,
|
||||||
solved_subs,
|
solved_subs,
|
||||||
|
@ -4037,6 +4059,7 @@ fn run_task<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
target_info,
|
target_info,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
|
abilities_store,
|
||||||
)),
|
)),
|
||||||
MakeSpecializations {
|
MakeSpecializations {
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -4046,6 +4069,7 @@ fn run_task<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
abilities_store,
|
||||||
} => Ok(make_specializations(
|
} => Ok(make_specializations(
|
||||||
arena,
|
arena,
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -4056,6 +4080,7 @@ fn run_task<'a>(
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
module_timing,
|
module_timing,
|
||||||
target_info,
|
target_info,
|
||||||
|
abilities_store,
|
||||||
)),
|
)),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::layout::{
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::expr::{ClosureData, IntValue};
|
use roc_can::expr::{ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, Guard, RenderAs, TagId};
|
||||||
|
@ -251,11 +252,22 @@ impl<'a> PartialProc<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum PartialExprLink {
|
enum PolymorphicExpr {
|
||||||
Aliases(Symbol),
|
/// A root ability member, which must be specialized at a call site, for example
|
||||||
|
/// "hash" which must be specialized to an exact symbol implementing "hash" for a type.
|
||||||
|
AbilityMember(Symbol),
|
||||||
|
/// A polymorphic expression we inline at the usage site.
|
||||||
Expr(roc_can::expr::Expr, Variable),
|
Expr(roc_can::expr::Expr, Variable),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum PartialExprLink {
|
||||||
|
/// The root polymorphic expression
|
||||||
|
Sink(PolymorphicExpr),
|
||||||
|
/// A hop in a partial expression alias chain
|
||||||
|
Aliases(Symbol),
|
||||||
|
}
|
||||||
|
|
||||||
/// A table of symbols to polymorphic expressions. For example, in the program
|
/// A table of symbols to polymorphic expressions. For example, in the program
|
||||||
///
|
///
|
||||||
/// n = 1
|
/// n = 1
|
||||||
|
@ -281,8 +293,8 @@ impl PartialExprs {
|
||||||
Self(BumpMap::new_in(arena))
|
Self(BumpMap::new_in(arena))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, symbol: Symbol, expr: roc_can::expr::Expr, expr_var: Variable) {
|
fn insert(&mut self, symbol: Symbol, expr: PolymorphicExpr) {
|
||||||
self.0.insert(symbol, PartialExprLink::Expr(expr, expr_var));
|
self.0.insert(symbol, PartialExprLink::Sink(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) {
|
fn insert_alias(&mut self, symbol: Symbol, aliases: Symbol) {
|
||||||
|
@ -293,7 +305,7 @@ impl PartialExprs {
|
||||||
self.0.contains_key(&symbol)
|
self.0.contains_key(&symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&mut self, mut symbol: Symbol) -> Option<(&roc_can::expr::Expr, Variable)> {
|
fn get(&mut self, mut symbol: Symbol) -> Option<&PolymorphicExpr> {
|
||||||
// In practice the alias chain is very short
|
// In practice the alias chain is very short
|
||||||
loop {
|
loop {
|
||||||
match self.0.get(&symbol) {
|
match self.0.get(&symbol) {
|
||||||
|
@ -303,8 +315,8 @@ impl PartialExprs {
|
||||||
Some(&PartialExprLink::Aliases(real_symbol)) => {
|
Some(&PartialExprLink::Aliases(real_symbol)) => {
|
||||||
symbol = real_symbol;
|
symbol = real_symbol;
|
||||||
}
|
}
|
||||||
Some(PartialExprLink::Expr(expr, var)) => {
|
Some(PartialExprLink::Sink(expr)) => {
|
||||||
return Some((expr, *var));
|
return Some(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1119,6 +1131,7 @@ pub struct Env<'a, 'i> {
|
||||||
pub target_info: TargetInfo,
|
pub target_info: TargetInfo,
|
||||||
pub update_mode_ids: &'i mut UpdateModeIds,
|
pub update_mode_ids: &'i mut UpdateModeIds,
|
||||||
pub call_specialization_counter: u32,
|
pub call_specialization_counter: u32,
|
||||||
|
pub abilities_store: &'i AbilitiesStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Env<'a, 'i> {
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
@ -4218,10 +4231,14 @@ pub fn with_hole<'a>(
|
||||||
// a proc in this module, or an imported symbol
|
// a proc in this module, or an imported symbol
|
||||||
procs.partial_procs.contains_key(key)
|
procs.partial_procs.contains_key(key)
|
||||||
|| (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key))
|
|| (env.is_imported_symbol(key) && !procs.is_imported_module_thunk(key))
|
||||||
|
|| env.abilities_store.is_ability_member_name(key)
|
||||||
};
|
};
|
||||||
|
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
||||||
|
// This might be an ability member - if so, use the appropriate specialization.
|
||||||
|
let proc_name = get_specialization(env, fn_var, proc_name).unwrap_or(proc_name);
|
||||||
|
|
||||||
// a call by a known name
|
// a call by a known name
|
||||||
call_by_name(
|
call_by_name(
|
||||||
env,
|
env,
|
||||||
|
@ -4330,8 +4347,30 @@ pub fn with_hole<'a>(
|
||||||
unreachable!("calling a non-closure layout")
|
unreachable!("calling a non-closure layout")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UnspecializedExpr(symbol) => match full_layout {
|
UnspecializedExpr(symbol) => match procs.partial_exprs.get(symbol).unwrap()
|
||||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
{
|
||||||
|
&PolymorphicExpr::AbilityMember(member) => {
|
||||||
|
let proc_name = get_specialization(env, fn_var, member).expect("Recorded as an ability member, but it doesn't have a specialization");
|
||||||
|
|
||||||
|
// a call by a known name
|
||||||
|
return call_by_name(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
proc_name,
|
||||||
|
loc_args,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PolymorphicExpr::Expr(lambda_expr, lambda_expr_var) => {
|
||||||
|
match full_layout {
|
||||||
|
RawFunctionLayout::Function(
|
||||||
|
arg_layouts,
|
||||||
|
lambda_set,
|
||||||
|
ret_layout,
|
||||||
|
) => {
|
||||||
let closure_data_symbol = env.unique_symbol();
|
let closure_data_symbol = env.unique_symbol();
|
||||||
|
|
||||||
result = match_on_lambda_set(
|
result = match_on_lambda_set(
|
||||||
|
@ -4345,15 +4384,12 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (lambda_expr, lambda_expr_var) =
|
|
||||||
procs.partial_exprs.get(symbol).unwrap();
|
|
||||||
|
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
let cache_snapshot = layout_cache.snapshot();
|
||||||
let _unified = roc_unify::unify::unify(
|
let _unified = roc_unify::unify::unify(
|
||||||
env.subs,
|
env.subs,
|
||||||
fn_var,
|
fn_var,
|
||||||
lambda_expr_var,
|
*lambda_expr_var,
|
||||||
roc_unify::unify::Mode::EQ,
|
roc_unify::unify::Mode::EQ,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4372,6 +4408,8 @@ pub fn with_hole<'a>(
|
||||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
unreachable!("calling a non-closure layout")
|
unreachable!("calling a non-closure layout")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
NotASymbol => {
|
NotASymbol => {
|
||||||
// the expression is not a symbol. That means it's an expression
|
// the expression is not a symbol. That means it's an expression
|
||||||
|
@ -4705,6 +4743,43 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_specialization<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
symbol_var: Variable,
|
||||||
|
symbol: Symbol,
|
||||||
|
) -> Option<Symbol> {
|
||||||
|
use roc_solve::ability::type_implementing_member;
|
||||||
|
use roc_unify::unify::unify;
|
||||||
|
|
||||||
|
match env.abilities_store.member_def(symbol) {
|
||||||
|
None => {
|
||||||
|
// This is not an ability member, it doesn't need specialization.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Some(member) => {
|
||||||
|
let snapshot = env.subs.snapshot();
|
||||||
|
let (_, must_implement_ability) = unify(
|
||||||
|
env.subs,
|
||||||
|
symbol_var,
|
||||||
|
member.signature_var,
|
||||||
|
roc_unify::unify::Mode::EQ,
|
||||||
|
)
|
||||||
|
.expect_success("This typechecked previously");
|
||||||
|
env.subs.rollback_to(snapshot);
|
||||||
|
let specializing_type =
|
||||||
|
type_implementing_member(&must_implement_ability, member.parent_ability);
|
||||||
|
|
||||||
|
let specialization = env
|
||||||
|
.abilities_store
|
||||||
|
.get_specialization(symbol, specializing_type)
|
||||||
|
.expect("No specialization is recorded - I thought there would only be a type error here.");
|
||||||
|
|
||||||
|
Some(specialization.symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn construct_closure_data<'a>(
|
fn construct_closure_data<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -5621,9 +5696,10 @@ pub fn from_can<'a>(
|
||||||
// At the definition site `n = 1` we only know `1` to have the type `[Int *]`,
|
// At the definition site `n = 1` we only know `1` to have the type `[Int *]`,
|
||||||
// which won't be refined until the call `asU8 n`. Add it as a partial expression
|
// which won't be refined until the call `asU8 n`. Add it as a partial expression
|
||||||
// that will be specialized at each concrete usage site.
|
// that will be specialized at each concrete usage site.
|
||||||
procs
|
procs.partial_exprs.insert(
|
||||||
.partial_exprs
|
*symbol,
|
||||||
.insert(*symbol, def.loc_expr.value, def.expr_var);
|
PolymorphicExpr::Expr(def.loc_expr.value, def.expr_var),
|
||||||
|
);
|
||||||
|
|
||||||
let result = from_can(env, variable, cont.value, procs, layout_cache);
|
let result = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
|
@ -6329,7 +6405,7 @@ fn store_pattern_help<'a>(
|
||||||
|
|
||||||
match can_pat {
|
match can_pat {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
if let Some((_, var)) = procs.partial_exprs.get(outer_symbol) {
|
if let Some(&PolymorphicExpr::Expr(_, var)) = procs.partial_exprs.get(outer_symbol) {
|
||||||
// It might be the case that symbol we're storing hasn't been reified to a value
|
// It might be the case that symbol we're storing hasn't been reified to a value
|
||||||
// yet, if it's polymorphic. Do that now.
|
// yet, if it's polymorphic. Do that now.
|
||||||
stmt = specialize_symbol(
|
stmt = specialize_symbol(
|
||||||
|
@ -6727,6 +6803,13 @@ fn handle_variable_aliasing<'a, BuildRest>(
|
||||||
where
|
where
|
||||||
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
BuildRest: FnOnce(&mut Env<'a, '_>, &mut Procs<'a>, &mut LayoutCache<'a>) -> Stmt<'a>,
|
||||||
{
|
{
|
||||||
|
if env.abilities_store.is_ability_member_name(right) {
|
||||||
|
procs
|
||||||
|
.partial_exprs
|
||||||
|
.insert(left, PolymorphicExpr::AbilityMember(right));
|
||||||
|
return build_rest(env, procs, layout_cache);
|
||||||
|
}
|
||||||
|
|
||||||
if procs.partial_exprs.contains(right) {
|
if procs.partial_exprs.contains(right) {
|
||||||
// If `right` links to a partial expression, make sure we link `left` to it as well, so
|
// If `right` links to a partial expression, make sure we link `left` to it as well, so
|
||||||
// that usages of it will be specialized when building the rest of the program.
|
// that usages of it will be specialized when building the rest of the program.
|
||||||
|
@ -6804,7 +6887,7 @@ fn specialize_symbol<'a>(
|
||||||
result: Stmt<'a>,
|
result: Stmt<'a>,
|
||||||
original: Symbol,
|
original: Symbol,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
if let Some((expr, expr_var)) = procs.partial_exprs.get(original) {
|
if let Some(PolymorphicExpr::Expr(expr, expr_var)) = procs.partial_exprs.get(original) {
|
||||||
// Specialize the expression type now, based off the `arg_var` we've been given.
|
// Specialize the expression type now, based off the `arg_var` we've been given.
|
||||||
// TODO: cache the specialized result
|
// TODO: cache the specialized result
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
|
@ -6812,14 +6895,14 @@ fn specialize_symbol<'a>(
|
||||||
let _unified = roc_unify::unify::unify(
|
let _unified = roc_unify::unify::unify(
|
||||||
env.subs,
|
env.subs,
|
||||||
arg_var.unwrap(),
|
arg_var.unwrap(),
|
||||||
expr_var,
|
*expr_var,
|
||||||
roc_unify::unify::Mode::EQ,
|
roc_unify::unify::Mode::EQ,
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = with_hole(
|
let result = with_hole(
|
||||||
env,
|
env,
|
||||||
expr.clone(),
|
expr.clone(),
|
||||||
expr_var,
|
*expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
symbol,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::{Category, PatternCategory};
|
use roc_types::types::{Category, PatternCategory};
|
||||||
use roc_unify::unify::MustImplementAbility;
|
use roc_unify::unify::MustImplementAbility;
|
||||||
|
use roc_unify::unify::MustImplementConstraints;
|
||||||
|
|
||||||
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
use crate::solve::{IncompleteAbilityImplementation, TypeError};
|
||||||
|
|
||||||
|
@ -18,17 +20,14 @@ pub enum AbilityImplError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DeferredMustImplementAbility(Vec<(Vec<MustImplementAbility>, AbilityImplError)>);
|
pub struct DeferredMustImplementAbility(Vec<(MustImplementConstraints, AbilityImplError)>);
|
||||||
|
|
||||||
impl DeferredMustImplementAbility {
|
impl DeferredMustImplementAbility {
|
||||||
pub fn add(&mut self, must_implement: Vec<MustImplementAbility>, on_error: AbilityImplError) {
|
pub fn add(&mut self, must_implement: MustImplementConstraints, on_error: AbilityImplError) {
|
||||||
self.0.push((must_implement, on_error));
|
self.0.push((must_implement, on_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(self, subs: &mut Subs, abilities_store: &AbilitiesStore) -> Vec<TypeError> {
|
pub fn check(self, subs: &mut Subs, abilities_store: &AbilitiesStore) -> Vec<TypeError> {
|
||||||
// Two passes here. First up let's build up records of what types fully implement
|
|
||||||
// abilities, and what specializations are available/missing for the ones that don't.
|
|
||||||
// Use a vec since these lists should usually be pretty small.
|
|
||||||
let mut good = vec![];
|
let mut good = vec![];
|
||||||
let mut bad = vec![];
|
let mut bad = vec![];
|
||||||
|
|
||||||
|
@ -45,8 +44,21 @@ impl DeferredMustImplementAbility {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mias, _) in self.0.iter() {
|
let mut problems = vec![];
|
||||||
for &mia @ MustImplementAbility { typ, ability } in mias {
|
|
||||||
|
// Keep track of which types that have an incomplete ability were reported as part of
|
||||||
|
// another type error (from an expression or pattern). If we reported an error for a type
|
||||||
|
// that doesn't implement an ability in that context, we don't want to repeat the error
|
||||||
|
// message.
|
||||||
|
let mut reported_in_context = vec![];
|
||||||
|
let mut incomplete_not_in_context = vec![];
|
||||||
|
|
||||||
|
for (constraints, on_error) in self.0.into_iter() {
|
||||||
|
let must_implement = constraints.get_unique();
|
||||||
|
|
||||||
|
// First off, make sure we populate information about which of the "must implement"
|
||||||
|
// constraints are met, and which aren't.
|
||||||
|
for &mia @ MustImplementAbility { typ, ability } in must_implement.iter() {
|
||||||
if is_good!(&mia) || get_bad!(mia).is_some() {
|
if is_good!(&mia) || get_bad!(mia).is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -80,22 +92,16 @@ impl DeferredMustImplementAbility {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Now figure out what errors we need to report.
|
// Now, figure out what errors we need to report.
|
||||||
let mut problems = vec![];
|
|
||||||
|
|
||||||
// Keep track of which types that have an incomplete ability were reported as part of
|
|
||||||
// another type error (from an expression or pattern). If we reported an error for a type
|
|
||||||
// that doesn't implement an ability in that context, we don't want to repeat the error
|
|
||||||
// message.
|
|
||||||
let mut reported_in_context = vec![];
|
|
||||||
let mut incomplete_not_in_context = vec![];
|
|
||||||
|
|
||||||
for (must_implement, on_error) in self.0.into_iter() {
|
|
||||||
use AbilityImplError::*;
|
use AbilityImplError::*;
|
||||||
match on_error {
|
match on_error {
|
||||||
IncompleteAbility => {
|
IncompleteAbility => {
|
||||||
|
// These aren't attached to another type error, so if these must_implement
|
||||||
|
// constraints aren't met, we'll emit a generic "this type doesn't implement an
|
||||||
|
// ability" error message at the end. We only want to do this if it turns out
|
||||||
|
// the "must implement" constraint indeed wasn't part of a more specific type
|
||||||
|
// error.
|
||||||
incomplete_not_in_context.extend(must_implement);
|
incomplete_not_in_context.extend(must_implement);
|
||||||
}
|
}
|
||||||
BadExpr(region, category, var) => {
|
BadExpr(region, category, var) => {
|
||||||
|
@ -138,9 +144,11 @@ impl DeferredMustImplementAbility {
|
||||||
reported_in_context.extend(must_implement);
|
reported_in_context.extend(must_implement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go through and attach generic "type does not implement ability" errors, if they were not
|
||||||
|
// part of a larger context.
|
||||||
for mia in incomplete_not_in_context.into_iter() {
|
for mia in incomplete_not_in_context.into_iter() {
|
||||||
if let Some(must_implement) = get_bad!(mia) {
|
if let Some(must_implement) = get_bad!(mia) {
|
||||||
if !reported_in_context.contains(&mia) {
|
if !reported_in_context.contains(&mia) {
|
||||||
|
@ -154,3 +162,29 @@ impl DeferredMustImplementAbility {
|
||||||
problems
|
problems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines what type implements an ability member of a specialized signature, given the
|
||||||
|
/// [MustImplementAbility] constraints of the signature.
|
||||||
|
pub fn type_implementing_member(
|
||||||
|
specialization_must_implement_constraints: &MustImplementConstraints,
|
||||||
|
ability: Symbol,
|
||||||
|
) -> Symbol {
|
||||||
|
debug_assert_eq!({
|
||||||
|
let ability_implementations_for_specialization =
|
||||||
|
specialization_must_implement_constraints
|
||||||
|
.clone()
|
||||||
|
.get_unique();
|
||||||
|
|
||||||
|
ability_implementations_for_specialization.len()
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
||||||
|
specialization_must_implement_constraints
|
||||||
|
);
|
||||||
|
|
||||||
|
specialization_must_implement_constraints
|
||||||
|
.iter_for_ability(ability)
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.typ
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
|
||||||
mod ability;
|
pub mod ability;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod solve;
|
pub mod solve;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ability::{AbilityImplError, DeferredMustImplementAbility};
|
use crate::ability::{type_implementing_member, AbilityImplError, DeferredMustImplementAbility};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
||||||
use roc_can::constraint::Constraint::{self, *};
|
use roc_can::constraint::Constraint::{self, *};
|
||||||
|
@ -703,7 +703,6 @@ fn solve(
|
||||||
check_ability_specialization(
|
check_ability_specialization(
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
&new_env,
|
|
||||||
pools,
|
pools,
|
||||||
rank,
|
rank,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -812,7 +811,6 @@ fn solve(
|
||||||
check_ability_specialization(
|
check_ability_specialization(
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
&new_env,
|
|
||||||
pools,
|
pools,
|
||||||
rank,
|
rank,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
|
@ -1282,7 +1280,6 @@ fn solve(
|
||||||
fn check_ability_specialization(
|
fn check_ability_specialization(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
env: &Env,
|
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
abilities_store: &mut AbilitiesStore,
|
abilities_store: &mut AbilitiesStore,
|
||||||
|
@ -1295,9 +1292,7 @@ fn check_ability_specialization(
|
||||||
// inferred type for the specialization actually aligns with the expected
|
// inferred type for the specialization actually aligns with the expected
|
||||||
// implementation.
|
// implementation.
|
||||||
if let Some((root_symbol, root_data)) = abilities_store.root_name_and_def(symbol) {
|
if let Some((root_symbol, root_data)) = abilities_store.root_name_and_def(symbol) {
|
||||||
let root_signature_var = env
|
let root_signature_var = root_data.signature_var;
|
||||||
.get_var_by_symbol(&root_symbol)
|
|
||||||
.expect("Ability should be registered in env by now!");
|
|
||||||
|
|
||||||
// Check if they unify - if they don't, then the claimed specialization isn't really one,
|
// Check if they unify - if they don't, then the claimed specialization isn't really one,
|
||||||
// and that's a type error!
|
// and that's a type error!
|
||||||
|
@ -1351,16 +1346,8 @@ fn check_ability_specialization(
|
||||||
|
|
||||||
// First, figure out and register for what type does this symbol specialize
|
// First, figure out and register for what type does this symbol specialize
|
||||||
// the ability member.
|
// the ability member.
|
||||||
let mut ability_implementations_for_specialization = must_implement_ability
|
let specialization_type =
|
||||||
.iter()
|
type_implementing_member(&must_implement_ability, root_data.parent_ability);
|
||||||
.filter(|mia| mia.ability == root_data.parent_ability)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
ability_implementations_for_specialization.dedup();
|
|
||||||
|
|
||||||
debug_assert!(ability_implementations_for_specialization.len() == 1, "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization");
|
|
||||||
|
|
||||||
// This is a valid specialization! Record it.
|
|
||||||
let specialization_type = ability_implementations_for_specialization[0].typ;
|
|
||||||
let specialization = MemberSpecialization {
|
let specialization = MemberSpecialization {
|
||||||
symbol,
|
symbol,
|
||||||
region: symbol_loc_var.region,
|
region: symbol_loc_var.region,
|
||||||
|
|
|
@ -5913,4 +5913,23 @@ mod solve_expr {
|
||||||
"U64",
|
"U64",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_ability_member() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ thething ] to "./platform"
|
||||||
|
|
||||||
|
Hash has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
thething =
|
||||||
|
itis = hash
|
||||||
|
itis
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"a -> U64 | a has Hash",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
86
compiler/test_gen/src/gen_abilities.rs
Normal file
86
compiler/test_gen/src/gen_abilities.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#[cfg(feature = "gen-llvm")]
|
||||||
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-dev")]
|
||||||
|
use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-wasm")]
|
||||||
|
use crate::helpers::wasm::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn hash_specialization() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
Hash has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
Id := U64
|
||||||
|
|
||||||
|
hash = \$Id n -> n
|
||||||
|
|
||||||
|
main = hash ($Id 1234)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1234,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn hash_specialization_multiple_add() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
Hash has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
Id := U64
|
||||||
|
|
||||||
|
hash = \$Id n -> n
|
||||||
|
|
||||||
|
One := {}
|
||||||
|
|
||||||
|
hash = \$One _ -> 1
|
||||||
|
|
||||||
|
main = hash ($Id 1234) + hash ($One {})
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1235,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn alias_member_specialization() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
Hash has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
Id := U64
|
||||||
|
|
||||||
|
hash = \$Id n -> n
|
||||||
|
|
||||||
|
main =
|
||||||
|
aliasedHash = hash
|
||||||
|
aliasedHash ($Id 1234)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
1234,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
// we actually want to compare against the literal float bits
|
// we actually want to compare against the literal float bits
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
|
|
||||||
|
pub mod gen_abilities;
|
||||||
pub mod gen_compare;
|
pub mod gen_compare;
|
||||||
pub mod gen_dict;
|
pub mod gen_dict;
|
||||||
pub mod gen_list;
|
pub mod gen_list;
|
||||||
|
|
7
compiler/test_mono/generated/specialize_ability_call.txt
Normal file
7
compiler/test_mono/generated/specialize_ability_call.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
procedure Test.5 (Test.8):
|
||||||
|
ret Test.8;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.10 : U64 = 1234i64;
|
||||||
|
let Test.9 : U64 = CallByName Test.5 Test.10;
|
||||||
|
ret Test.9;
|
|
@ -1294,6 +1294,25 @@ fn issue_2811() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn specialize_ability_call() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
Hash has
|
||||||
|
hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
Id := U64
|
||||||
|
|
||||||
|
hash : Id -> U64
|
||||||
|
hash = \$Id n -> n
|
||||||
|
|
||||||
|
main = hash ($Id 1234)
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// #[ignore]
|
// #[ignore]
|
||||||
// #[mono_test]
|
// #[mono_test]
|
||||||
// fn static_str_closure() {
|
// fn static_str_closure() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use roc_error_macros::todo_abilities;
|
use roc_error_macros::{internal_error, todo_abilities};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_types::subs::Content::{self, *};
|
use roc_types::subs::Content::{self, *};
|
||||||
|
@ -134,14 +134,26 @@ pub struct Context {
|
||||||
pub enum Unified {
|
pub enum Unified {
|
||||||
Success {
|
Success {
|
||||||
vars: Pool,
|
vars: Pool,
|
||||||
must_implement_ability: Vec<MustImplementAbility>,
|
must_implement_ability: MustImplementConstraints,
|
||||||
},
|
},
|
||||||
Failure(Pool, ErrorType, ErrorType, DoesNotImplementAbility),
|
Failure(Pool, ErrorType, ErrorType, DoesNotImplementAbility),
|
||||||
BadType(Pool, roc_types::types::Problem),
|
BadType(Pool, roc_types::types::Problem),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Unified {
|
||||||
|
pub fn expect_success(self, err_msg: &'static str) -> (Pool, MustImplementConstraints) {
|
||||||
|
match self {
|
||||||
|
Unified::Success {
|
||||||
|
vars,
|
||||||
|
must_implement_ability,
|
||||||
|
} => (vars, must_implement_ability),
|
||||||
|
_ => internal_error!("{}", err_msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Specifies that `type` must implement the ability `ability`.
|
/// Specifies that `type` must implement the ability `ability`.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct MustImplementAbility {
|
pub struct MustImplementAbility {
|
||||||
// This only points to opaque type names currently.
|
// This only points to opaque type names currently.
|
||||||
// TODO(abilities) support structural types in general
|
// TODO(abilities) support structural types in general
|
||||||
|
@ -149,12 +161,39 @@ pub struct MustImplementAbility {
|
||||||
pub ability: Symbol,
|
pub ability: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct MustImplementConstraints(Vec<MustImplementAbility>);
|
||||||
|
|
||||||
|
impl MustImplementConstraints {
|
||||||
|
pub fn push(&mut self, must_implement: MustImplementAbility) {
|
||||||
|
self.0.push(must_implement)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, other: Self) {
|
||||||
|
self.0.extend(other.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_unique(mut self) -> Vec<MustImplementAbility> {
|
||||||
|
self.0.sort();
|
||||||
|
self.0.dedup();
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_for_ability(&self, ability: Symbol) -> impl Iterator<Item = &MustImplementAbility> {
|
||||||
|
self.0.iter().filter(move |mia| mia.ability == ability)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Outcome {
|
pub struct Outcome {
|
||||||
mismatches: Vec<Mismatch>,
|
mismatches: Vec<Mismatch>,
|
||||||
/// We defer these checks until the end of a solving phase.
|
/// We defer these checks until the end of a solving phase.
|
||||||
/// NOTE: this vector is almost always empty!
|
/// NOTE: this vector is almost always empty!
|
||||||
must_implement_ability: Vec<MustImplementAbility>,
|
must_implement_ability: MustImplementConstraints,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Outcome {
|
impl Outcome {
|
||||||
|
|
|
@ -125,6 +125,7 @@ mod test_reporting {
|
||||||
mut solved,
|
mut solved,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
mut declarations_by_id,
|
mut declarations_by_id,
|
||||||
|
abilities_store,
|
||||||
..
|
..
|
||||||
} = result?;
|
} = result?;
|
||||||
|
|
||||||
|
@ -177,6 +178,7 @@ mod test_reporting {
|
||||||
target_info,
|
target_info,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
|
abilities_store: &abilities_store,
|
||||||
};
|
};
|
||||||
let _mono_expr = Stmt::new(
|
let _mono_expr = Stmt::new(
|
||||||
&mut mono_env,
|
&mut mono_env,
|
||||||
|
@ -334,6 +336,7 @@ mod test_reporting {
|
||||||
target_info,
|
target_info,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
|
abilities_store: &abilities_store,
|
||||||
};
|
};
|
||||||
let _mono_expr = Stmt::new(
|
let _mono_expr = Stmt::new(
|
||||||
&mut mono_env,
|
&mut mono_env,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue