mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Merge pull request #5166 from roc-lang/fix-bool-abilities
This commit is contained in:
commit
78dea53a46
12 changed files with 293 additions and 86 deletions
|
@ -78,23 +78,11 @@ impl FlatDecodable {
|
|||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
},
|
||||
Content::Alias(sym, _, real_var, _) => match sym {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Ok(Immediate(Symbol::DECODE_U8)),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Ok(Immediate(Symbol::DECODE_U16)),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Ok(Immediate(Symbol::DECODE_U32)),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Ok(Immediate(Symbol::DECODE_U64)),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Ok(Immediate(Symbol::DECODE_U128)),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Ok(Immediate(Symbol::DECODE_I8)),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Ok(Immediate(Symbol::DECODE_I16)),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Ok(Immediate(Symbol::DECODE_I32)),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Ok(Immediate(Symbol::DECODE_I64)),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Ok(Immediate(Symbol::DECODE_I128)),
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Ok(Immediate(Symbol::DECODE_DEC)),
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::DECODE_F32)),
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Ok(Immediate(Symbol::DECODE_F64)),
|
||||
Content::Alias(sym, _, real_var, _) => match from_builtin_symbol(sym) {
|
||||
Some(lambda) => lambda,
|
||||
// NB: I believe it is okay to unwrap opaques here because derivers are only used
|
||||
// by the backend, and the backend treats opaques like structural aliases.
|
||||
_ => Self::from_var(subs, real_var),
|
||||
None => Self::from_var(subs, real_var),
|
||||
},
|
||||
Content::RangedNumber(range) => {
|
||||
Self::from_var(subs, range.default_compilation_variable())
|
||||
|
@ -109,4 +97,30 @@ impl FlatDecodable {
|
|||
Content::LambdaSet(_) => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_builtin_symbol(symbol: Symbol) -> Result<FlatDecodable, DeriveError> {
|
||||
from_builtin_symbol(symbol).unwrap_or(Err(DeriveError::Underivable))
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_builtin_symbol(symbol: Symbol) -> Option<Result<FlatDecodable, DeriveError>> {
|
||||
use FlatDecodable::*;
|
||||
match symbol {
|
||||
Symbol::BOOL_BOOL => Some(Ok(Immediate(Symbol::DECODE_BOOL))),
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Some(Ok(Immediate(Symbol::DECODE_U8))),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Some(Ok(Immediate(Symbol::DECODE_U16))),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Some(Ok(Immediate(Symbol::DECODE_U32))),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Some(Ok(Immediate(Symbol::DECODE_U64))),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Some(Ok(Immediate(Symbol::DECODE_U128))),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Some(Ok(Immediate(Symbol::DECODE_I8))),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Some(Ok(Immediate(Symbol::DECODE_I16))),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Some(Ok(Immediate(Symbol::DECODE_I32))),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Some(Ok(Immediate(Symbol::DECODE_I64))),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Some(Ok(Immediate(Symbol::DECODE_I128))),
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Some(Ok(Immediate(Symbol::DECODE_DEC))),
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Some(Ok(Immediate(Symbol::DECODE_F32))),
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Some(Ok(Immediate(Symbol::DECODE_F64))),
|
||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Some(Err(DeriveError::Underivable)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,21 +112,8 @@ impl FlatEncodable {
|
|||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
},
|
||||
Content::Alias(sym, _, real_var, _) => match sym {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Ok(Immediate(Symbol::ENCODE_U8)),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Ok(Immediate(Symbol::ENCODE_U16)),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Ok(Immediate(Symbol::ENCODE_U32)),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Ok(Immediate(Symbol::ENCODE_U64)),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Ok(Immediate(Symbol::ENCODE_U128)),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Ok(Immediate(Symbol::ENCODE_I8)),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Ok(Immediate(Symbol::ENCODE_I16)),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Ok(Immediate(Symbol::ENCODE_I32)),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Ok(Immediate(Symbol::ENCODE_I64)),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Ok(Immediate(Symbol::ENCODE_I128)),
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Ok(Immediate(Symbol::ENCODE_DEC)),
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::ENCODE_F32)),
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Ok(Immediate(Symbol::ENCODE_F64)),
|
||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Err(Underivable),
|
||||
Content::Alias(sym, _, real_var, _) => match from_builtin_symbol(sym) {
|
||||
Some(lambda) => lambda,
|
||||
// TODO: I believe it is okay to unwrap opaques here because derivers are only used
|
||||
// by the backend, and the backend treats opaques like structural aliases.
|
||||
_ => Self::from_var(subs, real_var),
|
||||
|
@ -144,4 +131,30 @@ impl FlatEncodable {
|
|||
Content::LambdaSet(_) => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_builtin_symbol(symbol: Symbol) -> Result<FlatEncodable, DeriveError> {
|
||||
from_builtin_symbol(symbol).unwrap_or(Err(DeriveError::Underivable))
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_builtin_symbol(symbol: Symbol) -> Option<Result<FlatEncodable, DeriveError>> {
|
||||
use FlatEncodable::*;
|
||||
match symbol {
|
||||
Symbol::BOOL_BOOL => Some(Ok(Immediate(Symbol::ENCODE_BOOL))),
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Some(Ok(Immediate(Symbol::ENCODE_U8))),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Some(Ok(Immediate(Symbol::ENCODE_U16))),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Some(Ok(Immediate(Symbol::ENCODE_U32))),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Some(Ok(Immediate(Symbol::ENCODE_U64))),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Some(Ok(Immediate(Symbol::ENCODE_U128))),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Some(Ok(Immediate(Symbol::ENCODE_I8))),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Some(Ok(Immediate(Symbol::ENCODE_I16))),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Some(Ok(Immediate(Symbol::ENCODE_I32))),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Some(Ok(Immediate(Symbol::ENCODE_I64))),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Some(Ok(Immediate(Symbol::ENCODE_I128))),
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Some(Ok(Immediate(Symbol::ENCODE_DEC))),
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Some(Ok(Immediate(Symbol::ENCODE_F32))),
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Some(Ok(Immediate(Symbol::ENCODE_F64))),
|
||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Some(Err(DeriveError::Underivable)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ impl FlatHash {
|
|||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
},
|
||||
Content::Alias(sym, _, real_var, _) => match num_symbol_to_hash_lambda(sym) {
|
||||
Content::Alias(sym, _, real_var, _) => match builtin_symbol_to_hash_lambda(sym) {
|
||||
Some(lambda) => Ok(lambda),
|
||||
// NB: I believe it is okay to unwrap opaques here because derivers are only used
|
||||
// by the backend, and the backend treats opaques like structural aliases.
|
||||
|
@ -129,7 +129,7 @@ impl FlatHash {
|
|||
// during monomorphization, at which point we always choose a default layout
|
||||
// for ranged numbers, without concern for reification to a ground type.
|
||||
let chosen_width = range.default_compilation_width();
|
||||
let lambda = num_symbol_to_hash_lambda(chosen_width.symbol()).unwrap();
|
||||
let lambda = builtin_symbol_to_hash_lambda(chosen_width.symbol()).unwrap();
|
||||
Ok(lambda)
|
||||
}
|
||||
//
|
||||
|
@ -143,9 +143,13 @@ impl FlatHash {
|
|||
Content::LambdaSet(_) => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_builtin_symbol(symbol: Symbol) -> Result<FlatHash, DeriveError> {
|
||||
builtin_symbol_to_hash_lambda(symbol).ok_or(DeriveError::Underivable)
|
||||
}
|
||||
}
|
||||
|
||||
const fn num_symbol_to_hash_lambda(symbol: Symbol) -> Option<FlatHash> {
|
||||
const fn builtin_symbol_to_hash_lambda(symbol: Symbol) -> Option<FlatHash> {
|
||||
use FlatHash::*;
|
||||
match symbol {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => {
|
||||
|
|
|
@ -52,7 +52,7 @@ impl DeriveKey {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Debug)]
|
||||
#[derive(Hash, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Derived {
|
||||
/// If a derived implementation name is well-known ahead-of-time, we can inline the symbol
|
||||
/// directly rather than associating a key for an implementation to be made later on.
|
||||
|
@ -123,4 +123,34 @@ impl Derived {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn builtin_with_builtin_symbol(
|
||||
builtin: DeriveBuiltin,
|
||||
symbol: Symbol,
|
||||
) -> Result<Self, DeriveError> {
|
||||
match builtin {
|
||||
DeriveBuiltin::ToEncoder => match encoding::FlatEncodable::from_builtin_symbol(symbol)?
|
||||
{
|
||||
FlatEncodable::Immediate(imm) => Ok(Derived::Immediate(imm)),
|
||||
FlatEncodable::Key(repr) => Ok(Derived::Key(DeriveKey::ToEncoder(repr))),
|
||||
},
|
||||
DeriveBuiltin::Decoder => match decoding::FlatDecodable::from_builtin_symbol(symbol)? {
|
||||
FlatDecodable::Immediate(imm) => Ok(Derived::Immediate(imm)),
|
||||
FlatDecodable::Key(repr) => Ok(Derived::Key(DeriveKey::Decoder(repr))),
|
||||
},
|
||||
DeriveBuiltin::Hash => match hash::FlatHash::from_builtin_symbol(symbol)? {
|
||||
FlatHash::SingleLambdaSetImmediate(imm) => {
|
||||
Ok(Derived::SingleLambdaSetImmediate(imm))
|
||||
}
|
||||
FlatHash::Key(repr) => Ok(Derived::Key(DeriveKey::Hash(repr))),
|
||||
},
|
||||
DeriveBuiltin::IsEq => {
|
||||
// If obligation checking passes, we always lower derived implementations of `isEq`
|
||||
// to the `Eq` low-level, to be fulfilled by the backends.
|
||||
Ok(Derived::SingleLambdaSetImmediate(
|
||||
Symbol::BOOL_STRUCTURAL_EQ,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_types::types::Polarity;
|
|||
use roc_unify::unify::MetaCollector;
|
||||
use roc_unify::unify::{Env, Mode, Unified};
|
||||
|
||||
pub use roc_solve::ability::Resolved;
|
||||
pub use roc_solve::ability::{ResolveError, Resolved};
|
||||
pub use roc_types::subs::instantiate_rigids;
|
||||
|
||||
pub mod storage;
|
||||
|
@ -161,7 +161,7 @@ pub fn resolve_ability_specialization(
|
|||
abilities: &AbilitiesView,
|
||||
ability_member: Symbol,
|
||||
specialization_var: Variable,
|
||||
) -> Option<Resolved> {
|
||||
) -> Result<Resolved, ResolveError> {
|
||||
let late_resolver = LateResolver { home, abilities };
|
||||
roc_solve::ability::resolve_ability_specialization(
|
||||
subs,
|
||||
|
|
|
@ -5331,7 +5331,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
let resolved_proc = match resolved_proc {
|
||||
Resolved::Specialization(symbol) => symbol,
|
||||
Resolved::NeedsGenerated(_) => {
|
||||
Resolved::Derive(_) => {
|
||||
todo_abilities!("Generate impls for structural types")
|
||||
}
|
||||
};
|
||||
|
@ -5813,14 +5813,7 @@ fn late_resolve_ability_specialization<'a>(
|
|||
|
||||
match specialization {
|
||||
Resolved::Specialization(symbol) => symbol,
|
||||
Resolved::NeedsGenerated(var) => {
|
||||
let derive_key = roc_derive_key::Derived::builtin(
|
||||
member.try_into().expect("derived symbols must be builtins"),
|
||||
env.subs,
|
||||
var,
|
||||
)
|
||||
.expect("specialization var not derivable!");
|
||||
|
||||
Resolved::Derive(derive_key) => {
|
||||
match derive_key {
|
||||
roc_derive_key::Derived::Immediate(imm)
|
||||
| roc_derive_key::Derived::SingleLambdaSetImmediate(imm) => {
|
||||
|
|
|
@ -4,8 +4,9 @@ use roc_collections::{VecMap, VecSet};
|
|||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_PRINT_UNDERIVABLE;
|
||||
use roc_derive_key::{DeriveError, Derived};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve_problem::{
|
||||
NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
|
||||
|
@ -369,8 +370,21 @@ impl ObligationCache {
|
|||
}
|
||||
|
||||
let ImplKey { opaque, ability } = impl_key;
|
||||
|
||||
let has_declared_impl = 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.
|
||||
let builtin_opaque_impl_ok = || match ability {
|
||||
DeriveEncoding::ABILITY => DeriveEncoding::is_derivable_builtin_opaque(opaque),
|
||||
DeriveDecoding::ABILITY => DeriveDecoding::is_derivable_builtin_opaque(opaque),
|
||||
DeriveEq::ABILITY => DeriveEq::is_derivable_builtin_opaque(opaque),
|
||||
DeriveHash::ABILITY => DeriveHash::is_derivable_builtin_opaque(opaque),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let has_declared_impl = has_declared_impl || builtin_opaque_impl_ok();
|
||||
|
||||
let obligation_result = if !has_declared_impl {
|
||||
Err(Unfulfilled::OpaqueDoesNotImplement {
|
||||
typ: opaque,
|
||||
|
@ -493,6 +507,11 @@ fn is_builtin_number_alias(symbol: Symbol) -> bool {
|
|||
|| is_builtin_dec_alias(symbol)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_builtin_bool_alias(symbol: Symbol) -> bool {
|
||||
matches!(symbol, Symbol::BOOL_BOOL)
|
||||
}
|
||||
|
||||
struct NotDerivable {
|
||||
var: Variable,
|
||||
context: NotDerivableContext,
|
||||
|
@ -829,7 +848,8 @@ impl DerivableVisitor for DeriveEncoding {
|
|||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol)
|
||||
(is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol))
|
||||
|| is_builtin_bool_alias(symbol)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -924,7 +944,8 @@ impl DerivableVisitor for DeriveDecoding {
|
|||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol)
|
||||
(is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol))
|
||||
|| is_builtin_bool_alias(symbol)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1030,7 +1051,7 @@ impl DerivableVisitor for DeriveHash {
|
|||
|
||||
#[inline(always)]
|
||||
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
|
||||
is_builtin_number_alias(symbol)
|
||||
is_builtin_number_alias(symbol) || is_builtin_bool_alias(symbol)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1132,6 +1153,7 @@ impl DerivableVisitor for DeriveEq {
|
|||
is_builtin_fixed_int_alias(symbol)
|
||||
|| is_builtin_nat_alias(symbol)
|
||||
|| is_builtin_dec_alias(symbol)
|
||||
|| is_builtin_bool_alias(symbol)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1276,12 +1298,12 @@ pub fn type_implementing_specialization(
|
|||
}
|
||||
|
||||
/// Result of trying to resolve an ability specialization.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Resolved {
|
||||
/// A user-defined specialization should be used.
|
||||
Specialization(Symbol),
|
||||
/// A specialization must be generated for the given type variable.
|
||||
NeedsGenerated(Variable),
|
||||
/// A specialization must be generated with the given derive key.
|
||||
Derive(Derived),
|
||||
}
|
||||
|
||||
/// An [`AbilityResolver`] is a shell of an abilities store that answers questions needed for
|
||||
|
@ -1324,12 +1346,32 @@ impl AbilityResolver for AbilitiesStore {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether this 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 {
|
||||
matches!(module_id, ModuleId::NUM | ModuleId::BOOL)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ResolveError {
|
||||
NonDerivableAbility(Symbol),
|
||||
DeriveError(DeriveError),
|
||||
NoTypeImplementingSpecialization,
|
||||
}
|
||||
|
||||
impl From<DeriveError> for ResolveError {
|
||||
fn from(e: DeriveError) -> Self {
|
||||
Self::DeriveError(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_ability_specialization<R: AbilityResolver>(
|
||||
subs: &mut Subs,
|
||||
resolver: &R,
|
||||
ability_member: Symbol,
|
||||
specialization_var: Variable,
|
||||
) -> Option<Resolved> {
|
||||
) -> Result<Resolved, ResolveError> {
|
||||
use roc_unify::unify::{unify, Mode};
|
||||
|
||||
let (parent_ability, signature_var) = resolver
|
||||
|
@ -1353,29 +1395,51 @@ pub fn resolve_ability_specialization<R: AbilityResolver>(
|
|||
|
||||
subs.rollback_to(snapshot);
|
||||
|
||||
let obligated = type_implementing_specialization(&must_implement_ability, parent_ability)?;
|
||||
use ResolveError::*;
|
||||
|
||||
let obligated = type_implementing_specialization(&must_implement_ability, parent_ability)
|
||||
.ok_or(NoTypeImplementingSpecialization)?;
|
||||
|
||||
let resolved = match obligated {
|
||||
Obligated::Opaque(symbol) => {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
opaque: symbol,
|
||||
ability_member,
|
||||
};
|
||||
if builtin_module_with_unlisted_ability_impl(symbol.module_id()) {
|
||||
let derive_key = roc_derive_key::Derived::builtin_with_builtin_symbol(
|
||||
ability_member.try_into().map_err(NonDerivableAbility)?,
|
||||
symbol,
|
||||
)?;
|
||||
|
||||
match resolver.get_implementation(impl_key)? {
|
||||
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||
Resolved::Specialization(spec_symbol)
|
||||
Resolved::Derive(derive_key)
|
||||
} else {
|
||||
let impl_key = roc_can::abilities::ImplKey {
|
||||
opaque: symbol,
|
||||
ability_member,
|
||||
};
|
||||
|
||||
match resolver
|
||||
.get_implementation(impl_key)
|
||||
.ok_or(NoTypeImplementingSpecialization)?
|
||||
{
|
||||
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||
Resolved::Specialization(spec_symbol)
|
||||
}
|
||||
// TODO this is not correct. We can replace `Resolved` with `MemberImpl` entirely,
|
||||
// which will make this simpler.
|
||||
roc_types::types::MemberImpl::Error => {
|
||||
Resolved::Specialization(Symbol::UNDERSCORE)
|
||||
}
|
||||
}
|
||||
// TODO this is not correct. We can replace `Resolved` with `MemberImpl` entirely,
|
||||
// which will make this simpler.
|
||||
roc_types::types::MemberImpl::Error => Resolved::Specialization(Symbol::UNDERSCORE),
|
||||
}
|
||||
}
|
||||
Obligated::Adhoc(variable) => {
|
||||
// TODO: more rules need to be validated here, like is this a builtin ability?
|
||||
Resolved::NeedsGenerated(variable)
|
||||
let derive_key = roc_derive_key::Derived::builtin(
|
||||
ability_member.try_into().map_err(NonDerivableAbility)?,
|
||||
subs,
|
||||
variable,
|
||||
)?;
|
||||
|
||||
Resolved::Derive(derive_key)
|
||||
}
|
||||
};
|
||||
|
||||
Some(resolved)
|
||||
Ok(resolved)
|
||||
}
|
||||
|
|
|
@ -1718,14 +1718,12 @@ fn solve(
|
|||
member,
|
||||
specialization_id,
|
||||
}) => {
|
||||
if let Some(Resolved::Specialization(specialization)) =
|
||||
resolve_ability_specialization(
|
||||
subs,
|
||||
abilities_store,
|
||||
member,
|
||||
specialization_variable,
|
||||
)
|
||||
{
|
||||
if let Ok(Resolved::Specialization(specialization)) = resolve_ability_specialization(
|
||||
subs,
|
||||
abilities_store,
|
||||
member,
|
||||
specialization_variable,
|
||||
) {
|
||||
abilities_store.insert_resolved(specialization_id, specialization);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ use roc_types::{
|
|||
};
|
||||
use roc_unify::unify::{unify, Env as UEnv, Mode, MustImplementConstraints};
|
||||
|
||||
use crate::solve::{deep_copy_var_in, introduce, Pools};
|
||||
use crate::{
|
||||
ability::builtin_module_with_unlisted_ability_impl,
|
||||
solve::{deep_copy_var_in, introduce, Pools},
|
||||
};
|
||||
|
||||
/// What phase in the compiler is reaching out to specialize lambda sets?
|
||||
/// This is important to distinguish subtle differences in the behavior of the solving algorithm.
|
||||
|
@ -625,7 +628,9 @@ fn make_specialization_decision<P: Phase>(
|
|||
use Content::*;
|
||||
use SpecializationTypeKey::*;
|
||||
match subs.get_content_without_compacting(var) {
|
||||
Alias(opaque, _, _, AliasKind::Opaque) if opaque.module_id() != ModuleId::NUM => {
|
||||
Alias(opaque, _, _, AliasKind::Opaque)
|
||||
if !builtin_module_with_unlisted_ability_impl(opaque.module_id()) =>
|
||||
{
|
||||
if P::IS_LATE {
|
||||
SpecializeDecision::Specialize(Opaque(*opaque))
|
||||
} else {
|
||||
|
|
|
@ -8806,4 +8806,52 @@ mod solve_expr {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_decoder_for_bool() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main : Decoder Bool _
|
||||
main = Decode.custom \bytes, fmt ->
|
||||
Decode.decodeWith bytes Decode.decoder fmt
|
||||
# ^^^^^^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"Decoding#Decode.decoder(4) : List U8, fmt -[[] + fmt:Decode.bool(19):1]-> { rest : List U8, result : [Err [TooShort], Ok [False, True]] } | fmt has DecoderFormatting"
|
||||
print_only_under_alias: true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_to_encoder_for_bool() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main = Encode.toEncoder Bool.true
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"Encoding#Encode.toEncoder(2) : Bool -[[] + fmt:Encode.bool(17):1]-> Encoder fmt | fmt has EncoderFormatting"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_eq_for_bool() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main = Bool.isEq Bool.true Bool.false
|
||||
# ^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"Eq#Bool.isEq(9) : Bool, Bool -[[Bool.structuralEq(11)]]-> Bool"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,17 +57,17 @@ test_key_eq! {
|
|||
v!(Symbol::STR_STR), v!(Symbol::STR_STR)
|
||||
|
||||
alias_eq_real_type:
|
||||
v!(Symbol::BOOL_BOOL => v!([ True, False ])), v!([False, True])
|
||||
v!(Symbol::ATTR_ATTR => v!([ True, False ])), v!([False, True])
|
||||
diff_alias_same_real_type:
|
||||
v!(Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True]))
|
||||
v!(Symbol::ATTR_ATTR => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True]))
|
||||
|
||||
opaque_eq_real_type:
|
||||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!([False, True])
|
||||
v!(@Symbol::ATTR_ATTR => v!([ True, False ])), v!([False, True])
|
||||
diff_opaque_same_real_type:
|
||||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(@Symbol::UNDERSCORE => v!([False, True]))
|
||||
v!(@Symbol::ATTR_ATTR => v!([ True, False ])), v!(@Symbol::UNDERSCORE => v!([False, True]))
|
||||
|
||||
opaque_real_type_eq_alias_real_type:
|
||||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True]))
|
||||
v!(@Symbol::ATTR_ATTR => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True]))
|
||||
}
|
||||
|
||||
test_key_neq! {
|
||||
|
@ -86,14 +86,14 @@ test_key_neq! {
|
|||
v!([ Nil, Cons v!(^lst) ] as lst), v!([ Nil, Next v!(^lst) ] as lst)
|
||||
|
||||
same_alias_diff_real_type:
|
||||
v!(Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::BOOL_BOOL => v!([ False, True, Maybe ]))
|
||||
v!(Symbol::ATTR_ATTR => v!([ True, False ])), v!(Symbol::ATTR_ATTR => v!([ False, True, Maybe ]))
|
||||
diff_alias_diff_real_type:
|
||||
v!(Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([ False, True, Maybe ]))
|
||||
v!(Symbol::ATTR_ATTR => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([ False, True, Maybe ]))
|
||||
|
||||
same_opaque_diff_real_type:
|
||||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(@Symbol::BOOL_BOOL => v!([ False, True, Maybe ]))
|
||||
v!(@Symbol::ATTR_ATTR => v!([ True, False ])), v!(@Symbol::ATTR_ATTR => v!([ False, True, Maybe ]))
|
||||
diff_opaque_diff_real_type:
|
||||
v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(@Symbol::UNDERSCORE => v!([ False, True, Maybe ]))
|
||||
v!(@Symbol::ATTR_ATTR => v!([ True, False ])), v!(@Symbol::UNDERSCORE => v!([ False, True, Maybe ]))
|
||||
}
|
||||
|
||||
// }}} hash tests
|
||||
|
|
|
@ -504,6 +504,25 @@ mod encode_immediate {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn bool() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Encode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.fromUtf8 (Encode.toBytes Bool.false Json.toUtf8) is
|
||||
Ok s -> s
|
||||
_ -> "<bad>"
|
||||
"#
|
||||
),
|
||||
RocStr::from(r"false"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! num_immediate {
|
||||
($($num:expr, $typ:ident)*) => {$(
|
||||
#[test]
|
||||
|
@ -1001,6 +1020,25 @@ mod decode_immediate {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn bool() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "false" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok s -> s
|
||||
_ -> Bool.true
|
||||
"#
|
||||
),
|
||||
false,
|
||||
bool
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! num_immediate {
|
||||
($($num:expr, $typ:ident)*) => {$(
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue