Merge branch 'main' into clippy-1.74

This commit is contained in:
Ayaz 2023-12-02 20:09:06 -06:00 committed by GitHub
commit aaba3f4d82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
320 changed files with 11155 additions and 18862 deletions

View file

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

View file

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