[m-rev.] for review: Don't cast int32/uint32 to MR_Word in foreign code input assignments.

Peter Wang novalazy at gmail.com
Thu Oct 1 15:45:31 AEST 2026


On Thu, 01 Oct 2026 14:35:44 +1000 "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> 
> 
> On Thu,  1 Oct 2026 14:13:29 +1000, Peter Wang <novalazy at gmail.com> wrote:
> > This fixes gcc warnings on 64-bit platforms of the form:
> > 
> >     warning: overflow in conversion from ‘long long unsigned int’ to
> >     ‘int32_t’ {aka ‘int’} changes value from ‘18446744071562067968’ to
> >     ‘-2147483648’ [-Woverflow]
> > 
> > for the generated code:
> > 
> >     I32 = ((MR_Word) INT32_MIN);
> > 
> > compiler/llds_out_instr.m:
> >     In output_foreign_proc_inputs, output int32/uint32 input values
> >     without a cast to MR_Word.
> 
> For this to work by design instead of by accident, we have to ensure that
> the type of the integer we output here is the same as, or at least is
> automatically castable to, the declared type of the foreign language
> variable representing that input arg. That type is decided by whatever
> code sets the third field of the foreign_proc_arg_decl given to
> output_foreign_proc_decls, which is not in that file. You should find
> that code, and add a link to it here.

Do you mean the 2nd field of foreign_proc_arg_decl?
That code is in make_foreign_proc_decl:

        (
            BoxPolicy = bp_native_if_possible,
            OrigTypeString = exported_type_to_c_string(ModuleInfo, OrigType)
        ;
            BoxPolicy = bp_always_boxed,
            OrigTypeString = "MR_Word"
        ),
        ArgDecl = foreign_proc_arg_decl(OrigType, OrigTypeString, ArgName),

exported_type_to_c_string calls exported_builtin_type_to_c_string
which returns the C types for the fixed sized integer types.

> I am surprised that we do not treat 8 and 16 bit ints/uints the same way
> we now treat 32 and 64 bit. Why? If writing them out as lt_word is fine,
> there should be a comment explaining *why* it is fine.

You're right, 8- and 16-bit ints have the same issue.
Here is an updated patch.

Peter

---

Don't cast fixed sized int values to MR_Word in foreign code input assignments.

This fixes gcc warnings of the form:

    warning: overflow in conversion from ‘long long unsigned int’ to
    ‘int32_t’ {aka ‘int’} changes value from ‘18446744071562067968’ to
    ‘-2147483648’ [-Woverflow]

for the generated code:

    I32 = ((MR_Word) INT32_MIN);

compiler/llds_out_instr.m:
    In output_foreign_proc_inputs, output fixed sized integers
    without a cast to MR_Word.

hard_coded/Mmakefile:
hard_coded/foreign_proc_min_int.exp:
hard_coded/foreign_proc_min_int.m:
    Add test case.

diff --git a/compiler/llds_out_instr.m b/compiler/llds_out_instr.m
index 9eb99e26af..53507f959e 100644
--- a/compiler/llds_out_instr.m
+++ b/compiler/llds_out_instr.m
@@ -2010,23 +2010,36 @@ output_foreign_proc_input(Info, Stream, Input, !IO) :-
                     BuiltinType = builtin_type_float,
                     output_rval_as_type(Info, Rval, lt_float, Stream, !IO)
                 ;
