Files
mercury/compiler/mlds_to_cs_data.m
Zoltan Somogyi bb0b12deba Add in_range as a builtin op.
It is almost the same as the unsigned_lt builtin op, but it is
implemented as "0 =< Index, Index < Range" just for Java.

This is the first step of the bootstrapping process; the second step
will add a declaration for private_builtin.in_range.

compiler/builtin_ops.m:
    As above.

compiler/llds_out_data.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_cs_data.m:
compiler/mlds_to_java_data.m:
    Implement the new operation.

compiler/code_util.m:
compiler/llds.m:
compiler/ml_global_data.m:
compiler/mlds_dump.m:
compiler/opt_debug.m:
    Conform to the change above.

compiler/options.m:
    Add a way to test whether the installed compiler supports this new op.

tests/warnings/help_text.err_exp:
    Expect the new option name.
2025-10-01 15:24:55 +10:00

1167 lines
45 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2010-2012 The University of Melbourne.
% Copyright (C) 2013-2018, 2020-2022, 2024-2025 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.
%---------------------------------------------------------------------------%
%
% Output MLDS lvals, rvals and initializers in C#.
%
%---------------------------------------------------------------------------%
:- module ml_backend.mlds_to_cs_data.
:- interface.
:- import_module libs.
:- import_module libs.indent.
:- import_module ml_backend.mlds.
:- import_module ml_backend.mlds_to_cs_util.
:- import_module ml_backend.mlds_to_target_util.
:- import_module io.
:- import_module list.
:- import_module maybe.
%---------------------------------------------------------------------------%
:- pred output_lval_for_csharp(csharp_out_info::in, mlds_lval::in,
io.text_output_stream::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
:- pred output_call_rval_for_csharp(csharp_out_info::in, mlds_rval::in,
io.text_output_stream::in, io::di, io::uo) is det.
:- pred output_rval_for_csharp(csharp_out_info::in, mlds_rval::in,
io.text_output_stream::in, io::di, io::uo) is det.
:- type maybe_for_call
---> is_not_for_call
; is_for_call.
:- pred output_code_addr_for_csharp(csharp_out_info::in,
io.text_output_stream::in, mlds_code_addr::in, maybe_for_call::in,
io::di, io::uo) is det.
%---------------------------------------------------------------------------%
% Output " = " followed by the given initializer, if any, followed
% by the given suffix string and a newline.
%
% The initializer is printed using output_initializer_body_for_java with
% not_at_start_of_line (see below).
%
:- pred output_initializer_for_csharp(csharp_out_info::in,
io.text_output_stream::in, output_aux::in, indent::in, mlds_type::in,
mlds_initializer::in, string::in, io::di, io::uo) is det.
% Output the allocation part of the given initializer on the rest
% of the current line.
%
:- pred output_initializer_alloc_only_for_csharp(csharp_out_info::in,
io.text_output_stream::in, mlds_initializer::in, maybe(mlds_type)::in,
string::in, io::di, io::uo) is det.
% Output the given initializer. The formatting depends on whether
% the caller tells us that it has printed something on the current line
% already (not_at_start_of_line) or not (at_start_of_line).
%
% If the initializer is for a struct or an array, we put the initializer
% on separate lines, each indented by the given indent level, regardless
% of where we start.
%
% If the initializer is for a single object, then we put its initializer
% immediately after the previous of the current line if there is one
% (not_at_start_of_line); otherwise (at_start_of_line), we indent it by
% the specified level.
%
% In either case, we end the initializer with the given suffix
% (which will usually be a semicolon or a comma) and a newline.
%
:- pred output_initializer_body_for_csharp(csharp_out_info::in,
io.text_output_stream::in, initializer_starts::in, indent::in,
mlds_initializer::in, maybe(mlds_type)::in, string::in,
io::di, io::uo) is det.
% Output the given list of initializers with commas between them,
% putting each initializer on its own line with the given indent.
% Put the given suffix after the last initializer.
%
:- pred output_nonempty_initializer_body_list_for_csharp(csharp_out_info::in,
io.text_output_stream::in, indent::in, list(mlds_initializer)::in,
string::in, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.c_util. % for output_quoted_string*
:- import_module backend_libs.rtti.
:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module ml_backend.ml_util.
:- import_module ml_backend.mlds_to_cs_name.
:- import_module ml_backend.mlds_to_cs_type.
:- import_module parse_tree.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_type.
:- import_module int.
:- import_module int32.
:- import_module map.
:- import_module require.
:- import_module string.
:- import_module term.
:- import_module uint.
:- import_module uint32.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
output_lval_for_csharp(Info, Lval, Stream, !IO) :-
(
Lval = ml_field(_MaybeTag, PtrRval, _PtrType, FieldId, FieldType),
(
FieldId = ml_field_offset(OffsetRval),
( if
( FieldType = mlds_generic_type
; FieldType = mercury_nb_type(type_variable(_, _), _)
)
then
true
else
% The field type for field(_, _, offset(_), _, _) lvals
% must be something that maps to MR_Box.
unexpected($pred, "unexpected field type")
),
% XXX We shouldn't need this cast here, but there are cases where
% it is needed and the MLDS doesn't seem to generate it.
io.write_string(Stream, "((object[]) ", !IO),
output_rval_for_csharp(Info, PtrRval, Stream, !IO),
io.write_string(Stream, ")[", !IO),
output_rval_for_csharp(Info, OffsetRval, Stream, !IO),
io.write_string(Stream, "]", !IO)
;
FieldId = ml_field_named(QualFieldVarName, CtorType),
QualFieldVarName = qual_field_var_name(_, _, FieldVarName),
FieldVarNameStr =
field_var_name_to_ll_string_for_csharp(FieldVarName),
( if FieldVarName = fvn_data_tag then
% If the field we are trying to access is just a `data_tag'
% then it is a member of the base class.
output_bracketed_rval_for_csharp(Info, PtrRval, Stream, !IO),
io.format(Stream, ".%s", [s(FieldVarNameStr)], !IO)
else if PtrRval = ml_self(_) then
% Suppress type cast on `this' keyword. This makes a difference
% when assigning to `final' member variables in constructor
% functions.
output_rval_for_csharp(Info, PtrRval, Stream, !IO),
io.format(Stream, ".%s", [s(FieldVarNameStr)], !IO)
else
% Otherwise the field we are trying to access may be
% in a derived class. Objects are manipulated as instances
% of their base class, so we need to downcast to the derived
% class to access some fields.
io.format(Stream, "((%s) ",
[s(type_to_string_for_csharp(Info, CtorType))], !IO),
output_bracketed_rval_for_csharp(Info, PtrRval, Stream, !IO),
io.format(Stream, ").%s", [s(FieldVarNameStr)], !IO)
)
)
;
Lval = ml_mem_ref(Rval, _Type),
output_bracketed_rval_for_csharp(Info, Rval, Stream, !IO)
;
Lval = ml_target_global_var_ref(GlobalVarRef),
GlobalVarRef = env_var_ref(EnvVarName),
io.format(Stream, "mercury_envvar_%s", [s(EnvVarName)], !IO)
;
Lval = ml_local_var(LocalVarName, _),
LocalVarNameStr = local_var_name_to_ll_string_for_csharp(LocalVarName),
io.write_string(Stream, LocalVarNameStr, !IO)
;
Lval = ml_global_var(GlobalVarName, _),
GlobalVarNameStr =
maybe_qualified_global_var_name_to_string_for_csharp(Info,
GlobalVarName),
io.write_string(Stream, GlobalVarNameStr, !IO)
).
%---------------------------------------------------------------------------%
output_call_rval_for_csharp(Info, Rval, Stream, !IO) :-
( if
Rval = ml_const(Const),
Const = mlconst_code_addr(CodeAddr)
then
output_code_addr_for_csharp(Info, Stream, CodeAddr, is_for_call, !IO)
else
output_bracketed_rval_for_csharp(Info, Rval, Stream, !IO)
).
:- pred output_bracketed_rval_for_csharp(csharp_out_info::in, mlds_rval::in,
io.text_output_stream::in, io::di, io::uo) is det.
output_bracketed_rval_for_csharp(Info, Rval, Stream, !IO) :-
( if
% If it is just a variable name, then we don't need parentheses.
( Rval = ml_lval(ml_local_var(_,_))
; Rval = ml_lval(ml_global_var(_,_))
; Rval = ml_const(mlconst_code_addr(_))
)
then
output_rval_for_csharp(Info, Rval, Stream, !IO)
else
io.write_char(Stream, '(', !IO),
output_rval_for_csharp(Info, Rval, Stream, !IO),
io.write_char(Stream, ')', !IO)
).
output_rval_for_csharp(Info, Rval, Stream, !IO) :-
(
Rval = ml_lval(Lval),
output_lval_for_csharp(Info, Lval, Stream, !IO)
;
Rval = ml_mkword(_, _),
unexpected($pred, "tags not supported in C#")
;
Rval = ml_const(Const),
output_rval_const_for_csharp(Info, Stream, Const, !IO)
;
Rval = ml_cast(Type, SubRval),
output_cast_rval_for_csharp(Info, Type, SubRval, Stream, !IO)
;
Rval = ml_box(Type, SubRval),
( if Type = mercury_nb_type(comparison_result_type, _) then
io.write_string(Stream,
"builtin.comparison_result_object[(int) ", !IO),
output_rval_for_csharp(Info, SubRval, Stream, !IO),
io.write_string(Stream, "]", !IO)
else
output_boxed_rval_for_csharp(Info, Type, SubRval, Stream, !IO)
)
;
Rval = ml_unbox(Type, SubRval),
output_unboxed_rval_for_csharp(Info, Type, SubRval, Stream, !IO)
;
Rval = ml_unop(Unop, SubRval),
output_unop_for_csharp(Info, Stream, Unop, SubRval, !IO)
;
Rval = ml_binop(Op, SubRvalA, SubRvalB),
output_binop_for_csharp(Info, Stream, Op, SubRvalA, SubRvalB, !IO)
;
Rval = ml_mem_addr(Lval),
io.write_string(Stream, "out ", !IO),
output_lval_for_csharp(Info, Lval, Stream, !IO)
;
Rval = ml_scalar_common(_),
unexpected($pred, "ml_scalar_common")
;
Rval = ml_scalar_common_addr(ScalarCommon),
ScalarCommon = mlds_scalar_common(ModuleName, _Type,
ml_scalar_common_type_num(TypeNum), RowNum),
ModuleSymName = mlds_module_name_to_sym_name(ModuleName),
MangledModuleName =
strip_mercury_and_mangle_sym_name_for_csharp(ModuleSymName),
io.format(Stream, "%s.MR_scalar_common_%d[%d]",
[s(MangledModuleName), i(TypeNum), i(RowNum)], !IO)
;
Rval = ml_vector_common_row_addr(VectorCommon, RowRval),
VectorCommon = mlds_vector_common(_ModuleName, _Type,
ml_vector_common_type_num(TypeNum), StartRowNum, _NumRows),
% XXX Why do we print a "MangledModuleName." prefix for scalar common
% addresses but not for vector common addresses?
io.format(Stream, "MR_vector_common_%d[%d + ",
[i(TypeNum), i(StartRowNum)], !IO),
output_rval_for_csharp(Info, RowRval, Stream, !IO),
io.write_string(Stream, "]", !IO)
;
Rval = ml_self(_),
io.write_string(Stream, "this", !IO)
).
:- pred output_cast_rval_for_csharp(csharp_out_info::in, mlds_type::in,
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
output_cast_rval_for_csharp(Info, Type, Expr, Stream, !IO) :-
% rtti_to_mlds.m generates casts from int to runtime.PseudoTypeInfo, but
% for C# we need to treat these as constructions, not casts.
% Similarly for conversions from TypeCtorInfo to TypeInfo.
( if
Type = mlds_pseudo_type_info_type,
Expr = ml_const(mlconst_int(N))
then
maybe_output_inline_comment_for_csharp(Info, Stream, "cast", !IO),
( if have_preallocated_pseudo_type_var_for_csharp(N) then
io.write_string(Stream, "runtime.PseudoTypeInfo.K", !IO),
io.write_int(Stream, N, !IO)
else
io.write_string(Stream, "new runtime.PseudoTypeInfo(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO)
)
else if
( Type = mercury_nb_type(_, ctor_cat_system(cat_system_type_info))
; Type = mlds_type_info_type
)
then
% XXX We really should be able to tell if we are casting a
% TypeCtorInfo or a TypeInfo. Julien says that is probably going to
% be rather difficult as the compiler doesn't keep track of where
% type_ctor_infos are acting as type_infos properly. (zs agrees.)
maybe_output_inline_comment_for_csharp(Info, Stream, "cast", !IO),
io.write_string(Stream, "runtime.TypeInfo_Struct.maybe_new(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO)
else
% While the Java backend represents Mercury enums as Java classes
% with a value field, the C# backend represents them as C# enums.
% We therefore have no need to get a value field.
io.format(Stream, "(%s) ",
[s(type_to_string_for_csharp(Info, Type))], !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO)
).
:- pred have_preallocated_pseudo_type_var_for_csharp(int::in) is semidet.
have_preallocated_pseudo_type_var_for_csharp(N) :-
% Corresponds to static members in class PseudoTypeInfo.
N >= 1,
N =< 5.
:- pred output_boxed_rval_for_csharp(csharp_out_info::in, mlds_type::in,
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
output_boxed_rval_for_csharp(Info, _Type, Expr, Stream, !IO) :-
% C# does implicit boxing.
output_rval_for_csharp(Info, Expr, Stream, !IO).
% ( if csharp_builtin_type(Type, _JavaName, JavaBoxedName, _) then
% % valueOf may return cached instances instead of creating new objects.
% io.write_string(Stream, JavaBoxedName, !IO),
% io.write_string(Stream, ".valueOf(", !IO),
% output_rval(Info, Expr, !IO),
% io.write_string(Stream, ")", !IO)
% else
% io.write_string(Stream, "((object) (", !IO),
% output_rval(Info, Expr, !IO),
% io.write_string(Stream, "))", !IO)
% ).
:- pred output_unboxed_rval_for_csharp(csharp_out_info::in, mlds_type::in,
mlds_rval::in, io.text_output_stream::in, io::di, io::uo) is det.
output_unboxed_rval_for_csharp(Info, Type, Expr, Stream, !IO) :-
io.format(Stream, "((%s) ",
[s(type_to_string_for_csharp(Info, Type))], !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO).
%---------------------------------------------------------------------------%
:- pred output_unop_for_csharp(csharp_out_info::in, io.text_output_stream::in,
unary_op::in, mlds_rval::in, io::di, io::uo) is det.
output_unop_for_csharp(Info, Stream, UnaryOp, Expr, !IO) :-
% For the C# backend, there are no tags, so all the tagging operators
% are no-ops, except for `tag', which always returns zero (a tag of zero
% means there is no tag).
(
UnaryOp = tag ,
io.write_string(Stream, "/* tag */ 0", !IO)
;
( UnaryOp = strip_tag, UnaryOpStr = "/* strip_tag */ "
; UnaryOp = mkbody, UnaryOpStr = "/* mkbody */ "
; UnaryOp = unmkbody, UnaryOpStr = "/* unmkbody */ "
; UnaryOp = logical_not, UnaryOpStr = "!"
; UnaryOp = hash_string, UnaryOpStr = "mercury.String.hash_1_f_0"
; UnaryOp = hash_string2, UnaryOpStr = "mercury.String.hash2_1_f_0"
; UnaryOp = hash_string3, UnaryOpStr = "mercury.String.hash3_1_f_0"
; UnaryOp = hash_string4, UnaryOpStr = "mercury.String.hash4_1_f_0"
; UnaryOp = hash_string5, UnaryOpStr = "mercury.String.hash5_1_f_0"
; UnaryOp = hash_string6, UnaryOpStr = "mercury.String.hash6_1_f_0"
),
io.write_string(Stream, UnaryOpStr, !IO),
io.write_string(Stream, "(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
UnaryOp = bitwise_complement(IntType),
(
( IntType = int_type_int
; IntType = int_type_int32
; IntType = int_type_int64
; IntType = int_type_uint
; IntType = int_type_uint32
; IntType = int_type_uint64
),
io.write_string(Stream, "~(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
( IntType = int_type_int8, CastStr = "(sbyte)"
; IntType = int_type_int16, CastStr = "(short)"
),
io.write_string(Stream, CastStr, !IO),
io.write_string(Stream, " ~(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
% The result of the bitwise complement of byte or ushort in C#
% will be promoted to an int. Casting this back to the original
% type may result in a CS0221 error from the C# compiler
% due to the possible information loss involved in such a cast.
% We need to wrap the whole expression in an unchecked context
% in order to prevent the C# compiler from treating it as an error.
( IntType = int_type_uint8, CastStr = "(byte)"
; IntType = int_type_uint16, CastStr = "(ushort)"
),
io.write_string(Stream, "unchecked (", !IO),
io.write_string(Stream, CastStr, !IO),
io.write_string(Stream, " ~(", !IO),
output_rval_for_csharp(Info, Expr, Stream, !IO),
io.write_string(Stream, "))", !IO)
)
;
( UnaryOp = dword_float_get_word0
; UnaryOp = dword_float_get_word1
; UnaryOp = dword_int64_get_word0
; UnaryOp = dword_int64_get_word1
; UnaryOp = dword_uint64_get_word0
; UnaryOp = dword_uint64_get_word1
),
unexpected($pred, "invalid unary operator")
).
:- pred output_binop_for_csharp(csharp_out_info::in, io.text_output_stream::in,
binary_op::in, mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
output_binop_for_csharp(Info, Stream, Op, X, Y, !IO) :-
(
Op = array_index(_Type),
output_bracketed_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, "[", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, "]", !IO)
;
Op = str_cmp(CmpOp),
(
CmpOp = eq,
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, ".Equals(", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
( CmpOp = ne, OpStr = "!="
; CmpOp = lt, OpStr = "<"
; CmpOp = gt, OpStr = ">"
; CmpOp = le, OpStr = "<="
; CmpOp = ge, OpStr = ">="
),
io.write_string(Stream, "(", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, ".CompareOrdinal(", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ") ", !IO),
io.format(Stream, ") %s 0)", [s(OpStr)], !IO)
)
;
Op = str_nzp,
io.write_string(Stream, "(", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, ".CompareOrdinal(", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, "))", !IO)
;
Op = pointer_equal_conservative,
io.write_string(Stream, "System.Object.ReferenceEquals(", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, ", ", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
Op = int_arith(IntType, ArithOp),
output_int_arith_binop_for_csharp(Info, Stream, IntType, ArithOp,
X, Y, !IO)
;
Op = int_cmp(_IntType, CmpOp),
OpStr = cmp_op_c_operator(CmpOp),
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO)
;
( Op = unchecked_left_shift(_, _)
; Op = unchecked_right_shift(_, _)
; Op = bitwise_and(_)
; Op = bitwise_or(_)
; Op = bitwise_xor(_)
),
output_int_misc_binop_for_csharp(Info, Stream, Op, X, Y, !IO)
;
(
Op = int_as_uint_cmp(CmpOp),
( CmpOp = lt, OpStr = "<"
; CmpOp = le, OpStr = "<="
)
;
Op = in_range,
OpStr = "<"
),
io.write_string(Stream, "((uint) ", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.format(Stream, " %s (uint) ", [s(OpStr)], !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO)
;
( Op = logical_and, OpStr = "&&"
; Op = logical_or, OpStr = "||"
; Op = float_arith(ArithOp), OpStr =
arith_op_c_operator(coerce(ArithOp))
; Op = float_cmp(CmpOp), OpStr = cmp_op_c_operator(CmpOp)
),
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO)
;
( Op = body
; Op = string_unsafe_index_code_unit
; Op = offset_str_eq(_, _)
; Op = float_from_dword
; Op = int64_from_dword
; Op = uint64_from_dword
),
unexpected($pred, "invalid binary operator")
).
:- pred output_int_arith_binop_for_csharp(csharp_out_info::in,
io.text_output_stream::in, int_type::in, arith_op::in,
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
:- pragma no_inline(pred(output_int_arith_binop_for_csharp/8)).
output_int_arith_binop_for_csharp(Info, Stream, Type, Op, X, Y, !IO) :-
( Type = int_type_int, Cast = ""
; Type = int_type_int8, Cast = "(sbyte) "
; Type = int_type_int16, Cast = "(short) "
; Type = int_type_int32, Cast = ""
; Type = int_type_int64, Cast = ""
; Type = int_type_uint, Cast = ""
; Type = int_type_uint8, Cast = "(byte) "
; Type = int_type_uint16, Cast = "(ushort) "
; Type = int_type_uint32, Cast = ""
; Type = int_type_uint64, Cast = ""
),
OpStr = arith_op_c_operator(Op),
io.write_string(Stream, Cast, !IO),
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO).
:- pred output_int_misc_binop_for_csharp(csharp_out_info::in,
io.text_output_stream::in, binary_op::in(int_misc_binary_op),
mlds_rval::in, mlds_rval::in, io::di, io::uo) is det.
:- pragma no_inline(pred(output_int_misc_binop_for_csharp/7)).
output_int_misc_binop_for_csharp(Info, Stream, Op, X, Y, !IO) :-
(
( Op = bitwise_and(Type), OpStr = "&"
; Op = bitwise_xor(Type), OpStr = "^"
),
( Type = int_type_int, Cast = ""
; Type = int_type_int8, Cast = "(sbyte) "
; Type = int_type_int16, Cast = "(short) "
; Type = int_type_int32, Cast = ""
; Type = int_type_int64, Cast = ""
; Type = int_type_uint, Cast = ""
; Type = int_type_uint8, Cast = "(byte) "
; Type = int_type_uint16, Cast = "(ushort) "
; Type = int_type_uint32, Cast = ""
; Type = int_type_uint64, Cast = ""
),
io.write_string(Stream, Cast, !IO),
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO)
;
Op = bitwise_or(Type),
OpStr = "|",
(
( Type = int_type_int, Cast = ""
; Type = int_type_int16, Cast = "(short) "
; Type = int_type_int32, Cast = ""
; Type = int_type_int64, Cast = ""
; Type = int_type_uint, Cast = ""
; Type = int_type_uint8, Cast = "(byte) "
; Type = int_type_uint16, Cast = "(ushort) "
; Type = int_type_uint32, Cast = ""
; Type = int_type_uint64, Cast = ""
),
io.write_string(Stream, Cast, !IO),
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO)
;
Type = int_type_int8,
% The special treatment of bitwise-or for int8 is necessary
% to avoid warning CS0675 from the C# compiler.
% XXX The Microsoft C# reference manual' section on compiler
% messages says that this warning is for a "bitwise OR operator
% used on a sign extended operand". I (zs) understand why this
% deserves a warning, but do not understand why the same warning
% does not apply to bitwise AND and bitwise XOR.
% Unfortunately, the chance that a maintainer of the C# compiler
% will read this comment is zero :-(
io.write_string(Stream, "(sbyte) ((byte) ", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, " ", !IO),
io.write_string(Stream, OpStr, !IO),
io.write_string(Stream, " (byte) ", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO)
)
;
( Op = unchecked_left_shift(Type, ShiftType), OpStr = "<<"
; Op = unchecked_right_shift(Type, ShiftType), OpStr = ">>"
),
% C# does not automatically promote uints to ints, since
% half of all uints are too big to be represented as ints.
% However, the only valid shift amounts are very small,
% so for valid shift amounts, the cast cannot lose information.
(
ShiftType = shift_by_int,
CastY = ""
;
ShiftType = shift_by_uint,
CastY = "(int) "
),
( Type = int_type_int, Cast = ""
; Type = int_type_int8, Cast = "(sbyte) "
; Type = int_type_int16, Cast = "(short) "
; Type = int_type_int32, Cast = ""
; Type = int_type_int64, Cast = ""
; Type = int_type_uint, Cast = ""
; Type = int_type_uint8, Cast = "(byte) "
; Type = int_type_uint16, Cast = "(ushort) "
; Type = int_type_uint32, Cast = ""
; Type = int_type_uint64, Cast = ""
),
io.write_string(Stream, Cast, !IO),
io.write_string(Stream, "(", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, " ", !IO),
io.write_string(Stream, OpStr, !IO),
io.write_string(Stream, " ", !IO),
io.write_string(Stream, CastY, !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO)
).
:- pred output_basic_binop_for_csharp(csharp_out_info::in,
io.text_output_stream::in, string::in, mlds_rval::in, mlds_rval::in,
io::di, io::uo) is det.
:- pragma no_inline(pred(output_basic_binop_for_csharp/7)).
output_basic_binop_for_csharp(Info, Stream, OpStr, X, Y, !IO) :-
io.write_string(Stream, "(", !IO),
output_rval_for_csharp(Info, X, Stream, !IO),
io.write_string(Stream, " ", !IO),
io.write_string(Stream, OpStr, !IO),
io.write_string(Stream, " ", !IO),
output_rval_for_csharp(Info, Y, Stream, !IO),
io.write_string(Stream, ")", !IO).
%---------------------------------------------------------------------------%
:- pred output_rval_const_for_csharp(csharp_out_info::in,
io.text_output_stream::in, mlds_rval_const::in, io::di, io::uo) is det.
output_rval_const_for_csharp(Info, Stream, Const, !IO) :-
(
Const = mlconst_true,
io.write_string(Stream, "true", !IO)
;
Const = mlconst_false,
io.write_string(Stream, "false", !IO)
;
Const = mlconst_int(N),
output_int_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_uint(N),
output_uint_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_int8(N),
output_int8_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_uint8(N),
output_uint8_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_int16(N),
output_int16_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_uint16(N),
output_uint16_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_int32(N),
output_int32_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_uint32(N),
output_uint32_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_int64(N),
output_int64_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_uint64(N),
output_uint64_const_for_csharp(Stream, N, !IO)
;
Const = mlconst_char(C),
io.write_string(Stream, "( ", !IO),
output_int_const_for_csharp(Stream, C, !IO),
io.write_string(Stream, ")", !IO)
;
Const = mlconst_enum(N, EnumType),
% Explicit cast required.
output_cast_rval_for_csharp(Info, EnumType,
ml_const(mlconst_int(N)), Stream, !IO)
;
Const = mlconst_foreign(Lang, Value, Type),
expect(unify(Lang, lang_csharp), $pred, "language other than C#."),
% XXX Should we parenthesize this?
io.format(Stream, "(%s) ",
[s(type_to_string_for_csharp(Info, Type))], !IO),
io.write_string(Stream, Value, !IO)
;
Const = mlconst_float(FloatVal),
c_util.output_float_literal(Stream, FloatVal, !IO)
;
Const = mlconst_string(String),
output_quoted_string_csharp(Stream, String, !IO)
;
Const = mlconst_multi_string(String),
output_quoted_multi_string_csharp(Stream, String, !IO)
;
Const = mlconst_named_const(TargetPrefixes, NamedConst),
io.write_string(Stream, TargetPrefixes ^ csharp_prefix, !IO),
io.write_string(Stream, NamedConst, !IO)
;
Const = mlconst_code_addr(CodeAddr),
map.lookup(Info ^ csoi_code_addrs, CodeAddr, Name),
io.write_string(Stream, Name, !IO)
;
Const = mlconst_data_addr_local_var(VarName),
VarNameStr = local_var_name_to_ll_string_for_csharp(VarName),
io.write_string(Stream, VarNameStr, !IO)
;
Const = mlconst_data_addr_global_var(ModuleName, VarName),
MangledModuleName = strip_mercury_and_mangle_sym_name_for_csharp(
mlds_module_name_to_sym_name(ModuleName)),
VarNameStr = global_var_name_to_ll_string_for_csharp(VarName),
io.format(Stream, "%s.%s", [s(MangledModuleName), s(VarNameStr)], !IO)
;
Const = mlconst_data_addr_rtti(ModuleName, RttiId),
MangledModuleName = strip_mercury_and_mangle_sym_name_for_csharp(
mlds_module_name_to_sym_name(ModuleName)),
rtti.id_to_c_identifier(RttiId, RttiAddrName0),
RttiAddrName = limit_identifier_length(RttiAddrName0),
io.format(Stream, "%s.%s",
[s(MangledModuleName), s(RttiAddrName)], !IO)
;
Const = mlconst_data_addr_tabling(_QualProcLabel, _TablingId),
unexpected($pred, "NYI: mlconst_data_addr_tabling")
;
Const = mlconst_null(Type),
Initializer = get_default_initializer_for_csharp(Info, Type),
io.write_string(Stream, Initializer, !IO)
).
:- pred output_int_const_for_csharp(io.text_output_stream::in, int::in,
io::di, io::uo) is det.
output_int_const_for_csharp(Stream, N, !IO) :-
% You may wish to see the comment on output_int_const_for_java
% in mlds_to_java_data.m.
( if
N > 0,
not int32.from_int(N, _I32),
uint32.from_int(N, U32)
then
% The bit pattern fits in 32 bits, but is too big for a positive
% integer. The C# compiler will report an error about this unless we
% tell it otherwise.
N32 = uint32.cast_to_int(U32),
io.format(Stream, "unchecked((int) 0x%x)", [i(N32)], !IO)
else
io.write_int(Stream, N, !IO)
).
:- pred output_uint_const_for_csharp(io.text_output_stream::in, uint::in,
io::di, io::uo) is det.
output_uint_const_for_csharp(Stream, U, !IO) :-
io.write_uint(Stream, U, !IO),
io.write_string(Stream, "U", !IO).
:- pred output_int8_const_for_csharp(io.text_output_stream::in, int8::in,
io::di, io::uo) is det.
output_int8_const_for_csharp(Stream, I8, !IO) :-
io.write_string(Stream, "(sbyte) ", !IO),
io.write_int8(Stream, I8, !IO).
:- pred output_uint8_const_for_csharp(io.text_output_stream::in, uint8::in,
io::di, io::uo) is det.
output_uint8_const_for_csharp(Stream, U8, !IO) :-
io.write_string(Stream, "(byte) ", !IO),
io.write_uint8(Stream, U8, !IO).
:- pred output_int16_const_for_csharp(io.text_output_stream::in, int16::in,
io::di, io::uo) is det.
output_int16_const_for_csharp(Stream, I16, !IO) :-
io.write_string(Stream, "(short) ", !IO),
io.write_int16(Stream, I16, !IO).
:- pred output_uint16_const_for_csharp(io.text_output_stream::in, uint16::in,
io::di, io::uo) is det.
output_uint16_const_for_csharp(Stream, U16, !IO) :-
io.write_string(Stream, "(ushort) ", !IO),
io.write_uint16(Stream, U16, !IO).
:- pred output_int32_const_for_csharp(io.text_output_stream::in, int32::in,
io::di, io::uo) is det.
output_int32_const_for_csharp(Stream, I32, !IO) :-
io.write_int32(Stream, I32, !IO).
:- pred output_uint32_const_for_csharp(io.text_output_stream::in, uint32::in,
io::di, io::uo) is det.
output_uint32_const_for_csharp(Stream, U32, !IO) :-
io.write_uint32(Stream, U32, !IO),
io.write_string(Stream, "U", !IO).
:- pred output_int64_const_for_csharp(io.text_output_stream::in, int64::in,
io::di, io::uo) is det.
output_int64_const_for_csharp(Stream, I64, !IO) :-
io.write_int64(Stream, I64, !IO),
io.write_string(Stream, "L", !IO).
:- pred output_uint64_const_for_csharp(io.text_output_stream::in, uint64::in,
io::di, io::uo) is det.
output_uint64_const_for_csharp(Stream, U64, !IO) :-
io.write_uint64(Stream, U64, !IO),
io.write_string(Stream, "UL", !IO).
%---------------------------------------------------------------------------%
output_code_addr_for_csharp(Info, Stream, CodeAddr, IsCall, !IO) :-
CodeAddr = mlds_code_addr(QualFuncLabel, Signature),
Signature = mlds_func_signature(ArgTypes, RetTypes),
(
IsCall = is_not_for_call,
% Not a function call, so we are taking the address of the
% wrapper for that function (method).
PtrTypeStr = method_ptr_type_to_string(Info, ArgTypes, RetTypes),
io.format(Stream, "(%s) ", [s(PtrTypeStr)], !IO)
;
IsCall = is_for_call
),
QualFuncLabel = qual_func_label(ModuleName, FuncLabel),
FuncLabel = mlds_func_label(ProcLabel, MaybeAux),
MaybeAuxSuffix = mlds_maybe_aux_func_id_to_suffix(MaybeAux),
Qualifier = qualifier_to_nll_string_for_csharp(ModuleName, module_qual),
ProcLabelStr =
proc_label_to_ll_string_for_csharp(MaybeAuxSuffix, ProcLabel),
io.format(Stream, "%s.%s", [s(Qualifier), s(ProcLabelStr)], !IO).
%---------------------------------------------------------------------------%
output_initializer_for_csharp(Info, Stream, OutputAux, Indent, Type,
Initializer, Suffix, !IO) :-
(
( Initializer = init_obj(_)
; Initializer = init_struct(_, _)
; Initializer = init_array(_)
),
io.write_string(Stream, " = ", !IO),
% Due to cyclic references, we need to separate the allocation and
% initialisation steps of RTTI structures. If InitStyle is alloc_only,
% then we output an initializer to allocate a structure without filling
% in the fields.
(
( OutputAux = oa_none
; OutputAux = oa_cname(_, _)
; OutputAux = oa_force_init
),
output_initializer_body_for_csharp(Info, Stream,
not_at_start_of_line, Indent + 1u, Initializer, yes(Type),
Suffix, !IO)
;
OutputAux = oa_alloc_only,
output_initializer_alloc_only_for_csharp(Info, Stream,
Initializer, yes(Type), Suffix, !IO)
)
;
Initializer = no_initializer,
(
OutputAux = oa_force_init,
% Local variables need to be initialised to avoid warnings.
io.write_string(Stream, " = ", !IO),
io.write_string(Stream,
get_default_initializer_for_csharp(Info, Type), !IO)
;
( OutputAux = oa_none
; OutputAux = oa_cname(_, _)
; OutputAux = oa_alloc_only
)
),
io.format(Stream, "%s\n", [s(Suffix)], !IO)
).
%---------------------%
output_initializer_alloc_only_for_csharp(Info, Stream, Initializer,
MaybeType, Suffix, !IO) :-
(
Initializer = no_initializer,
unexpected($pred, "no_initializer")
;
Initializer = init_obj(_),
unexpected($pred, "init_obj")
;
Initializer = init_struct(StructType, FieldInits),
io.write_string(Stream, "new ", !IO),
( if
StructType = mercury_nb_type(_, CtorCat),
type_category_is_array(coerce(CtorCat)) = is_array
then
Size = list.length(FieldInits),
io.format(Stream, "object[%d]%s\n", [i(Size), s(Suffix)], !IO)
else
io.format(Stream, "%s()%s\n",
[s(type_to_string_for_csharp(Info, StructType)), s(Suffix)],
!IO)
)
;
Initializer = init_array(ElementInits),
Size = list.length(ElementInits),
io.write_string(Stream, "new ", !IO),
(
MaybeType = yes(Type),
type_to_string_and_dims_for_csharp(Info, Type,
BaseTypeName, ArrayDims0),
make_last_dimension_known_size(ArrayDims0, Size, ArrayDims),
DimsStr = array_dimensions_to_string(ArrayDims),
io.format(Stream, "%s%s%s\n",
[s(BaseTypeName), s(DimsStr), s(Suffix)], !IO)
;
MaybeType = no,
% XXX we need to know the type here
io.format(Stream, "/* XXX init_array */ object[%d]%s\n",
[i(Size), s(Suffix)], !IO)
)
).
%---------------------%
output_initializer_body_for_csharp(Info, Stream, InitStart, Indent,
Initializer, MaybeType, Suffix, !IO) :-
(
Initializer = no_initializer,
unexpected($pred, "no_initializer")
;
Initializer = init_obj(Rval),
(
InitStart = not_at_start_of_line
;
InitStart = at_start_of_line,
write_indent2(Stream, Indent, !IO)
),
output_rval_for_csharp(Info, Rval, Stream, !IO),
io.format(Stream, "%s\n", [s(Suffix)], !IO)
;
Initializer = init_struct(StructType, FieldInits),
(
InitStart = not_at_start_of_line,
io.nl(Stream, !IO)
;
InitStart = at_start_of_line
),
IndentStr = indent2_string(Indent),
type_to_string_and_dims_for_csharp(Info, StructType,
TypeStr0, ArrayDims),
TypeStr = add_array_dimensions(TypeStr0, ArrayDims),
init_arg_wrappers_cs_java(ArrayDims, LParen, RParen),
(
FieldInits = [],
io.format(Stream, "%snew %s%s%s%s\n",
[s(IndentStr), s(TypeStr), s(LParen), s(RParen), s(Suffix)],
!IO)
;
FieldInits = [HeadFieldInit | TailFieldInits],
io.format(Stream, "%snew %s%s\n",
[s(IndentStr), s(TypeStr), s(LParen)], !IO),
output_initializer_body_list_for_csharp(Info, Stream, Indent + 1u,
HeadFieldInit, TailFieldInits, "", !IO),
io.format(Stream, "%s%s%s\n",
[s(IndentStr), s(RParen), s(Suffix)], !IO)
)
;
Initializer = init_array(ElementInits),
(
InitStart = not_at_start_of_line,
io.nl(Stream, !IO)
;
InitStart = at_start_of_line
),
IndentStr = indent2_string(Indent),
(
MaybeType = yes(Type),
TypeStr = type_to_string_for_csharp(Info, Type)
;
MaybeType = no,
% XXX We need to know the type here.
TypeStr = "/* XXX init_array */ object[]"
),
(
ElementInits = [],
io.format(Stream, "%snew %s {}%s\n",
[s(IndentStr), s(TypeStr), s(Suffix)], !IO)
;
ElementInits = [HeadElementInit | TailElementInits],
io.format(Stream, "%snew %s {\n", [s(IndentStr), s(TypeStr)], !IO),
output_initializer_body_list_for_csharp(Info, Stream, Indent + 1u,
HeadElementInit, TailElementInits, "", !IO),
io.format(Stream, "%s}%s\n", [s(IndentStr), s(Suffix)], !IO)
)
).
%---------------------%
output_nonempty_initializer_body_list_for_csharp(Info, Stream, Indent, Inits,
Suffix, !IO) :-
list.det_head_tail(Inits, HeadInit, TailInits),
output_initializer_body_list_for_csharp(Info, Stream, Indent,
HeadInit, TailInits, Suffix, !IO).
:- pred output_initializer_body_list_for_csharp(csharp_out_info::in,
io.text_output_stream::in, indent::in, mlds_initializer::in,
list(mlds_initializer)::in, string::in, io::di, io::uo) is det.
output_initializer_body_list_for_csharp(Info, Stream, Indent,
HeadInit, TailInits, Suffix, !IO) :-
(
TailInits = [],
output_initializer_body_for_csharp(Info, Stream, at_start_of_line,
Indent, HeadInit, no, Suffix, !IO)
;
TailInits = [HeadTailInit | TailTailInits],
output_initializer_body_for_csharp(Info, Stream, at_start_of_line,
Indent, HeadInit, no, ",", !IO),
output_initializer_body_list_for_csharp(Info, Stream,
Indent, HeadTailInit, TailTailInits, Suffix, !IO)
).
%---------------------------------------------------------------------------%
% We need to provide initializers for local variables to avoid problems
% with undefined variables.
%
:- func get_default_initializer_for_csharp(csharp_out_info, mlds_type)
= string.
get_default_initializer_for_csharp(Info, Type) = Initializer :-
(
Type = mercury_nb_type(_, CtorCat),
(
( CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_void
),
Initializer = "null"
;
( CtorCat = ctor_cat_enum(_)
; CtorCat = ctor_cat_user(_)
; CtorCat = ctor_cat_builtin_dummy
),
TypeName = type_to_string_for_csharp(Info, Type),
Initializer = "default(" ++ TypeName ++ ")"
)
;
Type = mlds_builtin_type_int(IntType),
(
( IntType = int_type_int
; IntType = int_type_int8
; IntType = int_type_int16
; IntType = int_type_int32
% C# byte and ushort literals don't have a suffix.
; IntType = int_type_uint8
; IntType = int_type_uint16
),
Initializer = "0"
;
( IntType = int_type_uint
; IntType = int_type_uint32
),
Initializer = "0U"
;
IntType = int_type_int64,
Initializer = "0L"
;
IntType = int_type_uint64,
Initializer = "0UL"
)
;
Type = mlds_builtin_type_float,
Initializer = "0"
;
Type = mlds_builtin_type_char,
Initializer = "'\\u0000'"
;
Type = mlds_builtin_type_string,
Initializer = "null"
;
Type = mlds_native_bool_type,
Initializer = "false"
;
( Type = mlds_mercury_array_type(_)
; Type = mlds_cont_type(_)
; Type = mlds_commit_type
; Type = mlds_class_type(_)
; Type = mlds_enum_class_type(_)
; Type = mlds_env_type(_)
; Type = mlds_struct_type(_)
; Type = mlds_array_type(_)
; Type = mlds_mostly_generic_array_type(_)
; Type = mlds_ptr_type(_)
; Type = mlds_func_type(_)
; Type = mlds_generic_type
; Type = mlds_generic_env_ptr_type
; Type = mlds_type_info_type
; Type = mlds_pseudo_type_info_type
; Type = mlds_rtti_type(_)
; Type = mlds_tabling_type(_)
),
Initializer = "null"
;
Type = mlds_foreign_type(ForeignType),
(
ForeignType = csharp(csharp_type(CsharpType)),
Initializer = "default(" ++ CsharpType ++ ")"
;
( ForeignType = c(_)
; ForeignType = java(_)
),
unexpected($pred, "wrong foreign language type")
)
;
Type = mlds_unknown_type,
unexpected($pred, "variable has unknown_type")
).
%---------------------------------------------------------------------------%
:- end_module ml_backend.mlds_to_cs_data.
%---------------------------------------------------------------------------%