[m-rev.] for review: lift restriction on formatting 64-bit integers

Julien Fischer jfischer at opturion.com
Wed Jan 6 16:25:53 AEDT 2021


For review by anyone.

---------------------

Lift restriction on formatting 64-bit integers.

Zoltan's recent addition of support for formatting fixed size integer types
using string.format and friends works by casting the fixed size integer value
to an int or uint value and then re-using the existing code we already have for
formatting those. This works in all cases except when formatting 64-bit integer
types on systems where int / uint is a 32-bit quantity (notably, both the C#
and Java backends). This diff lifts that restrictions.

library/string.format.m:
     Add support for formatting 64-bit integers without having to cast them
     to an int or uint.

     Export new format predicates for use by the code generated by
     compiler/format_call.m.

compiler/format_call.m:
     Generate calls to the 64-bit versions of the format_*_component predicates
     where necessary.

compiler/simplify_proc.m:
     Update the list of predicates that may be introduced by the compiler.

NEWS:
     Delete the mention of the restriction.

tests/hard_coded/opt_format.{m,exp}:
     Extend this test to cover 64-bit integers.

Julien.

diff --git a/NEWS b/NEWS
index 9caa794..8f879a9 100644
--- a/NEWS
+++ b/NEWS
@@ -171,9 +171,6 @@ Changes to the Mercury standard library
    on values of not just the word sized integer types `int` and `uint`, but on
    sized versions of them as well.

-  For now, the printing of 64 bit ints and uints is supported only
-  on 64 bit platforms.
-
  ### Changes to the `term` module

  * The following obsolete predicates and functions have been removed:
diff --git a/compiler/format_call.m b/compiler/format_call.m
index d3a99ac..155c5a8 100644
--- a/compiler/format_call.m
+++ b/compiler/format_call.m
@@ -2,7 +2,7 @@
  % vim: ft=mercury ts=4 sw=4 et
  %---------------------------------------------------------------------------%
  % Copyright (C) 2006-2012 The University of Melbourne.
-% Copyright (C) 2015-2020 The Mercury team.
+% Copyright (C) 2015-2021 The Mercury team.
  % This file may only be copied under the terms of the GNU General
  % Public License - see the file COPYING in the Mercury distribution.
  %---------------------------------------------------------------------------%
@@ -1866,8 +1866,13 @@ represent_spec(ModuleInfo, Spec, MaybeResultVar, ResultVar, Goals, Context,
              !VarSet, !VarTypes),
          maybe_build_prec_arg(MaybePrec, PrecSuffix, PrecVars, PrecGoals,
              !VarSet, !VarTypes),
+        ( if IntSize = int_size_64 then
+            FormatPredBase = "format_signed_int64_component"
+        else
+            FormatPredBase = "format_signed_int_component"
+        ),
          generate_simple_call(ModuleInfo, mercury_string_format_module,
-            "format_signed_int_component" ++ WidthSuffix ++ PrecSuffix,
+            FormatPredBase ++ WidthSuffix ++ PrecSuffix,
              pf_predicate, only_mode, detism_det, purity_pure,
              [FlagsVar] ++ WidthVars ++ PrecVars ++ [ValueVar, ResultVar], [],
              instmap_delta_bind_var(ResultVar), Context, CallGoal),
