Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

floating point values outside Erlang's floating point range should cause error #4118

Merged
merged 2 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
code for use expressions ending with a trailing comma.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

- Fixed a bug where floats outside of Erlang's floating point range were not
causing errors.
([shayan](https://github.com/massivefermion))

- Fixed a bug where build tool could fail to add new dependencies when
dependencies with optional dependencies are present in the manifest.
([Louis Pilfold](https://github.com/lpil))
14 changes: 2 additions & 12 deletions compiler-core/src/erlang/tests/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,12 @@ pub fn main() {
fn numbers_with_scientific_notation() {
assert_erl!(
r#"
const i = 100.001e523
const j = -100.001e-523

const k = 100.001e1_230
const l = -100.001e-1_230

const m = 100.001e123_456_789
const n = -100.001e-123_456_789
const i = 100.001e223
const j = -100.001e-223

pub fn main() {
i
j
k
l
m
n
}
"#
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
---
source: compiler-core/src/erlang/tests/numbers.rs
expression: "\nconst i = 100.001e523\nconst j = -100.001e-523\n\nconst k = 100.001e1_230\nconst l = -100.001e-1_230\n\nconst m = 100.001e123_456_789\nconst n = -100.001e-123_456_789\n\npub fn main() {\n i\n j\n k\n l\n m\n n\n}\n"
expression: "\nconst i = 100.001e223\nconst j = -100.001e-223\n\npub fn main() {\n i\n j\n}\n"
snapshot_kind: text
---
----- SOURCE CODE

const i = 100.001e523
const j = -100.001e-523

const k = 100.001e1_230
const l = -100.001e-1_230

const m = 100.001e123_456_789
const n = -100.001e-123_456_789
const i = 100.001e223
const j = -100.001e-223

pub fn main() {
i
j
k
l
m
n
}


Expand All @@ -29,12 +20,8 @@ pub fn main() {

-export([main/0]).

-file("/root/project/test/my/mod.gleam", 11).
-file("/root/project/test/my/mod.gleam", 5).
-spec main() -> float().
main() ->
100.001e523,
-100.001e-523,
100.001e1230,
-100.001e-1230,
100.001e123456789,
-100.001e-123456789.
100.001e223,
-100.001e-223.
23 changes: 22 additions & 1 deletion compiler-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,27 @@ The error from the encryption library was:
.iter()
.map(|error| {
match error {
TypeError::ErlangFloatUnsafe {
location, ..
} => Diagnostic {
title: "Float is outside Erlang's floating point 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."),
hint: None,
level: Level::Error,
location: Some(Location {
label: Label {
text: None,
span: *location,
},
path: path.clone(),
src: src.clone(),
extra_labels: vec![],
}),
},


TypeError::SrcImportingTest {
location,
src_module,
Expand Down Expand Up @@ -3874,7 +3895,7 @@ or you can publish it using a different version number"),
level: Level::Error,
location: None,
hint: Some("Please add the --replace flag if you want to replace the release.".into()),
}],
}]
}
}
}
Expand Down
29 changes: 28 additions & 1 deletion compiler-core/src/type_/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,10 @@ pub enum Error {
DeprecatedVariantOnDeprecatedType {
location: SrcSpan,
},

ErlangFloatUnsafe {
location: SrcSpan,
},
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -1040,7 +1044,8 @@ impl Error {
| Error::UseFnIncorrectArity { location, .. }
| Error::BadName { location, .. }
| Error::AllVariantsDeprecated { location }
| Error::DeprecatedVariantOnDeprecatedType { location } => location.start,
| Error::DeprecatedVariantOnDeprecatedType { location }
| Error::ErlangFloatUnsafe { location } => location.start,
Error::UnknownLabels { unknown, .. } => {
unknown.iter().map(|(_, s)| s.start).min().unwrap_or(0)
}
Expand Down Expand Up @@ -1681,3 +1686,25 @@ pub fn check_javascript_int_safety(int_value: &BigInt, location: SrcSpan, proble
problems.warning(Warning::JavaScriptIntUnsafe { location });
}
}

/// When targeting Erlang, adds an error if the given Float value is outside the range
/// -1.7976931348623157e308 to 1.7976931348623157e308 which is the allowed range for
/// Erlang's floating point numbers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we link to there these numbers were taken from in the Erlang docs please 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually can't find them anywhere in the Erlang docs. I got them from here.

///
pub fn check_erlang_float_safety(
string_value: &EcoString,
location: SrcSpan,
problems: &mut Problems,
) {
let erl_min_float = -1.7976931348623157e308f64;
let erl_max_float = 1.7976931348623157e308f64;

let float_value: f64 = string_value
.replace("_", "")
.parse()
.expect("Unable to parse string to floating point value");

if float_value < erl_min_float || float_value > erl_max_float {
problems.error(Error::ErlangFloatUnsafe { location });
}
}
18 changes: 16 additions & 2 deletions compiler-core/src/type_/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {

UntypedExpr::Float {
location, value, ..
} => Ok(self.infer_float(value, location)),
} => {
if self.environment.target == Target::Erlang
&& !self.current_function_definition.has_erlang_external
{
check_erlang_float_safety(&value, location, self.problems)
}

Ok(self.infer_float(value, location))
}

UntypedExpr::String {
location, value, ..
Expand Down Expand Up @@ -2866,7 +2874,13 @@ impl<'a, 'b> ExprTyper<'a, 'b> {

Constant::Float {
location, value, ..
} => Ok(Constant::Float { location, value }),
} => {
if self.environment.target == Target::Erlang {
check_erlang_float_safety(&value, location, self.problems)
}

Ok(Constant::Float { location, value })
}

Constant::String {
location, value, ..
Expand Down
7 changes: 7 additions & 0 deletions compiler-core/src/type_/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,13 @@ impl<'a, 'b> PatternTyper<'a, 'b> {

Pattern::Float { location, value } => {
unify(type_, float()).map_err(|e| convert_unify_error(e, location))?;

if self.environment.target == Target::Erlang
&& !self.implementations.uses_erlang_externals
{
check_erlang_float_safety(&value, location, self.problems)
}

Ok(Pattern::Float { location, value })
}

Expand Down
30 changes: 30 additions & 0 deletions compiler-core/src/type_/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2655,3 +2655,33 @@ todo
"#
);
}

#[test]
fn out_of_range_erlang_float() {
assert_error!(r#"1.8e308"#);
}

#[test]
fn out_of_range_erlang_float_in_pattern() {
assert_error!(r#"let assert [1.8e308, b] = [x, y]"#);
}

#[test]
fn out_of_range_erlang_float_in_const() {
assert_module_error!(r#"const x = 1.8e308"#);
}

#[test]
fn negative_out_of_range_erlang_float() {
assert_error!(r#"-1.8e308"#);
}

#[test]
fn negative_out_of_range_erlang_float_in_pattern() {
assert_error!(r#"let assert [-1.8e308, b] = [x, y]"#);
}

#[test]
fn negative_out_of_range_erlang_float_in_const() {
assert_module_error!(r#"const x = -1.8e308"#);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: "-1.8e308"
snapshot_kind: text
---
----- SOURCE CODE
-1.8e308

----- ERROR
error: Float is outside Erlang's floating point 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: const x = -1.8e308
snapshot_kind: text
---
----- SOURCE CODE
const x = -1.8e308

----- ERROR
error: Float is outside Erlang's floating point 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: "let assert [-1.8e308, b] = [x, y]"
snapshot_kind: text
---
----- SOURCE CODE
let assert [-1.8e308, b] = [x, y]

----- ERROR
error: Unknown variable
┌─ /src/one/two.gleam:1:29
1 │ let assert [-1.8e308, b] = [x, y]
│ ^

The name `x` is not in scope here.

error: Float is outside Erlang's floating point 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: "1.8e308"
snapshot_kind: text
---
----- SOURCE CODE
1.8e308

----- ERROR
error: Float is outside Erlang's floating point 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: const x = 1.8e308
snapshot_kind: text
---
----- SOURCE CODE
const x = 1.8e308

----- ERROR
error: Float is outside Erlang's floating point 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: compiler-core/src/type_/tests/errors.rs
expression: "let assert [1.8e308, b] = [x, y]"
snapshot_kind: text
---
----- SOURCE CODE
let assert [1.8e308, b] = [x, y]

----- ERROR
error: Unknown variable
┌─ /src/one/two.gleam:1:28
1 │ let assert [1.8e308, b] = [x, y]
│ ^

The name `x` is not in scope here.

error: Float is outside Erlang's floating point 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.
Loading