mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Detect when big number literals cannot fit into the same type
This commit is contained in:
parent
e6c48abb11
commit
1905e1815d
3 changed files with 97 additions and 23 deletions
|
@ -68,6 +68,47 @@ impl NumericRange {
|
||||||
width.signedness_and_width().1 >= at_least_width.signedness_and_width().1
|
width.signedness_and_width().1 >= at_least_width.signedness_and_width().1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> IntWidth {
|
||||||
|
use NumericRange::*;
|
||||||
|
match self {
|
||||||
|
IntAtLeastSigned(w)
|
||||||
|
| IntAtLeastEitherSign(w)
|
||||||
|
| NumAtLeastSigned(w)
|
||||||
|
| NumAtLeastEitherSign(w) => *w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the intersection of `self` and `other`, i.e. the greatest lower bound of both, or
|
||||||
|
/// `None` if there is no common lower bound.
|
||||||
|
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||||
|
use NumericRange::*;
|
||||||
|
let (left, right) = (self.width(), other.width());
|
||||||
|
let constructor: fn(IntWidth) -> NumericRange = match (self, other) {
|
||||||
|
// Matching against a signed int, the intersection must also be a signed int
|
||||||
|
(IntAtLeastSigned(_), _) | (_, IntAtLeastSigned(_)) => IntAtLeastSigned,
|
||||||
|
// It's a signed number, but also an int, so the intersection must be a signed int
|
||||||
|
(NumAtLeastSigned(_), IntAtLeastEitherSign(_))
|
||||||
|
| (IntAtLeastEitherSign(_), NumAtLeastSigned(_)) => IntAtLeastSigned,
|
||||||
|
// It's a signed number
|
||||||
|
(NumAtLeastSigned(_), NumAtLeastSigned(_) | NumAtLeastEitherSign(_))
|
||||||
|
| (NumAtLeastEitherSign(_), NumAtLeastSigned(_)) => NumAtLeastSigned,
|
||||||
|
// Otherwise we must be an int, signed or unsigned
|
||||||
|
(IntAtLeastEitherSign(_), IntAtLeastEitherSign(_) | NumAtLeastEitherSign(_))
|
||||||
|
| (NumAtLeastEitherSign(_), IntAtLeastEitherSign(_)) => IntAtLeastEitherSign,
|
||||||
|
// Otherwise we must be a num, signed or unsigned
|
||||||
|
(NumAtLeastEitherSign(_), NumAtLeastEitherSign(_)) => NumAtLeastEitherSign,
|
||||||
|
};
|
||||||
|
|
||||||
|
// One is a superset of the other if it's a superset on both sides
|
||||||
|
if left.is_superset(&right, true) && left.is_superset(&right, false) {
|
||||||
|
Some(constructor(left))
|
||||||
|
} else if right.is_superset(&left, true) && right.is_superset(&left, false) {
|
||||||
|
Some(constructor(right))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn variable_slice(&self) -> &'static [Variable] {
|
pub fn variable_slice(&self) -> &'static [Variable] {
|
||||||
use NumericRange::*;
|
use NumericRange::*;
|
||||||
|
|
||||||
|
|
|
@ -488,6 +488,15 @@ fn unify_context<M: MetaCollector>(subs: &mut Subs, pool: &mut Pool, ctx: Contex
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn not_in_range_mismatch<M: MetaCollector>() -> Outcome<M> {
|
||||||
|
Outcome {
|
||||||
|
mismatches: vec![Mismatch::TypeNotInRange],
|
||||||
|
must_implement_ability: Default::default(),
|
||||||
|
lambda_sets_to_specialize: Default::default(),
|
||||||
|
extra_metadata: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_ranged_number<M: MetaCollector>(
|
fn unify_ranged_number<M: MetaCollector>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
@ -498,7 +507,7 @@ fn unify_ranged_number<M: MetaCollector>(
|
||||||
) -> Outcome<M> {
|
) -> Outcome<M> {
|
||||||
let other_content = &ctx.second_desc.content;
|
let other_content = &ctx.second_desc.content;
|
||||||
|
|
||||||
let outcome = match other_content {
|
match other_content {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// Ranged number wins
|
// Ranged number wins
|
||||||
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
||||||
|
@ -508,25 +517,31 @@ fn unify_ranged_number<M: MetaCollector>(
|
||||||
| Alias(..)
|
| Alias(..)
|
||||||
| Structure(..)
|
| Structure(..)
|
||||||
| RigidAbleVar(..)
|
| RigidAbleVar(..)
|
||||||
| FlexAbleVar(..) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
| FlexAbleVar(..) => {
|
||||||
|
let outcome = unify_pool(subs, pool, real_var, ctx.second, ctx.mode);
|
||||||
|
if !outcome.mismatches.is_empty() {
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
let outcome = check_valid_range(subs, ctx.second, range_vars);
|
||||||
|
if !outcome.mismatches.is_empty() {
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
let real_var = subs.fresh(subs.get_without_compacting(real_var));
|
||||||
|
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
||||||
|
}
|
||||||
&RangedNumber(other_real_var, other_range_vars) => {
|
&RangedNumber(other_real_var, other_range_vars) => {
|
||||||
let outcome = unify_pool(subs, pool, real_var, other_real_var, ctx.mode);
|
let outcome = unify_pool(subs, pool, real_var, other_real_var, ctx.mode);
|
||||||
if outcome.mismatches.is_empty() {
|
if !outcome.mismatches.is_empty() {
|
||||||
check_valid_range(subs, ctx.first, other_range_vars)
|
return outcome;
|
||||||
} else {
|
}
|
||||||
outcome
|
match range_vars.intersection(&other_range_vars) {
|
||||||
|
Some(range) => merge(subs, ctx, RangedNumber(real_var, range)),
|
||||||
|
None => not_in_range_mismatch(),
|
||||||
}
|
}
|
||||||
// TODO: We should probably check that "range_vars" and "other_range_vars" intersect
|
|
||||||
}
|
}
|
||||||
LambdaSet(..) => mismatch!(),
|
LambdaSet(..) => mismatch!(),
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
};
|
|
||||||
|
|
||||||
if !outcome.mismatches.is_empty() {
|
|
||||||
return outcome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check_valid_range(subs, ctx.second, range_vars)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_valid_range<M: MetaCollector>(
|
fn check_valid_range<M: MetaCollector>(
|
||||||
|
@ -544,14 +559,7 @@ fn check_valid_range<M: MetaCollector>(
|
||||||
return check_valid_range(subs, actual, range);
|
return check_valid_range(subs, actual, range);
|
||||||
}
|
}
|
||||||
Some(false) => {
|
Some(false) => {
|
||||||
let outcome = Outcome {
|
return not_in_range_mismatch();
|
||||||
mismatches: vec![Mismatch::TypeNotInRange],
|
|
||||||
must_implement_ability: Default::default(),
|
|
||||||
lambda_sets_to_specialize: Default::default(),
|
|
||||||
extra_metadata: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return outcome;
|
|
||||||
}
|
}
|
||||||
Some(true) => { /* fall through */ }
|
Some(true) => { /* fall through */ }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3301,12 +3301,12 @@ mod test_reporting {
|
||||||
|
|
||||||
This `ACons` tag application has the type:
|
This `ACons` tag application has the type:
|
||||||
|
|
||||||
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64),
|
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons (Int Signed64) [ACons (Int Signed64) (BList (Int Signed64) I64),
|
||||||
ANil] as ∞, BNil], ANil], BNil], ANil]
|
ANil] as ∞, BNil], ANil], BNil], ANil]
|
||||||
|
|
||||||
But the type annotation on `x` says it should be:
|
But the type annotation on `x` says it should be:
|
||||||
|
|
||||||
[ACons I64 (BList I64 I64), ANil] as a
|
[ACons (Int Signed64) (BList (Int Signed64) I64), ANil] as a
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9436,4 +9436,29 @@ All branches in an `if` must have the same type!
|
||||||
@r###"
|
@r###"
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
int_literals_cannot_fit_in_same_type,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
The 2nd argument to `isEq` is not what I expect:
|
||||||
|
|
||||||
|
4│ 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This argument is an integer of type:
|
||||||
|
|
||||||
|
I128
|
||||||
|
|
||||||
|
But `isEq` needs the 2nd argument to be:
|
||||||
|
|
||||||
|
U128
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue