Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#### :bug: Bug fix

- Fix integer range patterns, including nested record and exception patterns, incorrectly matching values below the range. https://github.com/rescript-lang/rescript/pull/8714
- Fix integer range patterns at int32 boundaries and wrapped nondecimal patterns matching incorrect values. https://github.com/rescript-lang/rescript/pull/8717 https://github.com/rescript-lang/rescript/pull/8858
Comment thread
cknitt marked this conversation as resolved.
- Integer patterns that alias the same int32 value (e.g. `0xFFFF_FFFF` and `-1`) may now produce unused-case warnings. https://github.com/rescript-lang/rescript/pull/8717 https://github.com/rescript-lang/rescript/pull/8858

#### :nail_care: Polish

Expand Down
14 changes: 10 additions & 4 deletions compiler/ml/matching.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ open Printf

let dbg = false

let int32_low = Int32.to_int Int32.min_int
let int32_high = Int32.to_int Int32.max_int

(* See Peyton-Jones, ``The Implementation of functional programming
languages'', chapter 5. *)
(*
Expand Down Expand Up @@ -2032,7 +2035,10 @@ let combine_constant names loc arg cst partial ctx def
| _ -> assert false)
const_lambda_list
in
call_switcher loc fail arg min_int max_int int_lambda_list names
(* Match intervals must use ReScript's int32 bounds, not the host's.
Otherwise the upper fail interval starts at 2147483648, which wraps
when converted to a Lambda integer constant. *)
call_switcher loc fail arg int32_low int32_high int_lambda_list names
| Const_char _ ->
let int_lambda_list =
List.map
Expand Down Expand Up @@ -2212,13 +2218,13 @@ let combine_constructor sw_names loc arg ex_pat cstr partial ctx def

let make_test_sequence_variant_constant fail arg int_lambda_list =
let _, (cases, actions) =
as_interval fail min_int max_int
as_interval fail int32_low int32_high
(List.map (fun (a, (_, c)) -> (a, c)) int_lambda_list)
in
Switcher.test_sequence arg cases actions

let call_switcher_variant_constant loc fail arg int_lambda_list names =
call_switcher loc fail arg min_int max_int
call_switcher loc fail arg int32_low int32_high
(List.map (fun (a, (_, c)) -> (a, c)) int_lambda_list)
names

Expand All @@ -2229,7 +2235,7 @@ let call_switcher_variant_constr loc fail arg int_lambda_list names =
Pgenval,
v,
Lprim (Pfield (0, Fld_poly_var_tag), [arg], loc),
call_switcher loc fail (Lvar v) min_int max_int
call_switcher loc fail (Lvar v) int32_low int32_high
(List.map (fun (a, (_, c)) -> (a, c)) int_lambda_list)
names )

Expand Down
7 changes: 6 additions & 1 deletion compiler/ml/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@ let type_constant = function
let constant : Parsetree.constant -> (Asttypes.constant, error) result =
function
| Pconst_integer (i, None) -> (
try Ok (Const_int (Misc.Int_literal_converter.int i))
try
(* After the existing host-dependent literal parsing, pattern grouping
and intervals must see Lambda's int32 value, so -0xFFFF_FFFF and 1
are the same key. This does not change overflow diagnostics. *)
let value = Misc.Int_literal_converter.int i in
Ok (Const_int (Int32.to_int (Int32.of_int value)))
with Failure _ -> Error (Literal_overflow "int"))
| Pconst_integer (i, Some 'n') ->
let sign, i = Bigint_utils.parse_bigint i in
Expand Down
39 changes: 38 additions & 1 deletion tests/build_tests/super_errors/expected/intoverflow.res.expected
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,42 @@
11 │ let v9: int = 0x1_0000_0000 // overflow
12 │ let v10: int = -0x1_0000_0000 // underflow
13 │
14 │ // Wrapped aliases are redundant after int32 normalization.

Integer literal exceeds the range of representable integers of type int
Integer literal exceeds the range of representable integers of type int


Warning number 107
/.../fixtures/intoverflow.res:17:5-14

15 │ let wrappedAlias = value =>
16 │ switch value {
17 │ | 4294967295 => 1
18 │ | -1 => 2
19 │ | _ => 0

Integer literal exceeds the range of representable integers of type int


Warning number 11
/.../fixtures/intoverflow.res:18:5-6

16 ┆ switch value {
17 ┆ | 4294967295 => 1
18 ┆ | -1 => 2
19 ┆ | _ => 0
20 ┆ }

this match case is unused.


Warning number 11
/.../fixtures/intoverflow.res:25:5

23 ┆ switch value {
24 ┆ | -0xFFFF_FFFF => 1
25 ┆ | 1 => 2
26 ┆ | _ => 0
27 ┆ }

this match case is unused.
15 changes: 15 additions & 0 deletions tests/build_tests/super_errors/fixtures/intoverflow.res
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,18 @@ let v7: int = 0xFFFF_FFFF // -1
let v8: int = -0xFFFF_FFFF // 1
let v9: int = 0x1_0000_0000 // overflow
let v10: int = -0x1_0000_0000 // underflow

// Wrapped aliases are redundant after int32 normalization.
let wrappedAlias = value =>
switch value {
| 4294967295 => 1
| -1 => 2
| _ => 0
}

let nondecimalAlias = value =>
switch value {
| -0xFFFF_FFFF => 1
| 1 => 2
| _ => 0
}
35 changes: 35 additions & 0 deletions tests/ounit_tests/ounit_integer_constant_tests.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
open OUnit

let suites =
__FILE__
>::: [
( "integer pattern constants agree with Lambda" >:: fun _ ->
List.iter
(fun (literal, expected) ->
match Typecore.constant (Pconst_integer (literal, None)) with
| Ok (Asttypes.Const_int value as typed) ->
assert_equal expected value;
assert_equal
(Lam_constant.Const_int
{i = Int32.of_int expected; comment = None})
(Lam_constant_convert.convert_constant
(Lambda.Const_base typed))
| _ -> assert_failure ("integer literal was rejected: " ^ literal))
[
("-0xFFFF_FFFF", 1);
("0xFFFF_FFFF", -1);
("2147483647", 2147483647);
("-2147483648", -2147483648);
] );
( "overflow parsing keeps its existing host limits" >:: fun _ ->
let result =
Typecore.constant (Pconst_integer ("2147483649", None))
in
assert_equal
(if Sys.int_size > 32 then Ok (Asttypes.Const_int (-2147483647))
else Error (Typecore.Literal_overflow "int"))
result;
assert_equal (Error (Typecore.Literal_overflow "int"))
(Typecore.constant
(Pconst_integer ("999999999999999999999999999", None))) );
]
1 change: 1 addition & 0 deletions tests/ounit_tests/ounit_tests_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let suites =
Ounit_bsb_regex_tests.suites;
Ounit_bsb_pkg_tests.suites;
Ounit_util_tests.suites;
Ounit_integer_constant_tests.suites;
Ounit_jsx_loc_tests.suites;
Ounit_analysis_references_tests.suites;
]
Expand Down
Loading
Loading