@@ -1888,8 +1893,13 @@ represent_spec(ModuleInfo, Spec, MaybeResultVar, ResultVar, Goals, Context,
          maybe_build_prec_arg(MaybePrec, PrecSuffix, PrecVars, PrecGoals,
              !VarSet, !VarTypes),
          build_int_base_arg(Base, BaseVar, BaseGoal, !VarSet, !VarTypes),
+        ( if IntSize = int_size_64 then
+            FormatPredBase = "format_unsigned_int64_component"
+        else
+            FormatPredBase = "format_unsigned_int_component"
+        ),
          generate_simple_call(ModuleInfo, mercury_string_format_module,
-            "format_unsigned_int_component" ++ WidthSuffix ++ PrecSuffix,
+            FormatPredBase ++ WidthSuffix ++ PrecSuffix,
              pf_predicate, only_mode, detism_det, purity_pure,
              [FlagsVar] ++ WidthVars ++ PrecVars ++
                  [BaseVar, ValueVar, ResultVar], [],
@@ -1911,8 +1921,13 @@ represent_spec(ModuleInfo, Spec, MaybeResultVar, ResultVar, Goals, Context,
          maybe_build_prec_arg(MaybePrec, PrecSuffix, PrecVars, PrecGoals,
              !VarSet, !VarTypes),
          build_int_base_arg(Base, BaseVar, BaseGoal, !VarSet, !VarTypes),
+        ( if UIntSize = uint_size_64 then
+            FormatPredBase = "format_uint64_component"
+        else
+            FormatPredBase = "format_uint_component"
+        ),
          generate_simple_call(ModuleInfo, mercury_string_format_module,
-            "format_uint_component" ++ WidthSuffix ++ PrecSuffix,
+            FormatPredBase ++ WidthSuffix ++ PrecSuffix,
              pf_predicate, only_mode, detism_det, purity_pure,
              [FlagsVar] ++ WidthVars ++ PrecVars ++
                  [BaseVar, ValueVar, ResultVar], [],
@@ -1945,17 +1960,18 @@ represent_spec(ModuleInfo, Spec, MaybeResultVar, ResultVar, Goals, Context,
      int_size::in, prog_var::in, prog_var::out, list(hlds_goal)::out,
      prog_varset::in, prog_varset::out, vartypes::in, vartypes::out) is det.

-cast_int_value_var_if_needed(ModuleInfo, Context, UIntSize,
+cast_int_value_var_if_needed(ModuleInfo, Context, IntSize,
          OrigValueVar, ValueVar, ValueCastGoals, !VarSet, !VarTypes) :-
      (
-        UIntSize = int_size_word,
+        ( IntSize = int_size_word
+        ; IntSize = int_size_64
+        ),
          ValueVar = OrigValueVar,
          ValueCastGoals = []
      ;
-        ( UIntSize = int_size_8, Size = "8"
-        ; UIntSize = int_size_16, Size = "16"
-        ; UIntSize = int_size_32, Size = "32"
-        ; UIntSize = int_size_64, Size = "64"
+        ( IntSize = int_size_8, Size = "8"
+        ; IntSize = int_size_16, Size = "16"
+        ; IntSize = int_size_32, Size = "32"
          ),
          varset.new_var(ValueVar, !VarSet),
          add_var_type(ValueVar, int_type, !VarTypes),
@@ -1974,14 +1990,15 @@ cast_int_value_var_if_needed(ModuleInfo, Context, UIntSize,
  cast_uint_value_var_if_needed(ModuleInfo, Context, UIntSize,
          OrigValueVar, ValueVar, ValueCastGoals, !VarSet, !VarTypes) :-
      (
-        UIntSize = uint_size_word,
+        ( UIntSize = uint_size_word
+        ; UIntSize = uint_size_64
+        ),
          ValueVar = OrigValueVar,
          ValueCastGoals = []
      ;
          ( UIntSize = uint_size_8, Size = "8"
          ; UIntSize = uint_size_16, Size = "16"
          ; UIntSize = uint_size_32, Size = "32"
-        ; UIntSize = uint_size_64, Size = "64"
          ),
          varset.new_var(ValueVar, !VarSet),
          add_var_type(ValueVar, uint_type, !VarTypes),
diff --git a/compiler/simplify_proc.m b/compiler/simplify_proc.m
index 000c88c..a2f429a 100644
--- a/compiler/simplify_proc.m
+++ b/compiler/simplify_proc.m
@@ -891,10 +891,22 @@ simplify_may_introduce_calls(ModuleName, PredName, _Arity) :-
          ; PredName = "format_unsigned_int_component_nowidth_prec"
          ; PredName = "format_unsigned_int_component_width_noprec"
          ; PredName = "format_unsigned_int_component_width_prec"
+        ; PredName = "format_signed_int64_component_nowidth_noprec"
+        ; PredName = "format_signed_int64_component_nowidth_prec"
+        ; PredName = "format_signed_int64_component_width_noprec"
+        ; PredName = "format_signed_int64_component_width_prec"
+        ; PredName = "format_unsigned_int64_component_nowidth_noprec"
+        ; PredName = "format_unsigned_int64_component_nowidth_prec"
+        ; PredName = "format_unsigned_int64_component_width_noprec"
+        ; PredName = "format_unsigned_int64_component_width_prec"
          ; PredName = "format_uint_component_nowidth_noprec"
          ; PredName = "format_uint_component_nowidth_prec"
          ; PredName = "format_uint_component_width_noprec"
          ; PredName = "format_uint_component_width_prec"
+        ; PredName = "format_uint4_component_nowidth_noprec"
+        ; PredName = "format_uint4_component_nowidth_prec"
+        ; PredName = "format_uint4_component_width_noprec"
+        ; PredName = "format_uint4_component_width_prec"
          ; PredName = "format_float_component_nowidth_noprec"
          ; PredName = "format_float_component_nowidth_prec"
          ; PredName = "format_float_component_width_noprec"
@@ -902,11 +914,9 @@ simplify_may_introduce_calls(ModuleName, PredName, _Arity) :-
          ; PredName = "format_cast_int8_to_int"
          ; PredName = "format_cast_int16_to_int"
          ; PredName = "format_cast_int32_to_int"
-        ; PredName = "format_cast_int64_to_int"
          ; PredName = "format_cast_uint8_to_uint"
          ; PredName = "format_cast_uint16_to_uint"
          ; PredName = "format_cast_uint32_to_uint"
-        ; PredName = "format_cast_uint64_to_uint"
          )
      ;
          ModuleName = "stream",
diff --git a/library/string.format.m b/library/string.format.m
index cd7642c..77921ac 100644
--- a/library/string.format.m
+++ b/library/string.format.m
@@ -1,7 +1,7 @@
  %---------------------------------------------------------------------------%
  % vim: ts=4 sw=4 et ft=mercury
  %---------------------------------------------------------------------------%
-% Copyright (C) 2014-2015, 2017-2020 The Mercury team.
+% Copyright (C) 2014-2015, 2017-2021 The Mercury team.
  % This file is distributed under the terms specified in COPYING.LIB.
  %---------------------------------------------------------------------------%
  %
@@ -78,6 +78,35 @@
      int::in, int::in, string_format_int_base::in, uint::in, string::out)
      is det.

+:- pred format_signed_int64_component_nowidth_noprec(string_format_flags::in,
+    int64::in, string::out) is det.
+:- pred format_signed_int64_component_nowidth_prec(string_format_flags::in,
+    int::in, int64::in, string::out) is det.
+:- pred format_signed_int64_component_width_noprec(string_format_flags::in,
+    int::in, int64::in, string::out) is det.
+:- pred format_signed_int64_component_width_prec(string_format_flags::in,
+    int::in, int::in, int64::in, string::out) is det.
+
+:- pred format_unsigned_int64_component_nowidth_noprec(string_format_flags::in,
+    string_format_int_base::in, int64::in, string::out) is det.
+:- pred format_unsigned_int64_component_nowidth_prec(string_format_flags::in,
+    int::in, string_format_int_base::in, int64::in, string::out) is det.
+:- pred format_unsigned_int64_component_width_noprec(string_format_flags::in,
+    int::in, string_format_int_base::in, int64::in, string::out) is det.
+:- pred format_unsigned_int64_component_width_prec(string_format_flags::in,
+    int::in, int::in, string_format_int_base::in, int64::in, string::out)
+    is det.
+
+:- pred format_uint64_component_nowidth_noprec(string_format_flags::in,
+    string_format_int_base::in, uint64::in, string::out) is det.
+:- pred format_uint64_component_nowidth_prec(string_format_flags::in,
+    int::in, string_format_int_base::in, uint64::in, string::out) is det.
+:- pred format_uint64_component_width_noprec(string_format_flags::in,
+    int::in, string_format_int_base::in, uint64::in, string::out) is det.
+:- pred format_uint64_component_width_prec(string_format_flags::in,
+    int::in, int::in, string_format_int_base::in, uint64::in, string::out)
+    is det.
+
  :- pred format_float_component_nowidth_noprec(string_format_flags::in,
      string_format_float_kind::in, float::in, string::out) is det.
  :- pred format_float_component_nowidth_prec(string_format_flags::in,
@@ -91,11 +120,9 @@
  :- pred format_cast_int8_to_int(int8::in, int::out) is det.
  :- pred format_cast_int16_to_int(int16::in, int::out) is det.
  :- pred format_cast_int32_to_int(int32::in, int::out) is det.
-:- pred format_cast_int64_to_int(int64::in, int::out) is det.
  :- pred format_cast_uint8_to_uint(uint8::in, uint::out) is det.
  :- pred format_cast_uint16_to_uint(uint16::in, uint::out) is det.
  :- pred format_cast_uint32_to_uint(uint32::in, uint::out) is det.
-:- pred format_cast_uint64_to_uint(uint64::in, uint::out) is det.

  %---------------------------------------------------------------------------%
  %---------------------------------------------------------------------------%
@@ -185,19 +212,36 @@ spec_to_string(Spec, String) :-
      ;
          % Signed int conversion specifiers.
          Spec = spec_signed_int(Flags, MaybeWidth, MaybePrec, SizedInt),
-        Int = sized_int_to_int(SizedInt),
-        format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int, String)
+        ( if SizedInt = sized_int64(Int64) then
+            format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
+            String)
+        else
+            Int = sized_int_to_int(SizedInt),
+            format_signed_int_component(Flags, MaybeWidth, MaybePrec, Int,
+                String)
+        )
      ;
-        % Unsigned int conversion specifiers.
+        % Unsigned int conversion specifiers (for signed values).
          Spec = spec_unsigned_int(Flags, MaybeWidth, MaybePrec, Base, SizedInt),
-        Int = sized_int_to_int(SizedInt),
-        format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base, Int,
-            String)
+        ( if SizedInt = sized_int64(Int64) then
+            format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base,
+                Int64, String)
+        else
+            Int = sized_int_to_int(SizedInt),
+            format_unsigned_int_component(Flags, MaybeWidth, MaybePrec, Base,
+                Int, String)
+        )
      ;
+        % Unsigned int conversion specifiers (for unsigned values).
          Spec = spec_uint(Flags, MaybeWidth, MaybePrec, Base, SizedUInt),
-        UInt = sized_uint_to_uint(SizedUInt),
-        format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
-            String)
+        ( if SizedUInt = sized_uint64(UInt64) then
+            format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
+                String)
+        else
+            UInt = sized_uint_to_uint(SizedUInt),
+            format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt,
+                String)
+        )
      ;
          % Float conversion specifiers.
          Spec = spec_float(Flags, MaybeWidth, MaybePrec, Kind, Float),
@@ -323,6 +367,89 @@ format_uint_component_width_prec(Flags, Width, Prec, Base, UInt,

  %---------------------------------------------------------------------------%

+format_signed_int64_component_nowidth_noprec(Flags, Int64, String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = no_specified_prec,
+    format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
+        String).
+
+format_signed_int64_component_nowidth_prec(Flags, Prec, Int64, String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = specified_prec(Prec),
+    format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
+        String).
+
+format_signed_int64_component_width_noprec(Flags, Width, Int64, String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = no_specified_prec,
+    format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
+        String).
+
+format_signed_int64_component_width_prec(Flags, Width, Prec, Int64,
+        String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = specified_prec(Prec),
+    format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64,
+        String).
+
+%---------------------------------------------------------------------------%
+
+format_unsigned_int64_component_nowidth_noprec(Flags, Base, Int64, String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = no_specified_prec,
+    format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
+        String).
+
+format_unsigned_int64_component_nowidth_prec(Flags, Prec, Base, Int64,
+        String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = specified_prec(Prec),
+    format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
+        String).
+
+format_unsigned_int64_component_width_noprec(Flags, Width, Base, Int64,
+        String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = no_specified_prec,
+    format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
+        String).
+
+format_unsigned_int64_component_width_prec(Flags, Width, Prec, Base, Int64,
+        String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = specified_prec(Prec),
+    format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
+        String).
+
+%---------------------------------------------------------------------------%
+
+format_uint64_component_nowidth_noprec(Flags, Base, UInt64, String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = no_specified_prec,
+    format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
+        String).
+
+format_uint64_component_nowidth_prec(Flags, Prec, Base, UInt64, String) :-
+    MaybeWidth = no_specified_width,
+    MaybePrec = specified_prec(Prec),
+    format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
+        String).
+
+format_uint64_component_width_noprec(Flags, Width, Base, UInt64, String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = no_specified_prec,
+    format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
+        String).
+
+format_uint64_component_width_prec(Flags, Width, Prec, Base, UInt64,
+        String) :-
+    MaybeWidth = specified_width(Width),
+    MaybePrec = specified_prec(Prec),
+    format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64,
+        String).
+
+%---------------------------------------------------------------------------%
+
  format_float_component_nowidth_noprec(Flags, Kind, Float, String) :-
      MaybeWidth = no_specified_width,
      MaybePrec = no_specified_prec,
