mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-15 17:40:16 +00:00
Merge branch 'main' into clippy-1.74
This commit is contained in:
commit
aaba3f4d82
320 changed files with 11155 additions and 18862 deletions
|
@ -304,6 +304,13 @@ impl ObligationCache {
|
|||
|
||||
Symbol::BOOL_EQ => Some(DeriveEq::is_derivable(self, abilities_store, subs, var)),
|
||||
|
||||
Symbol::INSPECT_INSPECT_ABILITY => Some(DeriveInspect::is_derivable(
|
||||
self,
|
||||
abilities_store,
|
||||
subs,
|
||||
var,
|
||||
)),
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -373,7 +380,10 @@ impl ObligationCache {
|
|||
|
||||
let ImplKey { opaque, ability } = impl_key;
|
||||
|
||||
let has_declared_impl = abilities_store.has_declared_implementation(opaque, ability);
|
||||
// Every type has the Inspect ability automatically, even opaques with no `implements` declaration.
|
||||
let is_inspect = ability == Symbol::INSPECT_INSPECT_ABILITY;
|
||||
let has_known_impl =
|
||||
is_inspect || abilities_store.has_declared_implementation(opaque, ability);
|
||||
|
||||
// Some builtins, like Float32 and Bool, would have a cyclic dependency on Encode/Decode/etc.
|
||||
// if their Roc implementations explicitly defined some abilities they support.
|
||||
|
@ -382,18 +392,17 @@ impl ObligationCache {
|
|||
DeriveDecoding::ABILITY => DeriveDecoding::is_derivable_builtin_opaque(opaque),
|
||||
DeriveEq::ABILITY => DeriveEq::is_derivable_builtin_opaque(opaque),
|
||||
DeriveHash::ABILITY => DeriveHash::is_derivable_builtin_opaque(opaque),
|
||||
DeriveInspect::ABILITY => DeriveInspect::is_derivable_builtin_opaque(opaque),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let has_declared_impl = has_declared_impl || builtin_opaque_impl_ok();
|
||||
|
||||
let obligation_result = if !has_declared_impl {
|
||||
let obligation_result = if has_known_impl || builtin_opaque_impl_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Unfulfilled::OpaqueDoesNotImplement {
|
||||
typ: opaque,
|
||||
ability,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
self.impl_cache.insert(impl_key, obligation_result);
|
||||
|
@ -849,6 +858,93 @@ trait DerivableVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
struct DeriveInspect;
|
||||
impl DerivableVisitor for DeriveInspect {
|
||||
const ABILITY: Symbol = Symbol::INSPECT_INSPECT_ABILITY;
|
||||
const ABILITY_SLICE: SubsSlice<Symbol> = Subs::AB_INSPECT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(_: Symbol) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_recursion(_var: Variable) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_apply(_: Variable, _: Symbol) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_record(
|
||||
_subs: &Subs,
|
||||
_var: Variable,
|
||||
_fields: RecordFields,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_tuple(
|
||||
_subs: &Subs,
|
||||
_var: Variable,
|
||||
_elems: TupleElems,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_recursive_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_function_or_tag_union(_var: Variable) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_empty_record(_var: Variable) -> Result<(), NotDerivable> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_empty_tag_union(_var: Variable) -> Result<(), NotDerivable> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_alias(_var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> {
|
||||
if is_builtin_number_alias(symbol) {
|
||||
Ok(Descend(false))
|
||||
} else {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_ranged_number(_var: Variable, _range: NumericRange) -> Result<(), NotDerivable> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_floating_point_content(
|
||||
_var: Variable,
|
||||
_subs: &mut Subs,
|
||||
_content_var: Variable,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
Ok(Descend(false))
|
||||
}
|
||||
}
|
||||
|
||||
struct DeriveEncoding;
|
||||
impl DerivableVisitor for DeriveEncoding {
|
||||
const ABILITY: Symbol = Symbol::ENCODE_ENCODING;
|
||||
|
@ -1392,7 +1488,7 @@ impl AbilityResolver for AbilitiesStore {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether this a module whose types' ability implementations should be checked via derive_key,
|
||||
/// Whether this is a module whose types' ability implementations should be checked via derive_key,
|
||||
/// because they do not explicitly list ability implementations due to circular dependencies.
|
||||
#[inline]
|
||||
pub(crate) fn builtin_module_with_unlisted_ability_impl(module_id: ModuleId) -> bool {
|
||||
|
|
|
@ -628,29 +628,7 @@ fn make_specialization_decision<P: Phase>(
|
|||
} else {
|
||||
// Solving within a module.
|
||||
phase.with_module_abilities_store(opaque.module_id(), |abilities_store| {
|
||||
let impl_key = ImplKey {
|
||||
opaque: *opaque,
|
||||
ability_member,
|
||||
};
|
||||
match abilities_store.get_implementation(impl_key) {
|
||||
None => {
|
||||
// Doesn't specialize; an error will already be reported for this.
|
||||
SpecializeDecision::Drop
|
||||
}
|
||||
Some(MemberImpl::Error) => {
|
||||
// TODO: probably not right, we may want to choose a derive decision!
|
||||
SpecializeDecision::Specialize(Opaque(*opaque))
|
||||
}
|
||||
Some(MemberImpl::Impl(specialization_symbol)) => {
|
||||
match abilities_store.specialization_info(*specialization_symbol) {
|
||||
Some(_) => SpecializeDecision::Specialize(Opaque(*opaque)),
|
||||
|
||||
// If we expect a specialization impl but don't yet know it, we must hold off
|
||||
// compacting the lambda set until the specialization is well-known.
|
||||
None => SpecializeDecision::PendingSpecialization(impl_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
make_ability_specialization_decision(*opaque, ability_member, abilities_store)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -698,6 +676,46 @@ fn make_specialization_decision<P: Phase>(
|
|||
}
|
||||
}
|
||||
|
||||
fn make_ability_specialization_decision(
|
||||
opaque: Symbol,
|
||||
ability_member: Symbol,
|
||||
abilities_store: &AbilitiesStore,
|
||||
) -> SpecializeDecision {
|
||||
use SpecializationTypeKey::*;
|
||||
let impl_key = ImplKey {
|
||||
opaque,
|
||||
ability_member,
|
||||
};
|
||||
match abilities_store.get_implementation(impl_key) {
|
||||
None => {
|
||||
match ability_member {
|
||||
// Inspect is special - if there is no implementation for the
|
||||
// opaque type, we always emit a default implementation.
|
||||
Symbol::INSPECT_TO_INSPECTOR => {
|
||||
SpecializeDecision::Specialize(Immediate(Symbol::INSPECT_OPAQUE))
|
||||
}
|
||||
_ => {
|
||||
// Doesn't specialize; an error will already be reported for this.
|
||||
SpecializeDecision::Drop
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(MemberImpl::Error) => {
|
||||
// TODO: probably not right, we may want to choose a derive decision!
|
||||
SpecializeDecision::Specialize(Opaque(opaque))
|
||||
}
|
||||
Some(MemberImpl::Impl(specialization_symbol)) => {
|
||||
match abilities_store.specialization_info(*specialization_symbol) {
|
||||
Some(_) => SpecializeDecision::Specialize(Opaque(opaque)),
|
||||
|
||||
// If we expect a specialization impl but don't yet know it, we must hold off
|
||||
// compacting the lambda set until the specialization is well-known.
|
||||
None => SpecializeDecision::PendingSpecialization(impl_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
||||
subs: &mut Subs,
|
||||
|
@ -705,115 +723,155 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
|||
phase: &P,
|
||||
ability_member: Symbol,
|
||||
lset_region: u8,
|
||||
specialization_key: SpecializationTypeKey,
|
||||
mut specialization_key: SpecializationTypeKey,
|
||||
target_rank: Rank,
|
||||
) -> Result<Variable, ()> {
|
||||
match specialization_key {
|
||||
SpecializationTypeKey::Opaque(opaque) => {
|
||||
let opaque_home = opaque.module_id();
|
||||
let external_specialized_lset =
|
||||
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
loop {
|
||||
match specialization_key {
|
||||
SpecializationTypeKey::Opaque(opaque) => {
|
||||
let opaque_home = opaque.module_id();
|
||||
let found = phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||
find_opaque_specialization_ambient_function(
|
||||
abilities_store,
|
||||
opaque,
|
||||
ability_member,
|
||||
};
|
||||
lset_region,
|
||||
)
|
||||
});
|
||||
|
||||
let opt_specialization =
|
||||
abilities_store.get_implementation(impl_key);
|
||||
match opt_specialization {
|
||||
None => {
|
||||
if P::IS_LATE {
|
||||
internal_error!(
|
||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||
opaque,
|
||||
ability_member
|
||||
);
|
||||
} else {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
Some(member_impl) => match member_impl {
|
||||
MemberImpl::Impl(spec_symbol) => {
|
||||
let specialization =
|
||||
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||
|
||||
let specialized_lambda_set = *specialization
|
||||
.specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.unwrap_or_else(|| panic!("lambda set region not resolved: {:?}", (spec_symbol, specialization)));
|
||||
Ok(specialized_lambda_set)
|
||||
}
|
||||
MemberImpl::Error => todo_abilities!(),
|
||||
},
|
||||
let external_specialized_lset = match found {
|
||||
FoundOpaqueSpecialization::UpdatedSpecializationKey(key) => {
|
||||
specialization_key = key;
|
||||
continue;
|
||||
}
|
||||
})?;
|
||||
FoundOpaqueSpecialization::AmbientFunction(lset) => lset,
|
||||
FoundOpaqueSpecialization::NotFound => {
|
||||
if P::IS_LATE {
|
||||
internal_error!(
|
||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||
opaque,
|
||||
ability_member
|
||||
);
|
||||
} else {
|
||||
// We'll have reported an error for this.
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
|
||||
external_specialized_lset,
|
||||
opaque_home,
|
||||
subs,
|
||||
);
|
||||
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
|
||||
external_specialized_lset,
|
||||
opaque_home,
|
||||
subs,
|
||||
);
|
||||
|
||||
Ok(specialized_ambient)
|
||||
}
|
||||
return Ok(specialized_ambient);
|
||||
}
|
||||
|
||||
SpecializationTypeKey::Derived(derive_key) => {
|
||||
let mut derived_module = derived_env.derived_module.lock().unwrap();
|
||||
SpecializationTypeKey::Derived(derive_key) => {
|
||||
let mut derived_module = derived_env.derived_module.lock().unwrap();
|
||||
|
||||
let (_, _, specialization_lambda_sets) =
|
||||
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
|
||||
let (_, _, specialization_lambda_sets) =
|
||||
derived_module.get_or_insert(derived_env.exposed_types, derive_key);
|
||||
|
||||
let specialized_lambda_set = *specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.expect("lambda set region not resolved");
|
||||
let specialized_lambda_set = *specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.expect("lambda set region not resolved");
|
||||
|
||||
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
|
||||
specialized_lambda_set,
|
||||
subs,
|
||||
target_rank,
|
||||
);
|
||||
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
|
||||
specialized_lambda_set,
|
||||
subs,
|
||||
target_rank,
|
||||
);
|
||||
|
||||
Ok(specialized_ambient)
|
||||
}
|
||||
return Ok(specialized_ambient);
|
||||
}
|
||||
|
||||
SpecializationTypeKey::Immediate(imm) => {
|
||||
// Immediates are like opaques in that we can simply look up their type definition in
|
||||
// the ability store, there is nothing new to synthesize.
|
||||
//
|
||||
// THEORY: if something can become an immediate, it will always be available in the
|
||||
// local ability store, because the transformation is local (?)
|
||||
//
|
||||
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
|
||||
// since immediates can only refer to builtins - and in userspace, all builtin types
|
||||
// are available in `exposed_types`.
|
||||
let immediate_lambda_set_at_region =
|
||||
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
|
||||
SpecializationTypeKey::Immediate(imm) => {
|
||||
// Immediates are like opaques in that we can simply look up their type definition in
|
||||
// the ability store, there is nothing new to synthesize.
|
||||
//
|
||||
// THEORY: if something can become an immediate, it will always be available in the
|
||||
// local ability store, because the transformation is local (?)
|
||||
//
|
||||
// TODO: I actually think we can get what we need here by examining `derived_env.exposed_types`,
|
||||
// since immediates can only refer to builtins - and in userspace, all builtin types
|
||||
// are available in `exposed_types`.
|
||||
let immediate_lambda_set_at_region =
|
||||
phase.get_and_copy_ability_member_ambient_function(imm, lset_region, subs);
|
||||
|
||||
Ok(immediate_lambda_set_at_region)
|
||||
}
|
||||
return Ok(immediate_lambda_set_at_region);
|
||||
}
|
||||
|
||||
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
|
||||
let module_id = imm.module_id();
|
||||
debug_assert!(module_id.is_builtin());
|
||||
SpecializationTypeKey::SingleLambdaSetImmediate(imm) => {
|
||||
let module_id = imm.module_id();
|
||||
debug_assert!(module_id.is_builtin());
|
||||
|
||||
let module_types = &derived_env
|
||||
.exposed_types
|
||||
.get(&module_id)
|
||||
.unwrap()
|
||||
.exposed_types_storage_subs;
|
||||
let module_types = &derived_env
|
||||
.exposed_types
|
||||
.get(&module_id)
|
||||
.unwrap()
|
||||
.exposed_types_storage_subs;
|
||||
|
||||
// Since this immediate has only one lambda set, the region must be pointing to 1, and
|
||||
// moreover the imported function type is the ambient function of the single lset.
|
||||
debug_assert_eq!(lset_region, 1);
|
||||
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
|
||||
let imported = module_types
|
||||
.storage_subs
|
||||
.export_variable_to(subs, *storage_var);
|
||||
// Since this immediate has only one lambda set, the region must be pointing to 1, and
|
||||
// moreover the imported function type is the ambient function of the single lset.
|
||||
debug_assert_eq!(lset_region, 1);
|
||||
let storage_var = module_types.stored_vars_by_symbol.get(&imm).unwrap();
|
||||
let imported = module_types
|
||||
.storage_subs
|
||||
.export_variable_to(subs, *storage_var);
|
||||
|
||||
roc_types::subs::instantiate_rigids(subs, imported.variable);
|
||||
roc_types::subs::instantiate_rigids(subs, imported.variable);
|
||||
|
||||
Ok(imported.variable)
|
||||
return Ok(imported.variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FoundOpaqueSpecialization {
|
||||
UpdatedSpecializationKey(SpecializationTypeKey),
|
||||
AmbientFunction(Variable),
|
||||
NotFound,
|
||||
}
|
||||
|
||||
fn find_opaque_specialization_ambient_function(
|
||||
abilities_store: &AbilitiesStore,
|
||||
opaque: Symbol,
|
||||
ability_member: Symbol,
|
||||
lset_region: u8,
|
||||
) -> FoundOpaqueSpecialization {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
opaque,
|
||||
ability_member,
|
||||
};
|
||||
|
||||
let opt_specialization = abilities_store.get_implementation(impl_key);
|
||||
match opt_specialization {
|
||||
None => match ability_member {
|
||||
Symbol::INSPECT_TO_INSPECTOR => FoundOpaqueSpecialization::UpdatedSpecializationKey(
|
||||
SpecializationTypeKey::Immediate(Symbol::INSPECT_OPAQUE),
|
||||
),
|
||||
_ => FoundOpaqueSpecialization::NotFound,
|
||||
},
|
||||
Some(member_impl) => match member_impl {
|
||||
MemberImpl::Impl(spec_symbol) => {
|
||||
let specialization =
|
||||
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||
|
||||
let specialized_lambda_set = *specialization
|
||||
.specialization_lambda_sets
|
||||
.get(&lset_region)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"lambda set region not resolved: {:?}",
|
||||
(spec_symbol, specialization)
|
||||
)
|
||||
});
|
||||
|
||||
FoundOpaqueSpecialization::AmbientFunction(specialized_lambda_set)
|
||||
}
|
||||
MemberImpl::Error => todo_abilities!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue