mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
Merge pull request #4644 from roc-lang/i4594
Unify ranged numbers with flex able, modulo obligation checking
This commit is contained in:
commit
9b4552608f
@ -593,6 +593,18 @@ trait DerivableVisitor {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_floating_point_content(
|
||||
var: Variable,
|
||||
_subs: &mut Subs,
|
||||
_content_var: Variable,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::NoContext,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_ranged_number(var: Variable, _range: NumericRange) -> Result<(), NotDerivable> {
|
||||
Err(NotDerivable {
|
||||
@ -717,14 +729,22 @@ trait DerivableVisitor {
|
||||
EmptyTagUnion => Self::visit_empty_tag_union(var)?,
|
||||
},
|
||||
Alias(
|
||||
Symbol::NUM_NUM | Symbol::NUM_INTEGER | Symbol::NUM_FLOATINGPOINT,
|
||||
Symbol::NUM_NUM | Symbol::NUM_INTEGER,
|
||||
_alias_variables,
|
||||
real_var,
|
||||
AliasKind::Opaque,
|
||||
) => {
|
||||
// Numbers: always decay until a ground is hit.
|
||||
// Unbound numbers and integers: always decay until a ground is hit,
|
||||
// since all of our builtin abilities currently support integers.
|
||||
stack.push(real_var);
|
||||
}
|
||||
Alias(Symbol::NUM_FLOATINGPOINT, _alias_variables, real_var, AliasKind::Opaque) => {
|
||||
let descend = Self::visit_floating_point_content(var, subs, real_var)?;
|
||||
if descend.0 {
|
||||
// Decay to a ground
|
||||
stack.push(real_var)
|
||||
}
|
||||
}
|
||||
Alias(opaque, _alias_variables, _real_var, AliasKind::Opaque) => {
|
||||
if obligation_cache
|
||||
.check_opaque_and_read(abilities_store, opaque, Self::ABILITY)
|
||||
@ -841,6 +861,15 @@ impl DerivableVisitor for DeriveEncoding {
|
||||
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 DeriveDecoding;
|
||||
@ -931,6 +960,15 @@ impl DerivableVisitor for DeriveDecoding {
|
||||
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 DeriveHash;
|
||||
@ -1021,6 +1059,15 @@ impl DerivableVisitor for DeriveHash {
|
||||
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 DeriveEq;
|
||||
@ -1116,6 +1163,32 @@ impl DerivableVisitor for DeriveEq {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_floating_point_content(
|
||||
var: Variable,
|
||||
subs: &mut Subs,
|
||||
content_var: Variable,
|
||||
) -> Result<Descend, NotDerivable> {
|
||||
use roc_unify::unify::{unify, Mode};
|
||||
|
||||
// Of the floating-point types,
|
||||
// only Dec implements Eq.
|
||||
let mut env = Env::new(subs);
|
||||
let unified = unify(
|
||||
&mut env,
|
||||
content_var,
|
||||
Variable::DECIMAL,
|
||||
Mode::EQ,
|
||||
Polarity::Pos,
|
||||
);
|
||||
match unified {
|
||||
roc_unify::unify::Unified::Success { .. } => Ok(Descend(false)),
|
||||
roc_unify::unify::Unified::Failure(..) => Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::Eq(NotDerivableEq::FloatingPoint),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_ranged_number(_var: Variable, _range: NumericRange) -> Result<(), NotDerivable> {
|
||||
// Ranged numbers are allowed, because they are always possibly ints - floats can not have
|
||||
|
@ -8414,4 +8414,21 @@ mod solve_expr {
|
||||
"[Ok a]* -> a",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_eq_for_float_forces_dec() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
n : Num *
|
||||
|
||||
main = n == 1.
|
||||
# ^
|
||||
"#
|
||||
),
|
||||
@"n : Dec"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1588,7 +1588,7 @@ fn float_type(
|
||||
num_binary64,
|
||||
AliasVariables::default(),
|
||||
Variable::EMPTY_TAG_UNION,
|
||||
AliasKind::Structural,
|
||||
AliasKind::Opaque,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
@ -650,7 +650,18 @@ fn unify_ranged_number<M: MetaCollector>(
|
||||
// Int a vs Int <range>, the rigid wins
|
||||
merge(env, ctx, RigidVar(*name))
|
||||
}
|
||||
RecursionVar { .. } | Alias(..) | Structure(..) | RigidAbleVar(..) | FlexAbleVar(..) => {
|
||||
FlexAbleVar(_, abilities) => {
|
||||
// Range wins, modulo obligation checking.
|
||||
merge_flex_able_with_concrete(
|
||||
env,
|
||||
ctx,
|
||||
ctx.second,
|
||||
*abilities,
|
||||
RangedNumber(range_vars),
|
||||
Obligated::Adhoc(ctx.first),
|
||||
)
|
||||
}
|
||||
RecursionVar { .. } | Alias(..) | Structure(..) | RigidAbleVar(..) => {
|
||||
check_and_merge_valid_range(env, pool, ctx, ctx.first, range_vars, ctx.second)
|
||||
}
|
||||
&RangedNumber(other_range_vars) => match range_vars.intersection(&other_range_vars) {
|
||||
|
@ -442,8 +442,8 @@ mod test {
|
||||
main = 0
|
||||
|
||||
expect
|
||||
vec1 = { x: 1.0, y: 2.0 }
|
||||
vec2 = { x: 4.0, y: 8.0 }
|
||||
vec1 = { x: 1.0f64, y: 2.0f64 }
|
||||
vec2 = { x: 4.0f64, y: 8.0f64 }
|
||||
|
||||
vec1 == vec2
|
||||
"#
|
||||
@ -453,17 +453,17 @@ mod test {
|
||||
This expectation failed:
|
||||
|
||||
5│> expect
|
||||
6│> vec1 = { x: 1.0, y: 2.0 }
|
||||
7│> vec2 = { x: 4.0, y: 8.0 }
|
||||
6│> vec1 = { x: 1.0f64, y: 2.0f64 }
|
||||
7│> vec2 = { x: 4.0f64, y: 8.0f64 }
|
||||
8│>
|
||||
9│> vec1 == vec2
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
vec1 : { x : Frac *, y : Frac * }
|
||||
vec1 : { x : F64, y : F64 }
|
||||
vec1 = { x: 1, y: 2 }
|
||||
|
||||
vec2 : { x : Frac *, y : Frac * }
|
||||
vec2 : { x : F64, y : F64 }
|
||||
vec2 = { x: 4, y: 8 }
|
||||
"#
|
||||
),
|
||||
|
@ -12572,4 +12572,60 @@ I recommend using camelCase. It's the standard style in Roc code!
|
||||
`crash` must be given exacly one message to crash with.
|
||||
"###
|
||||
);
|
||||
|
||||
test_no_problem!(
|
||||
resolve_eq_for_unbound_num,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
n : Num *
|
||||
|
||||
main = n == 1
|
||||
"#
|
||||
)
|
||||
);
|
||||
|
||||
test_report!(
|
||||
resolve_eq_for_unbound_num_float,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
n : Num *
|
||||
|
||||
main = n == 1f64
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
5│ main = n == 1f64
|
||||
^^^^
|
||||
|
||||
I can't generate an implementation of the `Eq` ability for
|
||||
|
||||
FloatingPoint ?
|
||||
|
||||
Note: I can't derive `Bool.isEq` for floating-point types. That's
|
||||
because Roc's floating-point numbers cannot be compared for total
|
||||
equality - in Roc, `NaN` is never comparable to `NaN`. If a type
|
||||
doesn't support total equality, it cannot support the `Eq` ability!
|
||||
"###
|
||||
);
|
||||
|
||||
test_no_problem!(
|
||||
resolve_hash_for_unbound_num,
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
n : Num *
|
||||
|
||||
main = \hasher -> Hash.hash hasher n
|
||||
"#
|
||||
)
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user