-                    BuiltinType = builtin_type_int(int_type_int64),
-                    output_rval_as_type(Info, Rval,
-                        lt_int(int_type_int64), Stream, !IO)
-                ;
-                    BuiltinType = builtin_type_int(int_type_uint64),
-                    output_rval_as_type(Info, Rval,
-                        lt_int(int_type_uint64), Stream, !IO)
+                    (
+                        BuiltinType = builtin_type_int(int_type_int8),
+                        DesiredType = lt_int(int_type_int8)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_uint8),
+                        DesiredType = lt_int(int_type_uint8)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_int16),
+                        DesiredType = lt_int(int_type_int16)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_uint16),
+                        DesiredType = lt_int(int_type_uint16)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_int32),
+                        DesiredType = lt_int(int_type_int32)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_uint32),
+                        DesiredType = lt_int(int_type_uint32)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_int64),
+                        DesiredType = lt_int(int_type_int64)
+                    ;
+                        BuiltinType = builtin_type_int(int_type_uint64),
+                        DesiredType = lt_int(int_type_uint64)
+                    ),
+                    output_rval_as_type(Info, Rval, DesiredType, Stream, !IO)
                 ;
                     ( BuiltinType = builtin_type_char
                     ; BuiltinType = builtin_type_int(int_type_int)
                     ; BuiltinType = builtin_type_int(int_type_uint)
-                    ; BuiltinType = builtin_type_int(int_type_int8)
-                    ; BuiltinType = builtin_type_int(int_type_uint8)
-                    ; BuiltinType = builtin_type_int(int_type_int16)
-                    ; BuiltinType = builtin_type_int(int_type_uint16)
-                    ; BuiltinType = builtin_type_int(int_type_int32)
-                    ; BuiltinType = builtin_type_int(int_type_uint32)
                     ),
                     output_rval_as_type(Info, Rval, lt_word, Stream, !IO)
                 )
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 4c79a5275c..b8e39798d9 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -171,6 +171,7 @@ ORDINARY_PROGS = \
 	foreign_enum_switch \
 	foreign_import_module \
 	foreign_name_mutable \
+	foreign_proc_min_int \
 	foreign_type_1 \
 	foreign_type_2 \
 	foreign_type_3 \
diff --git a/tests/hard_coded/foreign_proc_min_int.exp b/tests/hard_coded/foreign_proc_min_int.exp
new file mode 100644
index 0000000000..49d467d6bf
--- /dev/null
+++ b/tests/hard_coded/foreign_proc_min_int.exp
@@ -0,0 +1,3 @@
+-128
+-32768
+-2147483648
diff --git a/tests/hard_coded/foreign_proc_min_int.m b/tests/hard_coded/foreign_proc_min_int.m
new file mode 100644
index 0000000000..708e1bbda0
--- /dev/null
+++ b/tests/hard_coded/foreign_proc_min_int.m
@@ -0,0 +1,71 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module foreign_proc_min_int.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+:- import_module int8.
+:- import_module int16.
+:- import_module int32.
+:- import_module int64.
+
+main(!IO) :-
+    I8 = cast_int8(-128_i8),
+    io.print_line(I8, !IO),
+
+    I16 = cast_int16(-32768_i16),
+    io.print_line(I16, !IO),
+
+    I32 = cast_int32(-2147483648_i32),
+    io.print_line(I32, !IO).
+
+%---------------------------------------------------------------------------%
+
+:- func cast_int8(int8) = int64.
+
+:- pragma foreign_proc("C",
+    cast_int8(I8::in) = (I64::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    I64 = (int64_t) I8;
+").
+
+cast_int8(I) = int64.from_int(int8.cast_to_int(I)).
+
+%---------------------------------------------------------------------------%
+
+:- func cast_int16(int16) = int64.
+
+:- pragma foreign_proc("C",
+    cast_int16(I16::in) = (I64::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    I64 = (int64_t) I16;
+").
+
+cast_int16(I) = int64.from_int(int16.cast_to_int(I)).
+
+%---------------------------------------------------------------------------%
+
+:- func cast_int32(int32) = int64.
+
+:- pragma foreign_proc("C",
+    cast_int32(I32::in) = (I64::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    I64 = (int64_t) I32;
+").
+
+cast_int32(I) = int64.from_int(int32.cast_to_int(I)).
+
+%---------------------------------------------------------------------------%
-- 
2.54.0



More information about the reviews mailing list