From bb85d0f4056a57f73ff57c835a12d026f689a81f Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 20 Nov 2025 16:30:52 +0100 Subject: [PATCH 1/4] report invalid floating point values on both targets --- CHANGELOG.md | 7 ++++++ compiler-core/src/error.rs | 8 +++---- compiler-core/src/javascript/tests/numbers.rs | 8 +++---- ...s__numbers__division_inf_by_inf_float.snap | 24 +++++++++++++++---- ...s__numbers__float_scientific_literals.snap | 6 +---- ...ts__numbers__inf_float_case_statement.snap | 18 +++++++------- compiler-core/src/type_/error.rs | 24 +++++++++---------- compiler-core/src/type_/expression.rs | 12 ++-------- compiler-core/src/type_/pattern.rs | 8 +------ ...s__negative_out_of_range_erlang_float.snap | 8 +++---- ...ve_out_of_range_erlang_float_in_const.snap | 8 +++---- ..._out_of_range_erlang_float_in_pattern.snap | 8 +++---- ...ts__errors__out_of_range_erlang_float.snap | 8 +++---- ...s__out_of_range_erlang_float_in_const.snap | 8 +++---- ..._out_of_range_erlang_float_in_pattern.snap | 8 +++---- 15 files changed, 83 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 638b38b925b..16f1cc1a0dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ from 1.13 has been extended to int segments! Aside from the various performance improvements, this allows the compiler to mark more branches as unreachable. + ```gleam case bits { <<"a">> -> 0 @@ -111,6 +112,7 @@ _ -> 99 } ``` + ([fruno](https://github.com/fruno-bulax/)) ### Build tool @@ -207,6 +209,11 @@ rename all its occurrences. ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) +- The compiler now reports an error for literal floats that are outside the + floating point representable range on both targets. Previously it would only + do that when compiling on the Erlang target. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + - Fixed a typo in the error message emitted when trying to run a module that does not have a main function. ([Louis Pilfold](https://github.com/lpil)) diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 74406a188ea..a44feb12f57 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -1558,12 +1558,12 @@ The error from the encryption library was: } => error .iter() .map(|error| match error { - TypeError::ErlangFloatUnsafe { location, .. } => Diagnostic { - title: "Float is outside Erlang's floating point range".into(), + TypeError::LiteralFloatOutOfRange { location, .. } => Diagnostic { + title: "Float outside of valid range".into(), text: wrap( "This float value is too large to be represented by \ -Erlang's floating point type. To avoid this error float values must be in the range \ --1.7976931348623157e308 - 1.7976931348623157e308.", +a floating point type: float values must be in the range -1.7976931348623157e308 \ +- 1.7976931348623157e308.", ), hint: None, level: Level::Error, diff --git a/compiler-core/src/javascript/tests/numbers.rs b/compiler-core/src/javascript/tests/numbers.rs index eed9f293105..072ace62670 100644 --- a/compiler-core/src/javascript/tests/numbers.rs +++ b/compiler-core/src/javascript/tests/numbers.rs @@ -1,4 +1,4 @@ -use crate::assert_js; +use crate::{assert_js, assert_js_module_error}; #[test] fn int_literals() { @@ -41,9 +41,7 @@ pub fn go() { 0.01e-0 -10.01e-1 -10.01e-0 - 100.001e523 -100.001e-523 - 100.001e123_456_789 -100.001e-123_456_789 } "#, @@ -458,7 +456,7 @@ pub fn main(x) { #[test] fn inf_float_case_statement() { - assert_js!( + assert_js_module_error!( " pub fn main(x) { case x { @@ -472,7 +470,7 @@ pub fn main(x) { #[test] fn division_inf_by_inf_float() { - assert_js!( + assert_js_module_error!( " pub fn main(x) { -100.001e123_456_789 /. 100.001e123_456_789 diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap index 9b33a2135e1..b29ee32b4e4 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__division_inf_by_inf_float.snap @@ -9,7 +9,23 @@ pub fn main(x) { } ------ COMPILED JAVASCRIPT -export function main(x) { - return -Infinity / Infinity; -} +----- ERROR +error: Float outside of valid range + ┌─ /src/one/two.gleam:3:3 + │ +3 │ -100.001e123_456_789 /. 100.001e123_456_789 + │ ^^^^^^^^^^^^^^^^^^^^ + +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. + +error: Float outside of valid range + ┌─ /src/one/two.gleam:3:27 + │ +3 │ -100.001e123_456_789 /. 100.001e123_456_789 + │ ^^^^^^^^^^^^^^^^^^^ + +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap index d062626b3c5..8164f6f55b2 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__float_scientific_literals.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/javascript/tests/numbers.rs -expression: "\npub fn go() {\n 0.01e-1\n 0.01e-0\n -10.01e-1\n -10.01e-0\n 100.001e523\n -100.001e-523\n 100.001e123_456_789\n -100.001e-123_456_789\n}\n" +expression: "\npub fn go() {\n 0.01e-1\n 0.01e-0\n -10.01e-1\n -10.01e-0\n -100.001e-523\n -100.001e-123_456_789\n}\n" --- ----- SOURCE CODE @@ -9,9 +9,7 @@ pub fn go() { 0.01e-0 -10.01e-1 -10.01e-0 - 100.001e523 -100.001e-523 - 100.001e123_456_789 -100.001e-123_456_789 } @@ -22,8 +20,6 @@ export function go() { 0.01; -1.001; -10.01; - Infinity; -0.0; - Infinity; return -0.0; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap index c7d51378682..4120382b10f 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__inf_float_case_statement.snap @@ -12,11 +12,13 @@ pub fn main(x) { } ------ COMPILED JAVASCRIPT -export function main(x) { - if (x === Infinity) { - return "bar"; - } else { - return "foo"; - } -} +----- ERROR +error: Float outside of valid range + ┌─ /src/one/two.gleam:4:3 + │ +4 │ 100.001e123_456_789 -> "bar" + │ ^^^^^^^^^^^^^^^^^^^ + +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/error.rs b/compiler-core/src/type_/error.rs index 1998ff6aa47..f8bef465193 100644 --- a/compiler-core/src/type_/error.rs +++ b/compiler-core/src/type_/error.rs @@ -577,13 +577,16 @@ pub enum Error { location: SrcSpan, }, - /// Occers when any varient of a custom type is deprecated while + /// Occurs when any varient of a custom type is deprecated while /// the custom type itself is deprecated DeprecatedVariantOnDeprecatedType { location: SrcSpan, }, - ErlangFloatUnsafe { + /// Occurs when a literal floating point has a value that is outside of the + /// range representable by floats: -1.7976931348623157e308 to + /// 1.7976931348623157e308. + LiteralFloatOutOfRange { location: SrcSpan, }, @@ -1306,7 +1309,7 @@ impl Error { | Error::AllVariantsDeprecated { location } | Error::EchoWithNoFollowingExpression { location } | Error::DeprecatedVariantOnDeprecatedType { location } - | Error::ErlangFloatUnsafe { location } + | Error::LiteralFloatOutOfRange { location } | Error::FloatOperatorOnInts { location, .. } | Error::IntOperatorOnFloats { location, .. } | Error::StringConcatenationWithAddInt { location } @@ -1983,17 +1986,12 @@ pub fn check_javascript_int_safety(int_value: &BigInt, location: SrcSpan, proble /// -1.7976931348623157e308 to 1.7976931348623157e308 which is the allowed range for /// Erlang's floating point numbers /// -pub fn check_erlang_float_safety( - value: LiteralFloatValue, - location: SrcSpan, - problems: &mut Problems, -) { - let erl_min_float = -1.7976931348623157e308f64; - let erl_max_float = 1.7976931348623157e308f64; +pub fn check_float_safety(value: LiteralFloatValue, location: SrcSpan, problems: &mut Problems) { + let min_float = -1.7976931348623157e308f64; + let max_float = 1.7976931348623157e308f64; let float_value = value.value(); - - if float_value < erl_min_float || float_value > erl_max_float { - problems.error(Error::ErlangFloatUnsafe { location }); + if float_value < min_float || float_value > max_float { + problems.error(Error::LiteralFloatOutOfRange { location }); } } diff --git a/compiler-core/src/type_/expression.rs b/compiler-core/src/type_/expression.rs index 444182380b7..94fb6c07516 100644 --- a/compiler-core/src/type_/expression.rs +++ b/compiler-core/src/type_/expression.rs @@ -458,12 +458,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { value, float_value, } => { - if self.environment.target == Target::Erlang - && !self.current_function_definition.has_erlang_external - { - check_erlang_float_safety(float_value, location, self.problems) - } - + check_float_safety(float_value, location, self.problems); Ok(self.infer_float(value, float_value, location)) } @@ -3801,10 +3796,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { value, float_value, } => { - if self.environment.target == Target::Erlang { - check_erlang_float_safety(float_value, location, self.problems) - } - + check_float_safety(float_value, location, self.problems); Ok(Constant::Float { location, value, diff --git a/compiler-core/src/type_/pattern.rs b/compiler-core/src/type_/pattern.rs index 76c5ff85dbc..7ffc7a2c147 100644 --- a/compiler-core/src/type_/pattern.rs +++ b/compiler-core/src/type_/pattern.rs @@ -753,13 +753,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { float_value, } => { self.unify_types(type_, float(), location); - - if self.environment.target == Target::Erlang - && !self.implementations.uses_erlang_externals - { - check_erlang_float_safety(float_value, location, self.problems) - } - + check_float_safety(float_value, location, self.problems); Pattern::Float { location, value, diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float.snap index 48cc29b14d3..34c4028aa27 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float.snap @@ -6,12 +6,12 @@ expression: "-1.8e308" -1.8e308 ----- ERROR -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:1 │ 1 │ -1.8e308 │ ^^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_const.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_const.snap index f938555c959..4841d84a182 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_const.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_const.snap @@ -6,12 +6,12 @@ expression: const x = -1.8e308 const x = -1.8e308 ----- ERROR -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:11 │ 1 │ const x = -1.8e308 │ ^^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_pattern.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_pattern.snap index 6d9ef653f24..96a9a61b838 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_pattern.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__negative_out_of_range_erlang_float_in_pattern.snap @@ -22,12 +22,12 @@ error: Unknown variable The name `y` is not in scope here. -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:13 │ 1 │ let assert [-1.8e308, b] = [x, y] │ ^^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float.snap index 7c6fad8024f..ddfbfd75c9d 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float.snap @@ -6,12 +6,12 @@ expression: "1.8e308" 1.8e308 ----- ERROR -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:1 │ 1 │ 1.8e308 │ ^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_const.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_const.snap index 117542ac5ca..71a3567fe23 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_const.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_const.snap @@ -6,12 +6,12 @@ expression: const x = 1.8e308 const x = 1.8e308 ----- ERROR -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:11 │ 1 │ const x = 1.8e308 │ ^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_pattern.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_pattern.snap index 8c9dfe82043..47441c32f67 100644 --- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_pattern.snap +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__out_of_range_erlang_float_in_pattern.snap @@ -22,12 +22,12 @@ error: Unknown variable The name `y` is not in scope here. -error: Float is outside Erlang's floating point range +error: Float outside of valid range ┌─ /src/one/two.gleam:1:13 │ 1 │ let assert [1.8e308, b] = [x, y] │ ^^^^^^^ -This float value is too large to be represented by Erlang's floating point -type. To avoid this error float values must be in the range --1.7976931348623157e308 - 1.7976931348623157e308. +This float value is too large to be represented by a floating point type: +float values must be in the range -1.7976931348623157e308 - +1.7976931348623157e308. From 6207ad71613ae49676bcb95db0accb2aaa50a1b8 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 20 Nov 2025 16:41:53 +0100 Subject: [PATCH 2/4] remove now unused field --- compiler-core/src/type_/expression.rs | 2 -- compiler-core/src/type_/pattern.rs | 3 --- 2 files changed, 5 deletions(-) diff --git a/compiler-core/src/type_/expression.rs b/compiler-core/src/type_/expression.rs index 94fb6c07516..1794d7cd46b 100644 --- a/compiler-core/src/type_/expression.rs +++ b/compiler-core/src/type_/expression.rs @@ -1998,7 +1998,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> { // Ensure the pattern matches the type of the value let mut pattern_typer = pattern::PatternTyper::new( self.environment, - &self.implementations, &self.current_function_definition, &self.hydrator, self.problems, @@ -2327,7 +2326,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> { ) -> (TypedMultiPattern, Vec, bool) { let mut pattern_typer = pattern::PatternTyper::new( self.environment, - &self.implementations, &self.current_function_definition, &self.hydrator, self.problems, diff --git a/compiler-core/src/type_/pattern.rs b/compiler-core/src/type_/pattern.rs index 7ffc7a2c147..c2a5da87423 100644 --- a/compiler-core/src/type_/pattern.rs +++ b/compiler-core/src/type_/pattern.rs @@ -22,7 +22,6 @@ use std::sync::Arc; pub struct PatternTyper<'a, 'b> { environment: &'a mut Environment<'b>, - implementations: &'a Implementations, current_function: &'a FunctionDefinition, hydrator: &'a Hydrator, mode: PatternMode, @@ -98,7 +97,6 @@ enum PatternMode { impl<'a, 'b> PatternTyper<'a, 'b> { pub fn new( environment: &'a mut Environment<'b>, - implementations: &'a Implementations, current_function: &'a FunctionDefinition, hydrator: &'a Hydrator, problems: &'a mut Problems, @@ -106,7 +104,6 @@ impl<'a, 'b> PatternTyper<'a, 'b> { ) -> Self { Self { environment, - implementations, current_function, hydrator, mode: PatternMode::Initial, From f869904e3edb3275e1a61445e8fdf3c0fd4fdeb5 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 20 Nov 2025 16:39:01 +0100 Subject: [PATCH 3/4] fix changelog --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f1cc1a0dd..083d10f6228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ ``` case wibble { 0, _ -> 1 - ^^^^ Expected 1 patterns, got 2 + ^^^^ Expected 1 pattern, got 2 0 | -> 1 ^ I was expecting a pattern after this } @@ -55,8 +55,8 @@ ([Nafi](https://github.com/re-masashi)) - The lowercase bool pattern error is no longer a syntax error, but instead a - part of the analysis step. This allows the entire module to be analyzed, rather - than stopping at the syntax error. + part of the analysis step. This allows the entire module to be analyzed, + rather than stopping at the syntax error. ([mxtthias](https://github.com/mxtthias)) - Exhaustiveness checks for ints and floats now correctly handle unreachable @@ -247,7 +247,8 @@ ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) - Fixed a bug where the "pattern match on variable" code action would generate - invalid patterns by repeating a variable name already used in the same pattern. + invalid patterns by repeating a variable name already used in the same + pattern. ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) - Fixed a bug where useless comparison warnings for floats compared literal From 667cc2eb51895524862b4fc3d4640aeb54a0d493 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Fri, 21 Nov 2025 13:42:49 +0100 Subject: [PATCH 4/4] address review comment --- compiler-core/src/parse.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs index 4ee9c197078..9cb31ac91e4 100644 --- a/compiler-core/src/parse.rs +++ b/compiler-core/src/parse.rs @@ -4739,8 +4739,10 @@ impl PatternPosition { /// This allows us to implement `Eq`, which require reflexivity. /// /// Used for gleam float literals, which cannot be NaN. -/// While there is no syntax for "infinity", float literals -/// may overflow into (possibly negative) infinity on the JS target. +/// +/// While there is no syntax for "infinity", float literals might be too big and +/// overflow into infinity. This is still allowed so we can parse big literal +/// numbers and the error will be raised during the analysis phase. #[derive(Clone, Copy, Debug, PartialEq)] pub struct LiteralFloatValue(f64); @@ -4755,7 +4757,7 @@ impl LiteralFloatValue { .replace("_", "") .parse::() .ok() - .filter(|f| !f.is_nan()) + .filter(|float| !float.is_nan()) .map(LiteralFloatValue) }