@@ -433,6 +560,59 @@ format_uint_component(Flags, MaybeWidth, MaybePrec, Base, UInt, String) :-
          String = format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt)
      ).

+:- pred format_signed_int64_component(string_format_flags::in,
+    string_format_maybe_width::in, string_format_maybe_prec::in,
+    int64::in, string::out) is det.
+
+format_signed_int64_component(Flags, MaybeWidth, MaybePrec, Int64, String) :-
+    ( if using_sprintf then
+        FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
+            "", int64_decimal_specifier),
+        String = native_format_int64(FormatStr, Int64)
+    else
+        String = format_signed_int64(Flags, MaybeWidth, MaybePrec, Int64)
+    ).
+
+:- pred format_unsigned_int64_component(string_format_flags::in,
+    string_format_maybe_width::in, string_format_maybe_prec::in,
+    string_format_int_base::in, int64::in, string::out) is det.
+
+format_unsigned_int64_component(Flags, MaybeWidth, MaybePrec, Base, Int64,
+        String) :-
+    ( if using_sprintf then
+        ( Base = base_octal,   Spec = uint64_octal_specifier
+        ; Base = base_decimal, Spec = uint64_decimal_specifier
+        ; Base = base_hex_lc,  Spec = uint64_hex_lc_specifier
+        ; Base = base_hex_uc,  Spec = uint64_hex_uc_specifier
+        ; Base = base_hex_p,   Spec = uint64_hex_p_specifier
+        ),
+        FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
+            "", Spec),
+        String = native_format_int64(FormatStr, Int64)
+    else
+        UInt64 = uint64.cast_from_int64(Int64),
+        String = format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64)
+    ).
+
+:- pred format_uint64_component(string_format_flags::in,
+    string_format_maybe_width::in, string_format_maybe_prec::in,
+    string_format_int_base::in, uint64::in, string::out) is det.
+
+format_uint64_component(Flags, MaybeWidth, MaybePrec, Base, UInt64, String) :-
+    ( if using_sprintf then
+        ( Base = base_octal,   Spec = uint64_octal_specifier
+        ; Base = base_decimal, Spec = uint64_decimal_specifier
+        ; Base = base_hex_lc,  Spec = uint64_hex_lc_specifier
+        ; Base = base_hex_uc,  Spec = uint64_hex_uc_specifier
+        ; Base = base_hex_p,   Spec = uint64_hex_p_specifier
+        ),
+        FormatStr = make_format_sprintf(Flags, MaybeWidth, MaybePrec,
+            "", Spec),
+        String = native_format_uint64(FormatStr, UInt64)
+    else
+        String = format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64)
+    ).
+
  :- pred format_float_component(string_format_flags::in,
      string_format_maybe_width::in, string_format_maybe_prec::in,
      string_format_float_kind::in, float::in, string::out) is det.
