[m-rev.] for review: C# exported proc arguments and documentation

Peter Wang novalazy at gmail.com
Thu Oct 14 13:31:12 AEDT 2010


Branches: main

When a procedure has multiple output arguments, make the exported C# method
have `out' arguments at the same argument positions as the Mercury procedure.
(Non-exported procedures have all output arguments at the end of the argument
list, for reasons described elsewhere.)  When a procedure has a single output
argument, it is always returned as the method's return value.

Also begin documenting the foreign language interface for C#.

compiler/ml_proc_gen.m:
compiler/mlds_to_cs.m:
        As above.

library/rtti_implementation.m:
        Conform to the change.

doc/reference_manual.texi:
        Document the foreign language interface for C#.

diff --git a/compiler/ml_proc_gen.m b/compiler/ml_proc_gen.m
index 371e9ed..5934a1c 100644
--- a/compiler/ml_proc_gen.m
+++ b/compiler/ml_proc_gen.m
@@ -226,7 +226,9 @@ ml_gen_export_proc_params(ModuleInfo, PredId, ProcId, FuncParams) :-
     module_info_get_globals(ModuleInfo, Globals),
     globals.get_target(Globals, Target),
     (
-        Target = target_java,
+        ( Target = target_java
+        ; Target = target_csharp
+        ),
         globals.set_option(det_copy_out, bool(no), Globals, GlobalsByRef),
         module_info_set_globals(GlobalsByRef, ModuleInfo, ModuleInfoByRef),
         FuncParamsByRef = ml_gen_proc_params(ModuleInfoByRef, PredId, ProcId),
diff --git a/compiler/mlds_to_cs.m b/compiler/mlds_to_cs.m
index 59b7871..3d749a6 100644
--- a/compiler/mlds_to_cs.m
+++ b/compiler/mlds_to_cs.m
@@ -289,7 +289,6 @@ mlds_get_csharp_foreign_code(AllForeignCode) = ForeignCode :-
 output_exports(Info, Indent, Exports, !IO) :-
     list.foldl(output_export(Info, Indent), Exports, !IO).
 
-    % XXX it would be better to present exports with the original argument order
 :- pred output_export(csharp_out_info::in, indent::in, mlds_pragma_export::in,
     io::di, io::uo) is det.
 
@@ -309,34 +308,52 @@ output_export(Info, Indent, Export, !IO) :-
 
     (
         ReturnTypes = [],
-        io.write_string("void ", !IO),
-        OutParams = []
+        io.write_string("void ", !IO)
     ;
-        ReturnTypes = [RetType | OutParamTypes],
+        ReturnTypes = [RetType],
         output_type(Info, RetType, !IO),
-        io.write_string(" ", !IO),
-        list.map_foldl(make_out_param, OutParamTypes, OutParams, 2, _)
+        io.write_string(" ", !IO)
+    ;
+        ReturnTypes = [_, _ | _],
+        unexpected(this_file,
+            "output_export: multiple return values in export method")
     ),
     io.write_string(ExportName, !IO),
-    output_params(Info, Indent + 1, Parameters ++ OutParams, !IO),
+    output_params(Info, Indent + 1, Parameters, !IO),
     io.nl(!IO),
     indent_line(Indent, !IO),
     io.write_string("{\n", !IO),
     indent_line(Indent + 1, !IO),
+    list.filter(is_out_argument, Parameters, OutArgs, InArgs),
     (
-        ReturnTypes = []
+        ReturnTypes = [],
+        (
+            OutArgs = [],
+            RestOutArgs = []
+        ;
+            OutArgs = [FirstOutArg | RestOutArgs],
+            FirstOutArg = mlds_argument(FirstOutArgName, _, _),
+            output_name(FirstOutArgName, !IO),
+            io.write_string(" = ", !IO)
+        )
     ;
         ReturnTypes = [RetTypeB | _],
         % The cast is required when the exported method uses generics but the
         % underlying method does not use generics (i.e. returns Object).
         io.write_string("return (", !IO),
         output_type(Info, RetTypeB, !IO),
-        io.write_string(") ", !IO)
+        io.write_string(") ", !IO),
+        RestOutArgs = OutArgs
     ),
-    write_export_call(MLDS_Name, Parameters ++ OutParams, !IO),
+    write_export_call(MLDS_Name, InArgs ++ RestOutArgs, !IO),
     indent_line(Indent, !IO),
     io.write_string("}\n", !IO).
 
+:- pred is_out_argument(mlds_argument::in) is semidet.
+
+is_out_argument(mlds_argument(_, Type, _)) :-
+    Type = mlds_ptr_type(_).
+
 :- pred write_export_call(mlds_qualified_entity_name::in,
     list(mlds_argument)::in, io::di, io::uo) is det.
 
diff --git a/doc/reference_manual.texi b/doc/reference_manual.texi
index 5896043..07d9ddd 100644
--- a/doc/reference_manual.texi
+++ b/doc/reference_manual.texi
@@ -50,6 +50,7 @@ into another language, under the above conditions for modified versions.
 @author Tyson Dowd
 @author Ralph Becket
 @author Mark Brown
+ at author Peter Wang
 @page
 @vskip 0pt plus 1filll
 Copyright @copyright{} 1995--2010 The University of Melbourne.
@@ -4894,7 +4895,7 @@ directives that have not yet been executed will not be executed,
 @samp{initialize} is also allowed as a synonym for @samp{initialise}.
 
 Note: @samp{initialise} declarations are currently only supported on
-the C, Java and Erlang backends.
+the C, C#, Java and Erlang backends.
 
 @node Module finalisation
 @section Module finalisation
@@ -4935,7 +4936,7 @@ be executed.
 @samp{finalize} is also allowed as a synonym for @samp{finalise}.
 
 Note: @samp{finalise} declarations are currently only supported on
-the C, Java and Erlang backends.
+the C, C#, Java and Erlang backends.
 
 @node Module-local mutable variables
 @section Module-local mutable variables
@@ -5066,7 +5067,7 @@ an uncaught exception is also the same as though it were a predicate
 specified in a @samp{initialise} directive.
 
 Note: @samp{mutable} declarations are currently only supported on
-the C, Java and Erlang backends.
+the C, C#, Java and Erlang backends.
 
 @node Type classes
 @chapter Type classes
@@ -6883,7 +6884,8 @@ to the foreign language's parameter passing convention.
 
 @menu
 * C data passing conventions ::
-* IL and C# data passing conventions ::
+* C# data passing conventions ::
+* IL data passing conventions ::
 * Java data passing conventions ::
 * Erlang data passing conventions ::
 @end menu
@@ -6971,15 +6973,130 @@ Omitting the call to @samp{MR_word_to_float} in the above example would yield
 incorrect results for implementations where @samp{sizeof(MR_Float)} is greater
 than @samp{sizeof(MR_Word)}.
 
- at node IL and C# data passing conventions
- at subsection IL and C# data passing conventions
+ at node C# data passing conventions
+ at subsection C# data passing conventions
 
 The Mercury types @code{int}, @code{float}, @code{char},
-and @code{string} are mapped to the Common Language Runtime types 
+and @code{string} are mapped to the Common Language Infrastructure (CLI) types
 @code{System.Int32}, @code{System.Double}, @code{System.Char} and
 @code{System.String} respectively, which correspond to the C# types
- at code{int}, @code{double}, @code{char}, and @code{string},
-and to the IL assembler types
+ at code{int}, @code{double}, @code{char}, and @code{string}.
+
+For the Mercury standard library type @samp{bool.bool}, there is a
+corresponding C# type, @code{mr_bool.Bool_0}.  C# code can refer to the
+boolean data constructors @samp{yes} and @samp{no}, as @code{mr_bool.YES}
+and @code{mr_bool.NO} respectively.
+
+For the Mercury standard library type @samp{builtin.comparison_result}, there
+is a corresponding C# type, @code{builtin.Comparison_result_0}.  C# code
+can refer to the data constructors of this type, @samp{(<)}, @samp{(=)} and
+ at samp{(>)}, as @code{builtin.COMPARE_LESS}, @code{builtin.COMPARE_EQUAL}
+and @code{builtin.COMPARE_GREATER} respectively.
+
+Mercury variables of a type for which there is a C# @samp{pragma
+foreign_type} declaration (@pxref{Using foreign types from Mercury}) will be
+passed as the corresponding C# type.
+Both reference and value types are supported.
+
+Mercury tuple types are passed as @samp{object[]} where
+the length of the array is the number of elements in the tuple.
+
+Mercury variables whose type is a type variable will be passed as
+ at code{System.Object}.
+
+Mercury variables whose type is a Mercury discriminated union type
+will be passed as a CLI type whose type name is determined from
+the Mercury type name (ignoring any type parameters) followed by
+an underscore and then the type arity,
+expressed as a decimal integer.
+The first character of the type name will have its case inverted,
+and the name may be mangled to satisfy C# lexical rules.
+
+ at noindent
+For example, the following Mercury type corresponds to the C# class
+that follows (some implementation details elided):
+
+ at example
+:- type maybe(T)
+    --->    yes(yes_field :: T)
+    ;       no.
+
+public static class Maybe_1 @{
+    public static class Yes_1 : Maybe_1 @{
+        public object yes_field;
+        public Yes_1(object x) @{ ... @}
+    @}
+    public static class No_0 : Maybe_1 @{
+        public No_0() @{ ... @}
+    @}
+@}
+ at end example
+
+C# code generated by the Mercury compiler is placed in the @samp{mercury}
+namespace.  Mercury module qualifiers are converted into a C# class name by
+concatenating the components with double underscore separators (@samp{__}).
+For example the Mercury type @samp{foo.bar.baz/1} will be passed as the C#
+type @samp{mercury.foo__bar.Baz_1}.
+
+Mercury array types are mapped to @samp{System.Array}.
+
+Mercury variables whose type is a Mercury equivalence type
+will be passed as the representation of the right hand side of the
+equivalence type.
+
+This mapping is subject to change and you should try to avoid writing
+code that relies heavily upon a particular representation of Mercury
+terms.
+
+Mercury arguments declared with input modes are passed by value to the
+C# function.
+
+Arguments of type @samp{io.state} or @samp{store.store(_)} are not
+passed or returned at all.
+(The reason for this is that these types represent mutable state,
+and in C# modifications to mutable state are done via side effects,
+rather than argument passing.)
+
+The handling of multiple output arguments is as follows.
+
+If the Mercury procedure is deterministic and has no output arguments,
+then the return type of the C# function is @samp{void}; if it has
+one output argument, then the return value of the function is that
+output argument.
+
+If the Mercury procedure is deterministic and has two or more output
+arguments, then the return type of the C# function is @samp{void}.
+At the position of each output argument, the C# function has an
+ at samp{out} parameter.
+
+If the Mercury procedure is semi-deterministic then the C# function
+returns a @samp{bool}.  A @samp{true} return value denotes success
+and @samp{false} denotes failure.  Output arguments are handled in the
+same way as multiple outputs for deterministic procedures, using @samp{out}
+parameters.  On failure the values of the @samp{val} fields are
+undefined.
+
+Mercury lists can be manipulated by C# code using the following methods,
+which are defined by the Mercury implementation.
+
+ at example
+bool      list.is_empty(List_1 list)     // test if a list is empty
+object    list.det_head(List_1 list)     // get the head of a list
+List_1    list.det_tail(List_1 list)     // get the tail of a list
+List_1    list.empty_list()              // create an empty list
+List_1    list.cons(object head, List_1 tail)
+                                         // construct a list with
+                                         //  the given head and tail
+ at end example
+
+ at node IL data passing conventions
+ at subsection IL data passing conventions
+
+The Mercury types @code{int}, @code{float}, @code{char},
+and @code{string} are mapped to the Common Language Infrastructure (CLI) types
+ at code{System.Int32}, @code{System.Double}, @code{System.Char} and
+ at code{System.String} respectively, which correspond
+to the IL assembler types
 @samp{int}, @samp{float64}, @samp{char} and @samp{string}.
 
 Mercury variables whose type is a type variable will be passed as
@@ -7089,14 +7206,14 @@ public static class Maybe_1<T> @{
 @}
 @end example
 
-Mercury array types are mapped to Java array types.
-
 Java code generated by the Mercury compiler is placed in the @samp{jmercury}
 package.  Mercury module qualifiers are converted into a Java class name by
 concatenating the components with double underscore separators (@samp{__}).
 For example the Mercury type @samp{foo.bar.baz/1} will be passed as the Java
 type @samp{jmercury.foo__bar.Baz_1}.
 
+Mercury array types are mapped to Java array types.
+
 Mercury variables whose type is a Mercury equivalence type
 will be passed as the representation of the right hand side of the
 equivalence type.
@@ -7995,6 +8112,9 @@ Mercury User's Guide.
 
 @menu
 * Using pragma foreign_type for C#	:: Declaring C# types in Mercury
+* Using pragma foreign_export_enum for C# :: Using Mercury enumerations in C#
+* Using pragma foreign_enum for C#      :: Assigning Mercury enumerations
+                                           values in C#
 * Using pragma foreign_proc for C# 	:: Calling C# code from Mercury
 * Using pragma foreign_export for C#    :: Calling Mercury code from C#
 * Using pragma foreign_decl for C# 	:: Including C# declarations in Mercury
@@ -8004,11 +8124,40 @@ Mercury User's Guide.
 @node Using pragma foreign_type for C#
 @subsubsection Using pragma foreign_type for C#
 
-There is currently no direct support for using C# types from Mercury;
-however, the types for IL are compatible with C#, and so the foreign_type
-support for IL can be used instead.
-See the section on using pragma foreign_type for IL
-(@pxref{Using pragma foreign_type for IL}).
+A C# @samp{pragma foreign_type} declaration has the form:
+
+ at example
+:- pragma foreign_type("C#", @var{MercuryTypeName}, "@var{C#-Type}").
+ at end example
+
+The @var{C#-Type} can be any accessible C# type.
+
+The effect of this declaration is that Mercury values of type
+ at var{MercuryTypeName} will be passed to and from C# foreign_procs
+as having type @var{C#-Type}.
+
+Furthermore, any Mercury procedure exported with @samp{pragma foreign_export}
+will use @var{C#-Type} as the type for any
+parameters whose Mercury type is @var{MercuryTypeName}.
+
+ at node Using pragma foreign_export_enum for C#
+ at subsubsection Using pragma foreign_export_enum for C#
+
+For C# the symbolic names generated by a @samp{pragma foreign_export_enum}
+must form valid C# identifiers.
+These identifiers are used as the names of static class members.
+
+The default mapping used by @samp{pragma foreign_export_enum}
+declarations for C# is to use the Mercury constructor name as the
+base of the symbolic name.  For example, the symbolic name for
+the Mercury constructor @samp{foo} would be @code{foo}.
+
+ at node Using pragma foreign_enum for C#
+ at subsubsection Using pragma foreign_enum for C#
+
+Foreign enumeration values in C# must be a constant value expression
+which is a valid initializer within an enumeration of underlying
+type @samp{int}.
 
 @node Using pragma foreign_proc for C#
 @subsubsection Using pragma foreign_proc for C#
@@ -8023,7 +8172,7 @@ functions declared with @samp{pragma foreign_code}.
 The input and output variables for a C# @samp{pragma foreign_proc} will
 have C# types corresponding to their Mercury types.  The exact rules
 for mapping Mercury types to C# types are described in
- at ref{IL and C# data passing conventions}.
+ at ref{C# data passing conventions}.
 
 C# code in a @code{pragma foreign_proc} declaration
 for any procedure whose determinism indicates that it can fail
@@ -8053,7 +8202,33 @@ set @code{SUCCESS_INDICATOR} to false.
 @node Using pragma foreign_export for C#
 @subsubsection Using pragma foreign_export for C#
 
- at samp{pragma foreign_export} is not currently supported for C#.
+A @samp{pragma foreign_export} declaration for C# has the form:
+
+ at example
+:- pragma foreign_export("C#", @var{MercuryMode}, "@var{C#_Name}").
+ at end example
+
+For example,
+
+ at example
+:- pragma foreign_export("C#", foo(in, in, out), "FOO").
+ at end example
+
+The type signature of the C# interface to a Mercury procedure is as
+described in @ref{C# data passing conventions}.
+
+Calling polymorphically typed Mercury procedures from C# is a little bit more
+difficult than calling ordinary (monomorphically typed) Mercury procedures.
+The simplest method is to just create monomorphic forwarding procedures that
+call the polymorphic procedures, and export them, rather than exporting the
+polymorphic procedures.
+
+If you do export a polymorphically typed Mercury procedure, the compiler
+will prepend one @samp{type_info} argument to the parameter list of
+the Java interface function for each polymorphic type variable in the
+Mercury procedure's type signature.  The caller must arrange to pass
+in appropriate @samp{type_info} values corresponding to the types
+of the other arguments passed.
 
 @node Using pragma foreign_decl for C#
 @subsubsection Using pragma foreign_decl for C#
@@ -8231,7 +8406,7 @@ succeeds, the IL code must set the values of all output arguments.
 @c set @code{success} to false (zero).
 
 Each of the head variables will be represented by the Common Language
-Runtime types as specified in @ref{IL and C# data passing conventions}.
+Runtime types as specified in @ref{IL data passing conventions}.
 
 @node Using pragma foreign_export for IL
 @subsubsection Using pragma foreign_export for IL
@@ -8257,7 +8432,7 @@ any .NET language.
 
 The type signature of the IL interface to a Mercury procedure is determined as
 follows.  Mercury types are converted to IL types according to the rules in
- at ref{IL and C# data passing conventions}.  Input arguments are passed by value.
+ at ref{IL data passing conventions}.  Input arguments are passed by value.
 For output arguments, the caller must pass the address in which to store the
 result.  If the Mercury procedure can fail, then its IL interface method
 returns a truth value indicating success or failure.  If the Mercury procedure
@@ -9140,6 +9315,8 @@ True if the module is compiled with --highlevel-code enabled.
 True if the target language of the compilation is C.
 @item @samp{il}
 True if the target language of the compilation is MS IL.
+ at item @samp{csharp}
+True if the target language of the compilation is C#.
 @item @samp{java}
 True if the target language of the compilation is Java.
 @item @samp{erlang}
diff --git a/library/rtti_implementation.m b/library/rtti_implementation.m
index 17807c5..c80ff33 100644
--- a/library/rtti_implementation.m
+++ b/library/rtti_implementation.m
@@ -1694,7 +1694,9 @@ is_exist_pseudo_type_info(_, _) :-
         object[] args = new object[arity];
 
         for (int i = 0; i < arity; i++) {
-            univ.ML_unravel_univ((univ.Univ_0) list.det_head(lst), out args[i]);
+            TypeInfo_Struct ti;
+            univ.ML_unravel_univ(out ti,
+                (univ.Univ_0) list.det_head(lst), out args[i]);
             lst = list.det_tail(lst);
         }
 
@@ -2241,8 +2243,9 @@ construct(_, _, _) = _ :-
     object[] args_array = new object[Arity];
 
     for (int i = 0; i < Arity; i++) {
-        univ.ML_unravel_univ((univ.Univ_0) list.det_head(args_list),
-            out args_array[i]);
+        TypeInfo_Struct ti_tmp;
+        univ.ML_unravel_univ(out ti_tmp,
+            (univ.Univ_0) list.det_head(args_list), out args_array[i]);
         args_list = list.det_tail(args_list);
     }
 

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list