From eac1e3d5732ccf0cc89c0726afa5ffea9be90dcd Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Wed, 7 Oct 2026 06:15:27 +0000 Subject: [PATCH 1/2] Fix the sign of @inline bigint constants Bigint_utils.parse_bigint returns whether the literal is positive, but Ast_external_mk.inline_bigint bound that flag as `negative`, so the stored inline constant had its sign inverted. Translation passed the flag straight through as Lambda's positive `sign`, inverting it back, so generated JavaScript was correct; signatures and error messages printed `@inline(-12n)` for `@inline(12n)`. Store the real `negative` flag and convert it to Lambda's sign in translcore. Signed-off-by: Christoph Knittel Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + compiler/frontend/ast_external_mk.ml | 4 +-- compiler/ml/translcore.ml | 2 +- tests/ERROR_VARIANTS.md | 2 +- ...ine_bigint_signature_mismatch.res.expected | 31 +++++++++++++++++++ .../inline_bigint_signature_mismatch.res | 7 +++++ tests/tests/src/inline_const.mjs | 10 ++++++ tests/tests/src/inline_const.res | 9 ++++++ tests/tests/src/inline_const.resi | 9 ++++++ 9 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/build_tests/super_errors/expected/inline_bigint_signature_mismatch.res.expected create mode 100644 tests/build_tests/super_errors/fixtures/inline_bigint_signature_mismatch.res diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e60a1895a..8ab88b5cc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Keep comments on labelled parameters with default values, and before `=?` in optional arrow-type arguments, when formatting. https://github.com/rescript-lang/rescript/pull/8690 - Fix invalid JavaScript that exported names left unbound when a module's toplevel always throws. https://github.com/rescript-lang/rescript/pull/8692 - Make module inclusion error messages independent of the length of the source file path. https://github.com/rescript-lang/rescript/pull/8691 +- Fix the sign of `@inline` bigint constants printed in signatures and error messages, which showed `@inline(-12n)` for `@inline(12n)`. https://github.com/rescript-lang/rescript/pull/8732 #### :memo: Documentation diff --git a/compiler/frontend/ast_external_mk.ml b/compiler/frontend/ast_external_mk.ml index 817270fedf..f0c4e7cbf7 100644 --- a/compiler/frontend/ast_external_mk.ml +++ b/compiler/frontend/ast_external_mk.ml @@ -55,7 +55,7 @@ let inline_bool b = inline_const (Const_bool b) let inline_int i = inline_const (Const_int i) let inline_bigint s = - let negative, digits = Bigint_utils.parse_bigint s in - inline_const (Const_bigint {negative; digits}) + let positive, digits = Bigint_utils.parse_bigint s in + inline_const (Const_bigint {negative = not positive; digits}) let inline_float s = inline_const (Const_float s) diff --git a/compiler/ml/translcore.ml b/compiler/ml/translcore.ml index ac0c4bac27..310ac0aba8 100644 --- a/compiler/ml/translcore.ml +++ b/compiler/ml/translcore.ml @@ -463,7 +463,7 @@ let lambda_of_inline_const (c : External_ffi_types.inline_const) : | Const_bool true -> Const_js_true | Const_bool false -> Const_js_false | Const_int i -> Const_int i - | Const_bigint {negative; digits} -> Const_bigint (negative, digits) + | Const_bigint {negative; digits} -> Const_bigint (not negative, digits) | Const_float f -> Const_float f (* The argument of the dynamic-import primitive is a module reference, diff --git a/tests/ERROR_VARIANTS.md b/tests/ERROR_VARIANTS.md index 3901ef2c98..bcfb398770 100644 --- a/tests/ERROR_VARIANTS.md +++ b/tests/ERROR_VARIANTS.md @@ -366,7 +366,7 @@ Wrapper symptoms attached to inclusion failures. Source: `type symptom` in [incl | Variant | Status | Fixture | Notes | |---|---|---|---| | `Missing_field` | ✓ | `super_errors_multi/Iface_missing_value` | | -| `Value_descriptions` | ✓ | `super_errors_multi/Iface_value_descriptions`, `super_errors_multi/Iface_value_arity_mismatch`, `super_errors_multi/Smoke_interface_mismatch`, `super_errors_multi/Cross_external_spec_mismatch`, `super_errors_multi/Cross_external_payload_name`, `super_errors_multi/Cross_external_import_attrs`, `module_sig_value_arity_mismatch*.res`, `object_value_signature_set_mismatch.res` | Arity mismatches print a dedicated hint (implementation vs interface argument counts), including through aliases and nested function types. The object fixture pins that a value signature cannot drop `@set` from an object type (moregeneral requires equal field mutability). | +| `Value_descriptions` | ✓ | `super_errors_multi/Iface_value_descriptions`, `super_errors_multi/Iface_value_arity_mismatch`, `super_errors_multi/Smoke_interface_mismatch`, `super_errors_multi/Cross_external_spec_mismatch`, `super_errors_multi/Cross_external_payload_name`, `super_errors_multi/Cross_external_import_attrs`, `module_sig_value_arity_mismatch*.res`, `object_value_signature_set_mismatch.res`, `inline_bigint_signature_mismatch.res` | Arity mismatches print a dedicated hint (implementation vs interface argument counts), including through aliases and nested function types. The object fixture pins that a value signature cannot drop `@set` from an object type (moregeneral requires equal field mutability). The inline-bigint fixture pins the sign printed for `@inline()` values. | | `Type_declarations` | ✓ | `super_errors_multi/Iface_type_decl_record`, `super_errors_multi/Iface_type_decl_variant`, `RecordInclusion.res`, `type_decl_function_arity_mismatch.res` | | | `Extension_constructors` | ✓ | `super_errors_multi/Iface_extension_constructors` | | | `Module_types` | ✓ | `super_errors_multi/Iface_module_types` | | diff --git a/tests/build_tests/super_errors/expected/inline_bigint_signature_mismatch.res.expected b/tests/build_tests/super_errors/expected/inline_bigint_signature_mismatch.res.expected new file mode 100644 index 0000000000..4f1d04d79d --- /dev/null +++ b/tests/build_tests/super_errors/expected/inline_bigint_signature_mismatch.res.expected @@ -0,0 +1,31 @@ + + We've found a bug for you! + /.../fixtures/inline_bigint_signature_mismatch.res:4:5-7:1 + + 2 │ @inline(12n) let pos: bigint + 3 │ @inline(-5n) let neg: bigint + 4 │ } = { + 5 │  let pos = 12n + 6 │  let neg = -5n + 7 │ } + 8 │ + + Signature mismatch: + Modules do not match: + { + let pos: bigint + let neg: bigint +} + is not included in + { + @inline(12n) let pos: bigint + @inline(-5n) let neg: bigint +} + Values do not match: + let pos: bigint + is not included in + @inline(12n) let pos: bigint + /.../fixtures/inline_bigint_signature_mismatch.res:2:3-30: + Expected declaration + /.../fixtures/inline_bigint_signature_mismatch.res:5:7-9: + Actual declaration \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/inline_bigint_signature_mismatch.res b/tests/build_tests/super_errors/fixtures/inline_bigint_signature_mismatch.res new file mode 100644 index 0000000000..a372a171c6 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/inline_bigint_signature_mismatch.res @@ -0,0 +1,7 @@ +module M: { + @inline(12n) let pos: bigint + @inline(-5n) let neg: bigint +} = { + let pos = 12n + let neg = -5n +} diff --git a/tests/tests/src/inline_const.mjs b/tests/tests/src/inline_const.mjs index 1fb24a7650..5c853d4545 100644 --- a/tests/tests/src/inline_const.mjs +++ b/tests/tests/src/inline_const.mjs @@ -8,6 +8,15 @@ console.log([ 3e-6 ]); +function BigintConsts(X) { + return { + both: [ + 12n, + -5n + ] + }; +} + let x = true; function N1(funarg) { @@ -24,5 +33,6 @@ export { N1, h, hh, + BigintConsts, } /* Not a pure module */ diff --git a/tests/tests/src/inline_const.res b/tests/tests/src/inline_const.res index 9c44e0c866..2112455d4a 100644 --- a/tests/tests/src/inline_const.res +++ b/tests/tests/src/inline_const.res @@ -37,3 +37,12 @@ let (a, b, c, d, e) = (f, f1, f2, f3, f4) @inline let f6 = 1 let () = Console.log((xx, xx0)) + +module BigintConsts = ( + X: { + @inline(12n) let pos: bigint + @inline(-5n) let neg: bigint + }, +) => { + let both = (X.pos, X.neg) +} diff --git a/tests/tests/src/inline_const.resi b/tests/tests/src/inline_const.resi index 7b7bab2072..111d499a12 100644 --- a/tests/tests/src/inline_const.resi +++ b/tests/tests/src/inline_const.resi @@ -23,3 +23,12 @@ let hh: string @inline(true) let f5: bool @inline(1) let f6: int + +module BigintConsts: ( + X: { + @inline(12n) let pos: bigint + @inline(-5n) let neg: bigint + }, +) => { + let both: (bigint, bigint) +} From 3b2c6f1492cff75c3cc2a8fcfc7aa01ae7f4f551 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Wed, 7 Oct 2026 06:20:33 +0000 Subject: [PATCH 2/2] Store @inline bigint signs as positive, like every other layer Bigint_utils.parse_bigint, Asttypes.Const_bigint, Lambda.Const_bigint and Js_op.bigint_lit all record whether a bigint is positive. The inline constant was the only one recording `negative`, which is how its sign got inverted. Use `positive` there too, so the value passes through the frontend and translation unchanged. Signed-off-by: Christoph Knittel Co-Authored-By: Claude Opus 5.5 --- compiler/frontend/ast_external_mk.ml | 2 +- compiler/ml/external_ffi_types.ml | 2 +- compiler/ml/external_ffi_types.mli | 2 +- compiler/ml/translcore.ml | 2 +- compiler/syntax/src/res_outcome_printer.ml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/frontend/ast_external_mk.ml b/compiler/frontend/ast_external_mk.ml index f0c4e7cbf7..6614ae8698 100644 --- a/compiler/frontend/ast_external_mk.ml +++ b/compiler/frontend/ast_external_mk.ml @@ -56,6 +56,6 @@ let inline_int i = inline_const (Const_int i) let inline_bigint s = let positive, digits = Bigint_utils.parse_bigint s in - inline_const (Const_bigint {negative = not positive; digits}) + inline_const (Const_bigint {positive; digits}) let inline_float s = inline_const (Const_float s) diff --git a/compiler/ml/external_ffi_types.ml b/compiler/ml/external_ffi_types.ml index d4d8ae23d4..fbfd427613 100644 --- a/compiler/ml/external_ffi_types.ml +++ b/compiler/ml/external_ffi_types.ml @@ -82,7 +82,7 @@ type inline_const = source spelling is not needed after FFI processing. *) | Const_bool of bool | Const_int of int32 - | Const_bigint of {negative: bool; digits: string} + | Const_bigint of {positive: bool; digits: string} | Const_float of string type t = diff --git a/compiler/ml/external_ffi_types.mli b/compiler/ml/external_ffi_types.mli index fa5204f9de..a4b9afa190 100644 --- a/compiler/ml/external_ffi_types.mli +++ b/compiler/ml/external_ffi_types.mli @@ -82,7 +82,7 @@ type inline_const = source spelling is not needed after FFI processing. *) | Const_bool of bool | Const_int of int32 - | Const_bigint of {negative: bool; digits: string} + | Const_bigint of {positive: bool; digits: string} | Const_float of string type t = private diff --git a/compiler/ml/translcore.ml b/compiler/ml/translcore.ml index 310ac0aba8..2288d1fdaa 100644 --- a/compiler/ml/translcore.ml +++ b/compiler/ml/translcore.ml @@ -463,7 +463,7 @@ let lambda_of_inline_const (c : External_ffi_types.inline_const) : | Const_bool true -> Const_js_true | Const_bool false -> Const_js_false | Const_int i -> Const_int i - | Const_bigint {negative; digits} -> Const_bigint (not negative, digits) + | Const_bigint {positive; digits} -> Const_bigint (positive, digits) | Const_float f -> Const_float f (* The argument of the dynamic-import primitive is a module reference, diff --git a/compiler/syntax/src/res_outcome_printer.ml b/compiler/syntax/src/res_outcome_printer.ml index 8b9905d6b2..4a4d359506 100644 --- a/compiler/syntax/src/res_outcome_printer.ml +++ b/compiler/syntax/src/res_outcome_printer.ml @@ -483,8 +483,8 @@ let print_inline_const_doc (c : External_ffi_types.inline_const) = | Const_string s -> print_string_literal_doc s | Const_bool b -> Doc.text (if b then "true" else "false") | Const_int i -> Doc.text (Int32.to_string i) - | Const_bigint {negative; digits} -> - Doc.text ((if negative then "-" else "") ^ digits ^ "n") + | Const_bigint {positive; digits} -> + Doc.text ((if positive then "" else "-") ^ digits ^ "n") | Const_float f -> Doc.text f let print_external_module_doc (emn : External_ffi_types.external_module_name) =