mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-14 07:29:02 +03:00
Merge pull request #4533 from roc-lang/i4416
Choose `hash` lambda for ranged numbers based on their default compilation width
This commit is contained in:
commit
e88c6ea17b
@ -105,42 +105,29 @@ impl FlatHash {
|
||||
//
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
},
|
||||
Content::Alias(sym, _, real_var, _) => match sym {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_ADD_U8))
|
||||
}
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_ADD_U16))
|
||||
}
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_ADD_U32))
|
||||
}
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_ADD_U64))
|
||||
}
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_ADD_U128))
|
||||
}
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_HASH_I8))
|
||||
}
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_HASH_I16))
|
||||
}
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_HASH_I32))
|
||||
}
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_HASH_I64))
|
||||
}
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => {
|
||||
Ok(SingleLambdaSetImmediate(Symbol::HASH_HASH_I128))
|
||||
}
|
||||
Content::Alias(sym, _, real_var, _) => match num_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.
|
||||
_ => Self::from_var(subs, real_var),
|
||||
None => Self::from_var(subs, real_var),
|
||||
},
|
||||
Content::RangedNumber(_) => Err(Underivable),
|
||||
Content::RangedNumber(range) => {
|
||||
// Find the integer we're going to compile to, that'll tell us what lambda we
|
||||
// should resolve to.
|
||||
//
|
||||
// Note that at this point, we don't need to update the underlying type variable.
|
||||
// That's because
|
||||
//
|
||||
// - If the type variable always had a ground constructor after solving, we would
|
||||
// have already refined the ranged number during obligation checking.
|
||||
//
|
||||
// - If the type variable was generalized, then this branch is only reached
|
||||
// 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();
|
||||
Ok(lambda)
|
||||
}
|
||||
//
|
||||
Content::RecursionVar { structure, .. } => Self::from_var(subs, structure),
|
||||
//
|
||||
@ -153,3 +140,40 @@ impl FlatHash {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn num_symbol_to_hash_lambda(symbol: Symbol) -> Option<FlatHash> {
|
||||
use FlatHash::*;
|
||||
match symbol {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_ADD_U8))
|
||||
}
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_ADD_U16))
|
||||
}
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_ADD_U32))
|
||||
}
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_ADD_U64))
|
||||
}
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_ADD_U128))
|
||||
}
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I8))
|
||||
}
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I16))
|
||||
}
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I32))
|
||||
}
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I64))
|
||||
}
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I128))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ pub enum Derived {
|
||||
}
|
||||
|
||||
/// The builtin ability member to derive.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DeriveBuiltin {
|
||||
ToEncoder,
|
||||
Decoder,
|
||||
|
@ -2276,33 +2276,12 @@ impl<'a> Layout<'a> {
|
||||
env: &mut Env<'a, '_>,
|
||||
range: NumericRange,
|
||||
) -> Cacheable<LayoutResult<'a>> {
|
||||
use roc_types::num::IntLitWidth;
|
||||
|
||||
// If we chose the default int layout then the real var might have been `Num *`, or
|
||||
// similar. In this case fix-up width if we need to. Choose I64 if the range says
|
||||
// that the number will fit, otherwise choose the next-largest number layout.
|
||||
//
|
||||
// We don't pass the range down because `RangedNumber`s are somewhat rare, they only
|
||||
// appear due to number literals, so no need to increase parameter list sizes.
|
||||
let num_layout = match range {
|
||||
NumericRange::IntAtLeastSigned(w) | NumericRange::NumAtLeastSigned(w) => {
|
||||
[IntLitWidth::I64, IntLitWidth::I128]
|
||||
.iter()
|
||||
.find(|candidate| candidate.is_superset(&w, true))
|
||||
.expect("if number doesn't fit, should have been a type error")
|
||||
}
|
||||
NumericRange::IntAtLeastEitherSign(w) | NumericRange::NumAtLeastEitherSign(w) => [
|
||||
IntLitWidth::I64,
|
||||
IntLitWidth::U64,
|
||||
IntLitWidth::I128,
|
||||
IntLitWidth::U128,
|
||||
]
|
||||
.iter()
|
||||
.find(|candidate| candidate.is_superset(&w, false))
|
||||
.expect("if number doesn't fit, should have been a type error"),
|
||||
};
|
||||
let num_layout = range.default_compilation_width();
|
||||
|
||||
cacheable(Ok(Layout::int_literal_width_to_int(
|
||||
*num_layout,
|
||||
num_layout,
|
||||
env.target_info,
|
||||
)))
|
||||
}
|
||||
|
@ -605,6 +605,7 @@ enum SpecializationTypeKey {
|
||||
SingleLambdaSetImmediate(Symbol),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SpecializeDecision {
|
||||
Specialize(SpecializationTypeKey),
|
||||
Drop,
|
||||
|
@ -8221,6 +8221,22 @@ mod solve_expr {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn choose_ranged_num_for_hash() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
\h -> Hash.hash h 7
|
||||
# ^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"Hash#Hash.hash(1) : a, I64 -[[Hash.hashI64(12)]]-> a | a has Hasher"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generalize_inferred_opaque_variable_bound_to_ability_issue_4408() {
|
||||
infer_eq_without_problem(
|
||||
|
@ -1,3 +1,5 @@
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
use crate::subs::Variable;
|
||||
|
||||
/// A bound placed on a number because of its literal value.
|
||||
@ -121,6 +123,29 @@ impl NumericRange {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Chooses the int width to compile this ranged number into.
|
||||
/// I64 is chosen if the range says that the number will fit,
|
||||
/// otherwise the next-largest number layout is chosen.
|
||||
pub fn default_compilation_width(&self) -> IntLitWidth {
|
||||
*match self {
|
||||
NumericRange::IntAtLeastSigned(w) | NumericRange::NumAtLeastSigned(w) => {
|
||||
[IntLitWidth::I64, IntLitWidth::I128]
|
||||
.iter()
|
||||
.find(|candidate| candidate.is_superset(w, true))
|
||||
.expect("if number doesn't fit, should have been a type error")
|
||||
}
|
||||
NumericRange::IntAtLeastEitherSign(w) | NumericRange::NumAtLeastEitherSign(w) => [
|
||||
IntLitWidth::I64,
|
||||
IntLitWidth::U64,
|
||||
IntLitWidth::I128,
|
||||
IntLitWidth::U128,
|
||||
]
|
||||
.iter()
|
||||
.find(|candidate| candidate.is_superset(w, false))
|
||||
.expect("if number doesn't fit, should have been a type error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
@ -281,6 +306,25 @@ impl IntLitWidth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn symbol(&self) -> Symbol {
|
||||
match self {
|
||||
IntLitWidth::U8 => Symbol::NUM_U8,
|
||||
IntLitWidth::U16 => Symbol::NUM_U16,
|
||||
IntLitWidth::U32 => Symbol::NUM_U32,
|
||||
IntLitWidth::U64 => Symbol::NUM_U64,
|
||||
IntLitWidth::U128 => Symbol::NUM_U128,
|
||||
IntLitWidth::I8 => Symbol::NUM_I8,
|
||||
IntLitWidth::I16 => Symbol::NUM_I16,
|
||||
IntLitWidth::I32 => Symbol::NUM_I32,
|
||||
IntLitWidth::I64 => Symbol::NUM_I64,
|
||||
IntLitWidth::I128 => Symbol::NUM_I128,
|
||||
IntLitWidth::Nat => Symbol::NUM_NAT,
|
||||
IntLitWidth::F32 => Symbol::NUM_F32,
|
||||
IntLitWidth::F64 => Symbol::NUM_F64,
|
||||
IntLitWidth::Dec => Symbol::NUM_DEC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
Loading…
Reference in New Issue
Block a user