@@ -468,6 +648,8 @@ format_float_component(Flags, MaybeWidth, MaybePrec, Kind, Float, String) :-
      %   native_format_string/2
      %   native_format_char/2
      %   native_format_uint/2
+    %   native_format_int64/2
+    %   native_format_uint64/2
      %
  :- pred using_sprintf is semidet.

@@ -584,6 +766,97 @@ int_length_modifier = _ :-
      % by default.
      error("string.int_length_modifier/0 not defined").

+% NOTE: C does not provide a way to determine the length modifier for the
+% intN_t and uintN_t types in isolation.
+
+:- func int64_decimal_specifier = string.
+:- pragma no_determinism_warning(int64_decimal_specifier/0).
+
+:- pragma foreign_proc("C",
+    int64_decimal_specifier = (Spec::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"
+    MR_make_aligned_string(Spec, PRId64);
+").
+
+int64_decimal_specifier = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.int64_decimal_specifier/0 not defined").
+
+:- func uint64_octal_specifier = string.
+:- pragma no_determinism_warning(uint64_octal_specifier/0).
+
+:- pragma foreign_proc("C",
+    uint64_octal_specifier = (Spec::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"
+    MR_make_aligned_string(Spec, PRIo64);
+").
+
+uint64_octal_specifier = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.uint64_octal_specifier/0 not defined").
+
+:- func uint64_decimal_specifier = string.
+:- pragma no_determinism_warning(uint64_decimal_specifier/0).
+
+:- pragma foreign_proc("C",
+    uint64_decimal_specifier = (Spec::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"
+    MR_make_aligned_string(Spec, PRIu64);
+").
+
+uint64_decimal_specifier = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.uint64_decimal_specifier/0 not defined").
+
+:- func uint64_hex_lc_specifier = string.
+:- pragma no_determinism_warning(uint64_hex_lc_specifier/0).
+
+:- pragma foreign_proc("C",
+    uint64_hex_lc_specifier = (Spec::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"
+    MR_make_aligned_string(Spec, PRIx64);
+").
+
+uint64_hex_lc_specifier = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.uint64_hex_lc_specifier/0 not defined").
+
+:- func uint64_hex_uc_specifier = string.
+:- pragma no_determinism_warning(uint64_hex_uc_specifier/0).
+
+:- pragma foreign_proc("C",
+    uint64_hex_uc_specifier = (Spec::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"
+    MR_make_aligned_string(Spec, PRIX64);
+").
+
+uint64_hex_uc_specifier = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.uint64_hex_uc_specifier/0 not defined").
+
+    % C does not define the 'p' specifier for uint64_t, so we just treat it as
+    % hexadecimal here. What that specifier does is implementation defined
+    % in C anyway. 
+:- func uint64_hex_p_specifier = string.
+
+uint64_hex_p_specifier =
+    uint64_hex_lc_specifier.
+
      % Create a string from a float using the format string.
      % Note it is the responsibility of the caller to ensure that the
      % format string is valid.
@@ -650,6 +923,42 @@ native_format_uint(_, _) = _ :-
      % by default.
      error("string.native_format_uint/2 not defined").

+:- func native_format_int64(string, int64) = string.
+:- pragma no_determinism_warning(native_format_int64/2).
+
+:- pragma foreign_proc("C",
+    native_format_int64(FormatStr::in, Val::in) = (Str::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"{
+    MR_save_transient_hp();
+    Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
+    MR_restore_transient_hp();
+}").
+
+native_format_int64(_, _) = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.native_format_int64/2 not defined").
+
+:- func native_format_uint64(string, uint64) = string.
+:- pragma no_determinism_warning(native_format_uint64/2).
+
+:- pragma foreign_proc("C",
+    native_format_uint64(FormatStr::in, Val::in) = (Str::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness, no_sharing],
+"{
+    MR_save_transient_hp();
+    Str = MR_make_string(MR_ALLOC_ID, FormatStr, Val);
+    MR_restore_transient_hp();
+}").
+
+native_format_uint64(_, _) = _ :-
+    % This predicate is only called if using_sprintf/0, so we produce an error
+    % by default.
+    error("string.native_format_uint64/2 not defined").
+
      % Create a string from a string using the format string.
      % Note it is the responsibility of the caller to ensure that the
      % format string is valid.
@@ -705,6 +1014,8 @@ format_char(Flags, MaybeWidth, Char) = String :-
      CharStr = string.char_to_string(Char),
      String = justify_string(Flags, MaybeWidth, CharStr).

+%---------------------------------------------------------------------------%
+
      % Format a string.
      %
  :- func format_string(string_format_flags, string_format_maybe_width,
@@ -720,6 +1031,10 @@ format_string(Flags, MaybeWidth, MaybePrec, OldStr) = NewStr :-
      ),
      NewStr = justify_string(Flags, MaybeWidth, PrecStr).

+%---------------------------------------------------------------------------%
+
+    % Format an int (as a signed value).
+    %
  :- func format_signed_int(string_format_flags, string_format_maybe_width,
      string_format_maybe_prec, int) = string.

@@ -787,13 +1102,17 @@ format_signed_int(Flags, MaybeWidth, MaybePrec, Int) = String :-

  %---------------------------------------------------------------------------%

+    % Format uint.
+    % Also used for formatting ints as unsigned values.
+    %
  :- func format_uint(string_format_flags, string_format_maybe_width,
      string_format_maybe_prec, string_format_int_base, uint) = string.

  format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt) = String :-
      ( if UInt = 0u then
-        % Zero is a special case. uint_to_*string functions return "0" for 0,
-        % but we must return "" if our caller explicitly allowed us to do so.
+        % Zero is a special case, the uint_to_*string functions return "0" for
+        % 0, but we must return "" if our caller explicitly allowed us to do
+        % so.
          ( if MaybePrec = specified_prec(0) then
              UIntStr = ""
          else
@@ -912,6 +1231,190 @@ format_uint(Flags, MaybeWidth, MaybePrec, Base, UInt) = String :-

  %---------------------------------------------------------------------------%

+    % Format an int64 (as a signed value).
+    %
+:- func format_signed_int64(string_format_flags, string_format_maybe_width,
+    string_format_maybe_prec, int64) = string.
+
+format_signed_int64(Flags, MaybeWidth, MaybePrec, Int) = String :-
+    ( if Int = 0i64 then
+        % Zero is a special case, int64_to_string returns "0" for 0, but we
+        % must return "" only if our caller explicitly allowed us to do so.
+        ( if MaybePrec = specified_prec(0) then
+            AbsIntStr = ""
+        else
+            AbsIntStr = "0"
+        )
+    else if Int = int64.min_int64 then
+        AbsIntStr = "9223372036854775808"
+    else
+        AbsInt = int64.unchecked_abs(Int),
+        AbsIntStr = int64_to_string(AbsInt)
+    ),
+    AbsIntStrLength = string.count_codepoints(AbsIntStr),
+
+    % Do we need to increase precision?
+    ( if
+        MaybePrec = specified_prec(Prec),
+        Prec > AbsIntStrLength
+    then
+        PrecStr = string.pad_left(AbsIntStr, '0', Prec)
+    else
+        PrecStr = AbsIntStr
+    ),
+
+    % Do we need to pad to the field width?
+    ( if
+        MaybeWidth = specified_width(Width),
+        Width > string.count_codepoints(PrecStr),
+        Flags ^ flag_zero = flag_zero_set,
+        Flags ^ flag_minus = flag_minus_clear,
+        MaybePrec = no_specified_prec
+    then
+        FieldStr = string.pad_left(PrecStr, '0', Width - 1),
+        ZeroPadded = yes
+    else
+        FieldStr = PrecStr,
+        ZeroPadded = no
+    ),
+
+    % Prefix with appropriate sign or zero padding.
+    % The previous step has deliberately left room for this.
+    SignedStr = add_sign_like_prefix_to_int64_if_needed(Flags, ZeroPadded, Int,
+        FieldStr),
+    String = justify_string(Flags, MaybeWidth, SignedStr).
+
+%---------------------------------------------------------------------------%
+
+    % Format a uint64.
+    % Also used for formatting int64s as unsigned values.
+    %
+:- func format_uint64(string_format_flags, string_format_maybe_width,
+    string_format_maybe_prec, string_format_int_base, uint64) = string.
+
+format_uint64(Flags, MaybeWidth, MaybePrec, Base, UInt64) = String :-
+    ( if UInt64 = 0u64 then
+        % Zero is a special case, the uint64_to_*string functions return "0"
+        % for 0, but we must return "" only if our caller explicitly allowed us
+        % to do so.
+        ( if MaybePrec = specified_prec(0) then
+            UInt64Str = ""
+        else
+            UInt64Str = "0"
+        )
+    else
+        (
+            Base = base_octal,
+            UInt64Str = uint64_to_octal_string(UInt64)
+        ;
+            Base = base_decimal,
+            UInt64Str = uint64_to_string(UInt64)
+        ;
+            ( Base = base_hex_lc
+            ; Base = base_hex_p
+            ),
+            UInt64Str = uint64_to_hex_string(UInt64)
+        ;
+            Base = base_hex_uc,
+            UInt64Str = uint64_to_uc_hex_string(UInt64)
+        )
+    ),
+    UInt64StrLength = string.count_codepoints(UInt64Str),
+
+    % Do we need to increase precision?
+    ( if
+        MaybePrec = specified_prec(Prec),
+        Prec > UInt64StrLength
+    then
+        PrecStr = string.pad_left(UInt64Str, '0', Prec)
+    else
+        PrecStr = UInt64Str
+    ),
+
+    % Do we need to increase the precision of an octal?
+    ( if
+        Base = base_octal,
+        Flags ^ flag_hash = flag_hash_set,
+        not string.prefix(PrecStr, "0")
+    then
+        PrecModStr = "0" ++ PrecStr
+    else
+        PrecModStr = PrecStr
+    ),
+
+    % Do we need to pad to the field width?
+    ( if
+        MaybeWidth = specified_width(Width),
+        Width > string.count_codepoints(PrecModStr),
+        Flags ^ flag_zero = flag_zero_set,
+        Flags ^ flag_minus = flag_minus_clear,
+        MaybePrec = no_specified_prec
+    then
+        % Do we need to make room for "0x" or "0X" ?
+        ( if
+            Flags ^ flag_hash = flag_hash_set,
+            require_complete_switch [Base]
+            (
+                Base = base_hex_p,
+                Prefix = "0x"
+            ;
+                Base = base_hex_lc,
+                UInt64 \= 0u64,
+                Prefix = "0x"
+            ;
+                Base = base_hex_uc,
+                UInt64 \= 0u64,
+                Prefix = "0X"
+            ;
+                ( Base = base_octal
+                ; Base = base_decimal
+                ),
+                % These get padded with just zeroes on the left.
+                fail
+            )
+        then
+            FieldStr = string.pad_left(PrecModStr, '0', Width - 2),
+            FieldModStr = Prefix ++ FieldStr
+        else
+            FieldStr = string.pad_left(PrecModStr, '0', Width),
+            FieldModStr = FieldStr
+        )
+    else
+        FieldStr = PrecModStr,
+        % Do we have to prefix "0x" or "0X"?
+        ( if
+            Flags ^ flag_hash = flag_hash_set,
+            require_complete_switch [Base]
+            (
+                Base = base_hex_p,
+                Prefix = "0x"
+            ;
+                Base = base_hex_lc,
+                UInt64 \= 0u64,
+                Prefix = "0x"
+            ;
+                Base = base_hex_uc,
+                UInt64 \= 0u64,
+                Prefix = "0X"
+            ;
+                Base = base_octal,
+                % We took care of adding the "0" prefix above.
+                fail
+            ;
+                Base = base_decimal,
+                fail
+            )
+        then
+            FieldModStr = Prefix ++ FieldStr
+        else
+            FieldModStr = FieldStr
+        )
+    ),
+
+    String = justify_string(Flags, MaybeWidth, FieldModStr).
+
+%---------------------------------------------------------------------------%
+
      % Format a float.
      %
  :- func format_float(string_format_flags, string_format_maybe_width,
@@ -1020,6 +1523,8 @@ format_float(Flags, MaybeWidth, MaybePrec, Kind, Float) = String :-
      ),
      String = justify_string(Flags, MaybeWidth, SignedStr).

+%---------------------------------------------------------------------------%
+
  :- func get_prec_to_use(string_format_maybe_prec) = int.
  :- pragma inline(get_prec_to_use/1).

@@ -1073,6 +1578,28 @@ add_sign_like_prefix_to_int_if_needed(Flags, ZeroPadded, Int, FieldStr)
          )
      ).

+:- func add_sign_like_prefix_to_int64_if_needed(string_format_flags, bool,
+    int64, string) = string.
+:- pragma inline(add_sign_like_prefix_to_int64_if_needed/4).
+
+add_sign_like_prefix_to_int64_if_needed(Flags, ZeroPadded, Int64, FieldStr)
+        = SignedStr :-
+    ( if int64.(Int64 < 0i64) then
+        SignedStr = "-" ++ FieldStr
+    else if Flags ^ flag_plus = flag_plus_set then
+        SignedStr = "+" ++ FieldStr
+    else if Flags ^ flag_space = flag_space_set then
+        SignedStr = " " ++ FieldStr
+    else
+        (
+            ZeroPadded = yes,
+            SignedStr = "0" ++ FieldStr
+        ;
+            ZeroPadded = no,
+            SignedStr = FieldStr
+        )
+    ).
+
  :- func add_sign_like_prefix_to_float_if_needed(string_format_flags, bool,
      float, string) = string.
  :- pragma inline(add_sign_like_prefix_to_float_if_needed/4).
@@ -1593,8 +2120,8 @@ sized_int_to_int(SizedInt) = Int :-
          SizedInt = sized_int32(Int32),
          format_cast_int32_to_int(Int32, Int)
      ;
-        SizedInt = sized_int64(Int64),
-        format_cast_int64_to_int(Int64, Int)
+        SizedInt = sized_int64(_),
+        throw(software_error("formatting int64 via a cast"))
      ).

  :- func sized_uint_to_uint(sized_uint) = uint.
@@ -1612,8 +2139,8 @@ sized_uint_to_uint(SizedUInt) = UInt :-
          SizedUInt = sized_uint32(UInt32),
          format_cast_uint32_to_uint(UInt32, UInt)
      ;
-        SizedUInt = sized_uint64(UInt64),
-        format_cast_uint64_to_uint(UInt64, UInt)
+        SizedUInt = sized_uint64(_),
+        throw(software_error("formatting uint64 via a cast"))
      ).

  format_cast_int8_to_int(Int8, Int) :-
@@ -1622,12 +2149,6 @@ format_cast_int16_to_int(Int16, Int) :-
      Int = int16.cast_to_int(Int16).
  format_cast_int32_to_int(Int32, Int) :-
      Int = int32.cast_to_int(Int32).
-format_cast_int64_to_int(Int64, Int) :-
-    ( if words_are_64_bit then
-        Int = int64.cast_to_int(Int64)
-    else
-        throw(software_error("casting from int64 to int on 32 bit platform"))
-    ).

  format_cast_uint8_to_uint(UInt8, UInt) :-
      UInt = uint8.cast_to_uint(UInt8).
@@ -1635,40 +2156,5 @@ format_cast_uint16_to_uint(UInt16, UInt) :-
      UInt = uint16.cast_to_uint(UInt16).
  format_cast_uint32_to_uint(UInt32, UInt) :-
      UInt = uint32.cast_to_uint(UInt32).
-format_cast_uint64_to_uint(UInt64, UInt) :-
-    ( if words_are_64_bit then
-        UInt = uint64.cast_to_uint(UInt64)
-    else
-        throw(software_error("casting from uint64 to uint on 32 bit platform"))
-    ).
-
-:- pred words_are_64_bit is semidet.
-:- pragma inline(words_are_64_bit/0).
-
-:- pragma foreign_proc("C",
-    words_are_64_bit,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
-        does_not_affect_liveness, no_sharing],
-"
-    if (MR_BITS_PER_WORD == 64) {
-        SUCCESS_INDICATOR = MR_TRUE;
-    } else {
-        SUCCESS_INDICATOR = MR_FALSE;
-    }
-").
-:- pragma foreign_proc("C#",
-    words_are_64_bit,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
-        does_not_affect_liveness, no_sharing],
-"
-    SUCCESS_INDICATOR = false;
-").
-:- pragma foreign_proc("Java",
-    words_are_64_bit,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
-        does_not_affect_liveness, no_sharing],
-"
-    SUCCESS_INDICATOR = false;
-").

  %---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/opt_format.exp b/tests/hard_coded/opt_format.exp
index dc99855..98c95e4 100644
--- a/tests/hard_coded/opt_format.exp
+++ b/tests/hard_coded/opt_format.exp
@@ -1,14 +1,14 @@
-abc_42_def_%x_ghi_HAL_jkl\
+abc_42_def_%x_ghi_HAL_jkl_1111_mno_2222_pqr\
  cba_y_def_%143_ghi_IBM    _jkl\
  cba_JCN_fed_%z_ghi_241_jkl\
  abc_0343_def_%v_ghi_KDO_jkl\

-abc_42_def_%a_ghi_WHAL_jkl\
+abc_42_def_%a_ghi_WHAL_jkl_3333_mno_4444_pqr\
  cba_b_def_%143_ghi_WIBM_jkl\
  cba_WJCN_fed_%c_ghi_241_jkl\
  abc_  343_def_%d_ghi_ WKDO_jkl\

-abc_42_def_%e_ghi_XHAL_jkl\
+abc_42_def_%e_ghi_XHAL_jkl_5555_mno_6666_pqr\
  cba_f_def_%143_ghi_XIBM_jkl\
  cba_XJCN_fed_%g_ghi_241_jkl\
  abc_  343_def_%h_ghi_ XKDO_jkl\
diff --git a/tests/hard_coded/opt_format.m b/tests/hard_coded/opt_format.m
index 71d80aa..e1a41a5 100644
--- a/tests/hard_coded/opt_format.m
+++ b/tests/hard_coded/opt_format.m
@@ -24,28 +24,31 @@
  :- import_module string.

  main(!IO) :-
-    io.write_string(test_string_format_1(42,  'x', "HAL"), !IO),
+    io.write_string(test_string_format_1(42,  'x', "HAL", 1111i64, 2222u64),
+        !IO),
      io.write_string(test_string_format_2(142, 'y', "IBM"), !IO),
      io.write_string(test_string_format_2(242, 'z', "JCN"), !IO),
      io.write_string(test_string_format_2(342, 'v', "KDO"), !IO),
      io.nl(!IO),
-    test_io_format_1(42,  'a', "WHAL", !IO),
+    test_io_format_1(42,  'a', "WHAL", 3333i64, 4444u64, !IO),
      test_io_format_2(142, 'b', "WIBM", !IO),
      test_io_format_2(242, 'c', "WJCN", !IO),
      test_io_format_2(342, 'd', "WKDO", !IO),
      io.nl(!IO),
      io.output_stream(OutStream, !IO),
-    test_stream_writer_format_1(OutStream, 42,  'e', "XHAL", !IO),
+    test_stream_writer_format_1(OutStream, 42,  'e', "XHAL", 5555i64, 6666u64,
+        !IO),
      test_stream_writer_format_2(OutStream, 142, 'f', "XIBM", !IO),
      test_stream_writer_format_2(OutStream, 242, 'g', "XJCN", !IO),
      test_stream_writer_format_2(OutStream, 342, 'h', "XKDO", !IO).

  %---------------------------------------------------------------------------%

-:- func test_string_format_1(int, char, string) = string.
+:- func test_string_format_1(int, char, string, int64, uint64) = string.

-test_string_format_1(Int, Char, Str) =
-    string.format("abc_%d_def_%%%c_ghi_%s_jkl\\\n", [i(Int), c(Char), s(Str)]).
+test_string_format_1(Int, Char, Str, Int64, UInt64) =
+    string.format("abc_%d_def_%%%c_ghi_%s_jkl_%d_mno_%u_pqr\\\n",
+        [i(Int), c(Char), s(Str), i64(Int64), u64(UInt64)]).

  :- func test_string_format_2(int, char, string) = string.

@@ -74,11 +77,12 @@ test_string_format_2(Int, Char, Str) = Result :-

  %---------------------------------------------------------------------------%

-:- pred test_io_format_1(int::in, char::in, string::in, io::di, io::uo) is det.
+:- pred test_io_format_1(int::in, char::in, string::in, int64::in, uint64::in,
+    io::di, io::uo) is det.

-test_io_format_1(Int, Char, Str, !IO) :-
-    io.format("abc_%d_def_%%%c_ghi_%s_jkl\\\n",
-        [i(Int), c(Char), s(Str)], !IO).
+test_io_format_1(Int, Char, Str, Int64, UInt64, !IO) :-
+    io.format("abc_%d_def_%%%c_ghi_%s_jkl_%d_mno_%u_pqr\\\n",
+        [i(Int), c(Char), s(Str), i64(Int64), u64(UInt64)], !IO).

  :- pred test_io_format_2(int::in, char::in, string::in, io::di, io::uo) is det.

@@ -108,11 +112,12 @@ test_io_format_2(Int, Char, Str, !IO) :-
  %---------------------------------------------------------------------------%

  :- pred test_stream_writer_format_1(Stream::in, int::in, char::in, string::in,
-    State::di, State::uo) is det <= stream.writer(Stream, string, State).
+    int64::in, uint64::in, State::di, State::uo) is det
+    <= stream.writer(Stream, string, State).

-test_stream_writer_format_1(Stream, Int, Char, Str, !State) :-
-    stream.string_writer.format(Stream, "abc_%d_def_%%%c_ghi_%s_jkl\\\n",
-        [i(Int), c(Char), s(Str)], !State).
+test_stream_writer_format_1(Stream, Int, Char, Str, Int64, UInt64, !State) :-
+    stream.string_writer.format(Stream, "abc_%d_def_%%%c_ghi_%s_jkl_%d_mno_%u_pqr\\\n",
+        [i(Int), c(Char), s(Str), i64(Int64), u64(UInt64)], !State).

  :- pred test_stream_writer_format_2(Stream::in, int::in, char::in, string::in,
      State::di, State::uo) is det <= stream.writer(Stream, string, State).


More information about the reviews mailing list