[m-dev.] for review: MLDS back-end: implement typeclasses

Fergus Henderson fjh at cs.mu.OZ.AU
Thu May 11 03:09:36 AEST 2000


Sorry, I was a little bit premature in posting that diff.
Testing revealed that I had managed to break the LLDS back-end.
So I've made the following additional changes.

(I've enclosed first a relative diff, then a full diff
for the affected files.)

--- CHANGES16.old	Thu May 11 02:53:58 2000
+++ CHANGES16	Thu May 11 03:02:53 2000
@@ -61,7 +61,14 @@
 
 compiler/llds_out.m:
 compiler/rtti_out.m:
-	Move the code for handling dynamic initialization of
+	Change llds_out.m so that it calls a new predicate
+	output_base_typeclass_info_storage_type_name, defined in
+	rtti_out.m, when outputting base_typeclass_info declarations.
+	This is needed because base_typeclass_infos are now defined
+	as RTTI constants with type `Code * []', and so need to be
+	handled as such by rtti_out.m rather than being assumed to
+	be structs like the other LLDS data_names.
+	Also move the code for handling dynamic initialization of
 	method pointers from llds_out.m to rtti_out.m,
 	at the same time changing it to handle their new definitions
 	as rtti_data rather than comp_gen_c_data.
--- /tmp/fjh/mercury/compiler/llds_out.m	Thu May 11 02:49:28 2000
+++ ./llds_out.m	Thu May 11 02:50:42 2000
@@ -2915,21 +2915,28 @@
 
 output_data_addr_storage_type_name(ModuleName, DataVarName, BeingDefined,
 		LaterIndent) -->
-	{ data_name_linkage(DataVarName, Linkage) },
-	globals__io_get_globals(Globals),
-	{ c_data_linkage_string(Globals, Linkage, BeingDefined, LinkageStr) },
-	io__write_string(LinkageStr),
+	( { DataVarName = base_typeclass_info(ClassId, Instance) } ->
+		output_base_typeclass_info_storage_type_name(
+			ClassId, Instance, no)
+	;
+		{ data_name_linkage(DataVarName, Linkage) },
+		globals__io_get_globals(Globals),
+		{ c_data_linkage_string(Globals, Linkage, BeingDefined,
+			LinkageStr) },
+		io__write_string(LinkageStr),
 
-	{ data_name_would_include_code_address(DataVarName, InclCodeAddr) },
-	{ c_data_const_string(Globals, InclCodeAddr, ConstStr) },
-	io__write_string(ConstStr),
+		{ data_name_would_include_code_address(DataVarName,
+			InclCodeAddr) },
+		{ c_data_const_string(Globals, InclCodeAddr, ConstStr) },
+		io__write_string(ConstStr),
 
-	io__write_string("struct "),
-	output_data_addr(ModuleName, DataVarName),
-	io__write_string("_struct\n"),
-	io__write_string(LaterIndent),
-	io__write_string("\t"),
-	output_data_addr(ModuleName, DataVarName).
+		io__write_string("struct "),
+		output_data_addr(ModuleName, DataVarName),
+		io__write_string("_struct\n"),
+		io__write_string(LaterIndent),
+		io__write_string("\t"),
+		output_data_addr(ModuleName, DataVarName)
+	).
 
 :- pred data_name_linkage(data_name::in, linkage::out) is det.
 
--- /tmp/fjh/mercury/compiler/rtti_out.m	Thu May 11 02:49:36 2000
+++ ./rtti_out.m	Thu May 11 02:45:40 2000
@@ -23,6 +23,7 @@
 
 :- interface.
 
+:- import_module hlds_data.
 :- import_module rtti, llds_out.
 :- import_module bool, io.
 
@@ -56,6 +57,11 @@
 :- pred output_rtti_addr_storage_type_name(rtti_type_id::in, rtti_name::in,
 	bool::in, io__state::di, io__state::uo) is det.
 
+	% the same as output_rtti_addr_storage_type_name,
+	% but for a base_typeclass_info.
+:- pred output_base_typeclass_info_storage_type_name(class_id::in, string::in,
+		bool::in, io__state::di, io__state::uo) is det.
+
         % Return true iff the given type of RTTI data structure includes
 	% code addresses.
 :- pred rtti_name_would_include_code_addr(rtti_name::in, bool::out) is det.
@@ -72,7 +78,6 @@
 
 :- implementation.
 
-:- import_module hlds_data.
 :- import_module pseudo_type_info, code_util, llds, prog_out, c_util.
 :- import_module options, globals.
 :- import_module int, string, list, require, std_util.
@@ -359,8 +364,11 @@
 	{ CodeAddrs = list__map(make_code_addr, Methods) },
 	output_code_addrs_decls(CodeAddrs, "", "", 0, _, DeclSet0, DeclSet1),
 	io__write_string("\n"),
-	output_base_typeclass_info_decl(ClassId, InstanceString, yes,
-		DeclSet1, DeclSet),
+	output_base_typeclass_info_storage_type_name(ClassId, InstanceString,
+		yes),
+	% XXX It would be nice to avoid generating redundant declarations
+	% of base_typeclass_infos, but currently we don't.
+	{ DeclSet1 = DeclSet },
 	io__write_string(" = {\n\t(Code *) "),
 	io__write_list([N1, N2, N3, N4, N5], ",\n\t(Code *) ", io__write_int),
 	io__write_string(",\n\t"),
@@ -490,8 +498,7 @@
 	; { RttiData = base_typeclass_info(ClassId, InstanceStr, _) } ->
 		% rtti_data_to_name/3 does not handle this case
 		output_base_typeclass_info_decl(ClassId,
-			InstanceStr, no, DeclSet0, DeclSet),
-		io__write_string(";\n")
+			InstanceStr, no, DeclSet0, DeclSet)
 	;
 		{ rtti_data_to_name(RttiData, RttiTypeId, RttiName) },
 		output_generic_rtti_data_decl(RttiTypeId, RttiName,
@@ -504,13 +511,19 @@
 
 output_base_typeclass_info_decl(ClassId, InstanceStr,
 		BeingDefined, DeclSet0, DeclSet) -->
-	output_rtti_name_storage_type_name(
-		output_base_typeclass_info_name(ClassId, InstanceStr),
-		base_typeclass_info(ClassId, InstanceStr), BeingDefined),
-	% XXX It would be nice to avoid generating multiple declarations
+	output_base_typeclass_info_storage_type_name(ClassId, InstanceStr,
+			BeingDefined),
+	io__write_string(";\n"),
+	% XXX It would be nice to avoid generating redundant declarations
 	% of base_typeclass_infos, but currently we don't.
 	{ DeclSet = DeclSet0 }.
 
+output_base_typeclass_info_storage_type_name(ClassId, InstanceStr,
+		BeingDefined) -->
+	output_rtti_name_storage_type_name(
+		output_base_typeclass_info_name(ClassId, InstanceStr),
+		base_typeclass_info(ClassId, InstanceStr), BeingDefined).
+
 %-----------------------------------------------------------------------------%
 
 :- pred output_generic_rtti_data_decl(rtti_type_id::in, rtti_name::in,
@@ -712,8 +725,8 @@
 	; { RttiData = base_typeclass_info(ClassId, InstanceStr, _) } ->
 		% rtti_data_to_name/3 does not handle this case,
 		% so we need to handle it here
-		output_base_typeclass_info_decl(ClassId, InstanceStr,
-				no, DeclSet0, DeclSet),
+		output_base_typeclass_info_decl(ClassId, InstanceStr, no,
+			DeclSet0, DeclSet),
 		{ N = N0 }
 	;
 		{ rtti_data_to_name(RttiData, RttiTypeId, RttiName) },

**********************************************************************
*** FULL DIFF ***
**********************************************************************

Index: rtti_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/rtti_out.m,v
retrieving revision 1.10
diff -u -d -u -r1.10 rtti_out.m
--- rtti_out.m	2000/04/25 11:32:04	1.10
+++ rtti_out.m	2000/05/10 16:45:40
@@ -23,6 +23,7 @@
 
 :- interface.
 
+:- import_module hlds_data.
 :- import_module rtti, llds_out.
 :- import_module bool, io.
 
@@ -56,6 +57,11 @@
 :- pred output_rtti_addr_storage_type_name(rtti_type_id::in, rtti_name::in,
 	bool::in, io__state::di, io__state::uo) is det.
 
+	% the same as output_rtti_addr_storage_type_name,
+	% but for a base_typeclass_info.
+:- pred output_base_typeclass_info_storage_type_name(class_id::in, string::in,
+		bool::in, io__state::di, io__state::uo) is det.
+
         % Return true iff the given type of RTTI data structure includes
 	% code addresses.
 :- pred rtti_name_would_include_code_addr(rtti_name::in, bool::out) is det.
@@ -74,7 +80,7 @@
 
 :- import_module pseudo_type_info, code_util, llds, prog_out, c_util.
 :- import_module options, globals.
-:- import_module string, list, require, std_util.
+:- import_module int, string, list, require, std_util.
 
 %-----------------------------------------------------------------------------%
 
@@ -341,12 +347,40 @@
 %	io__write_string(",\n\t"),
 %	output_maybe_static_code_addr(Prettyprinter),
 	io__write_string("\n};\n").
+output_rtti_data_defn(base_typeclass_info(ClassId, InstanceString,
+		BaseTypeClassInfo), DeclSet0, DeclSet) -->
+	output_base_typeclass_info_defn(ClassId, InstanceString,
+		BaseTypeClassInfo, DeclSet0, DeclSet).
 output_rtti_data_defn(pseudo_type_info(Pseudo), DeclSet0, DeclSet) -->
 	output_pseudo_type_info_defn(Pseudo, DeclSet0, DeclSet).
 
+:- pred output_base_typeclass_info_defn(class_id, string, base_typeclass_info,
+		decl_set, decl_set, io__state, io__state).
+:- mode output_base_typeclass_info_defn(in, in, in, in, out, di, uo) is det.
+
+output_base_typeclass_info_defn(ClassId, InstanceString,
+		base_typeclass_info(N1, N2, N3, N4, N5, Methods),
+		DeclSet0, DeclSet) -->
+	{ CodeAddrs = list__map(make_code_addr, Methods) },
+	output_code_addrs_decls(CodeAddrs, "", "", 0, _, DeclSet0, DeclSet1),
+	io__write_string("\n"),
+	output_base_typeclass_info_storage_type_name(ClassId, InstanceString,
+		yes),
+	% XXX It would be nice to avoid generating redundant declarations
+	% of base_typeclass_infos, but currently we don't.
+	{ DeclSet1 = DeclSet },
+	io__write_string(" = {\n\t(Code *) "),
+	io__write_list([N1, N2, N3, N4, N5], ",\n\t(Code *) ", io__write_int),
+	io__write_string(",\n\t"),
+	io__write_list(CodeAddrs, ",\n\t", output_static_code_addr),
+	io__write_string("\n};\n").
+
 :- func make_maybe_code_addr(maybe(rtti_proc_label)) = maybe(code_addr).
 make_maybe_code_addr(no) = no.
-make_maybe_code_addr(yes(ProcLabel)) = yes(CodeAddr) :-
+make_maybe_code_addr(yes(ProcLabel)) = yes(make_code_addr(ProcLabel)).
+
+:- func make_code_addr(rtti_proc_label) = code_addr.
+make_code_addr(ProcLabel) = CodeAddr :-
 	code_util__make_entry_label_from_rtti(ProcLabel, no, CodeAddr).
 
 :- pred output_pseudo_type_info_defn(pseudo_type_info, decl_set, decl_set,
@@ -461,12 +495,35 @@
 		% so we don't need to declare them.
 		% Also rtti_data_to_name/3 does not handle this case.
 		{ DeclSet = DeclSet0 }
+	; { RttiData = base_typeclass_info(ClassId, InstanceStr, _) } ->
+		% rtti_data_to_name/3 does not handle this case
+		output_base_typeclass_info_decl(ClassId,
+			InstanceStr, no, DeclSet0, DeclSet)
 	;
 		{ rtti_data_to_name(RttiData, RttiTypeId, RttiName) },
 		output_generic_rtti_data_decl(RttiTypeId, RttiName,
 			DeclSet0, DeclSet)
 	).
 
+:- pred output_base_typeclass_info_decl(class_id::in, string::in,
+		bool::in, decl_set::in, decl_set::out,
+		io__state::di, io__state::uo) is det.
+
+output_base_typeclass_info_decl(ClassId, InstanceStr,
+		BeingDefined, DeclSet0, DeclSet) -->
+	output_base_typeclass_info_storage_type_name(ClassId, InstanceStr,
+			BeingDefined),
+	io__write_string(";\n"),
+	% XXX It would be nice to avoid generating redundant declarations
+	% of base_typeclass_infos, but currently we don't.
+	{ DeclSet = DeclSet0 }.
+
+output_base_typeclass_info_storage_type_name(ClassId, InstanceStr,
+		BeingDefined) -->
+	output_rtti_name_storage_type_name(
+		output_base_typeclass_info_name(ClassId, InstanceStr),
+		base_typeclass_info(ClassId, InstanceStr), BeingDefined).
+
 %-----------------------------------------------------------------------------%
 
 :- pred output_generic_rtti_data_decl(rtti_type_id::in, rtti_name::in,
@@ -488,6 +545,15 @@
 	{ decl_set_insert(DeclSet0, data_addr(DataAddr), DeclSet) }.
 
 output_rtti_addr_storage_type_name(RttiTypeId, RttiName, BeingDefined) -->
+	output_rtti_name_storage_type_name(
+		output_rtti_addr(RttiTypeId, RttiName),
+		RttiName, BeingDefined).
+
+:- pred output_rtti_name_storage_type_name(
+	pred(io__state, io__state)::pred(di, uo) is det,
+	rtti_name::in, bool::in, io__state::di, io__state::uo) is det.
+
+output_rtti_name_storage_type_name(OutputName, RttiName, BeingDefined) -->
 	output_rtti_type_decl(RttiName),
 	{ rtti_name_linkage(RttiName, Linkage) },
 	globals__io_get_globals(Globals),
@@ -501,7 +567,7 @@
 	{ rtti_name_c_type(RttiName, CType, Suffix) },
 	c_util__output_quoted_string(CType),
 	io__write_string(" "),
-	output_rtti_addr(RttiTypeId, RttiName),
+	OutputName,
 	io__write_string(Suffix).
 
 :- pred output_rtti_type_decl(rtti_name::in, io__state::di, io__state::uo)
@@ -568,9 +634,37 @@
 		io__write_int(Arity),
 		io__write_string("_0);\n")
 	;
+		{ Data = base_typeclass_info(ClassName, ClassArity,
+			base_typeclass_info(_N1, _N2, _N3, _N4, _N5,
+				Methods)) }
+	->
+		io__write_string("#ifndef MR_STATIC_CODE_ADDRESSES\n"),
+			% the field number for the first method is 5,
+			% since the methods are stored after N1 .. N5,
+			% and fields are numbered from 0.
+		{ FirstFieldNum = 5 },
+		{ CodeAddrs = list__map(make_code_addr, Methods) },
+		output_init_method_pointers(FirstFieldNum, CodeAddrs,
+			ClassName, ClassArity),
+		io__write_string("#endif /* MR_STATIC_CODE_ADDRESSES */\n")
+	;
 		[]
 	).
 
+:- pred output_init_method_pointers(int, list(code_addr), class_id, string,
+		io__state, io__state).
+:- mode output_init_method_pointers(in, in, in, in, di, uo) is det.
+
+output_init_method_pointers(_, [], _, _) --> [].
+output_init_method_pointers(FieldNum, [Arg|Args], ClassId, InstanceStr) -->
+	io__write_string("\t\t"),
+	io__write_string("MR_field(MR_mktag(0), "),
+	output_base_typeclass_info_name(ClassId, InstanceStr),
+	io__format(", %d) =\n\t\t\t", [i(FieldNum)]),
+	output_code_addr(Arg),
+	io__write_string(";\n"),
+	output_init_method_pointers(FieldNum + 1, Args, ClassId, InstanceStr).
+
 %-----------------------------------------------------------------------------%
 
 :- pred output_maybe_rtti_addrs_decls(rtti_type_id::in,
@@ -628,6 +722,12 @@
 		% Also rtti_data_to_name/3 does not handle this case.
 		{ DeclSet = DeclSet0 },
 		{ N = N0 }
+	; { RttiData = base_typeclass_info(ClassId, InstanceStr, _) } ->
+		% rtti_data_to_name/3 does not handle this case,
+		% so we need to handle it here
+		output_base_typeclass_info_decl(ClassId, InstanceStr, no,
+			DeclSet0, DeclSet),
+		{ N = N0 }
 	;
 		{ rtti_data_to_name(RttiData, RttiTypeId, RttiName) },
 		output_rtti_addr_decls(RttiTypeId, RttiName,
@@ -691,6 +791,9 @@
 		% rtti_data_to_name/3 does not handle this case
 		io__write_string("(MR_PseudoTypeInfo) "),
 		io__write_int(VarNum)
+	; { RttiData = base_typeclass_info(ClassId, InstanceStr, _) } ->
+		% rtti_data_to_name/3 does not handle this case
+		output_base_typeclass_info_name(ClassId, InstanceStr)
 	;
 		{ rtti_data_to_name(RttiData, RttiTypeId, RttiName) },
 		output_addr_of_rtti_addr(RttiTypeId, RttiName)
@@ -788,11 +891,16 @@
 	io__state::di, io__state::uo) is det.
 
 output_maybe_static_code_addr(yes(CodeAddr)) -->
+	output_static_code_addr(CodeAddr).
+output_maybe_static_code_addr(no) -->
+	io__write_string("NULL").
+
+:- pred output_static_code_addr(code_addr::in, io__state::di, io__state::uo)
+	is det.
+output_static_code_addr(CodeAddr) -->
 	io__write_string("MR_MAYBE_STATIC_CODE("),
 	output_code_addr(CodeAddr),
 	io__write_string(")").
-output_maybe_static_code_addr(no) -->
-	io__write_string("NULL").
 
 %-----------------------------------------------------------------------------%
 
@@ -809,6 +917,7 @@
 rtti_name_would_include_code_addr(du_stag_ordered_table(_),  no).
 rtti_name_would_include_code_addr(du_ptag_ordered_table,     no).
 rtti_name_would_include_code_addr(type_ctor_info,            yes).
+rtti_name_would_include_code_addr(base_typeclass_info(_, _), yes).
 rtti_name_would_include_code_addr(pseudo_type_info(Pseudo),
 		pseudo_type_info_would_incl_code_addr(Pseudo)).
 rtti_name_would_include_code_addr(type_hashcons_pointer,     no).
@@ -839,6 +948,7 @@
 rtti_name_c_type(du_ptag_ordered_table,    "MR_DuPtagLayout", "[]").
 rtti_name_c_type(type_ctor_info,           "struct MR_TypeCtorInfo_Struct",
 						"").
+rtti_name_c_type(base_typeclass_info(_, _), "Code *", "[]").
 rtti_name_c_type(pseudo_type_info(Pseudo), TypePrefix, TypeSuffix) :-
 	pseudo_type_info_name_c_type(Pseudo, TypePrefix, TypeSuffix).
 rtti_name_c_type(type_hashcons_pointer,    "union MR_TableNode_Union **", "").
Index: llds_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/llds_out.m,v
retrieving revision 1.142
diff -u -d -u -r1.142 llds_out.m
--- llds_out.m	2000/04/26 05:40:20	1.142
+++ llds_out.m	2000/05/10 16:50:42
@@ -182,6 +182,12 @@
 :- pred llds_out__make_base_typeclass_info_name(class_id, string, string).
 :- mode llds_out__make_base_typeclass_info_name(in, in, out) is det.
 
+	% output the name for base_typeclass_info,
+	% with the appropriate "mercury_data_" prefix.
+
+:- pred output_base_typeclass_info_name(class_id, string, io__state, io__state).
+:- mode output_base_typeclass_info_name(in, in, di, uo) is det.
+
 	% Convert a label to a string description of the stack layout
 	% structure of that label.
 
@@ -656,14 +662,6 @@
 	->
 		rtti_out__init_rtti_data_if_nec(RttiData)
 	;
-		{ Data = comp_gen_c_data(ModuleName, DataName, _, ArgRvals,
-			_, _) },
-		{ DataName = base_typeclass_info(_ClassName, _ClassArity) }
-	->
-		io__write_string("#ifndef MR_STATIC_CODE_ADDRESSES\n"),
-		output_init_method_pointers(1, ArgRvals, DataName, ModuleName),
-		io__write_string("#endif /* MR_STATIC_CODE_ADDRESSES */\n")
-	;
 		{ Data = comp_gen_c_data(ModuleName, DataName, _, _, _, _) },
 		{ DataName = module_layout }
 	->
@@ -677,23 +675,6 @@
 	),
 	output_c_data_init_list(Datas).
 
-:- pred output_init_method_pointers(int, list(maybe(rval)), data_name, module_name,
-	io__state, io__state).
-:- mode output_init_method_pointers(in, in, in, in, di, uo) is det.
-
-output_init_method_pointers(_, [], _, _) --> [].
-output_init_method_pointers(ArgNum, [Arg|Args], DataName, ModuleName) -->
-	( { Arg = yes(const(code_addr_const(CodeAddr))) } ->
-		io__write_string("\t\t"),
-		output_data_addr(ModuleName, DataName),
-		io__format(".f%d =\n\t\t\t", [i(ArgNum)]),
-		output_code_addr(CodeAddr),
-		io__write_string(";\n")
-	;
-		[]
-	),
-	output_init_method_pointers(ArgNum + 1, Args, DataName, ModuleName).
-
 	% Output a comment to tell mkinit what functions to
 	% call from <module>_init.c.
 :- pred output_init_comment(module_name, io__state, io__state).
@@ -2934,21 +2915,28 @@
 
 output_data_addr_storage_type_name(ModuleName, DataVarName, BeingDefined,
 		LaterIndent) -->
-	{ data_name_linkage(DataVarName, Linkage) },
-	globals__io_get_globals(Globals),
-	{ c_data_linkage_string(Globals, Linkage, BeingDefined, LinkageStr) },
-	io__write_string(LinkageStr),
+	( { DataVarName = base_typeclass_info(ClassId, Instance) } ->
+		output_base_typeclass_info_storage_type_name(
+			ClassId, Instance, no)
+	;
+		{ data_name_linkage(DataVarName, Linkage) },
+		globals__io_get_globals(Globals),
+		{ c_data_linkage_string(Globals, Linkage, BeingDefined,
+			LinkageStr) },
+		io__write_string(LinkageStr),
 
-	{ data_name_would_include_code_address(DataVarName, InclCodeAddr) },
-	{ c_data_const_string(Globals, InclCodeAddr, ConstStr) },
-	io__write_string(ConstStr),
+		{ data_name_would_include_code_address(DataVarName,
+			InclCodeAddr) },
+		{ c_data_const_string(Globals, InclCodeAddr, ConstStr) },
+		io__write_string(ConstStr),
 
-	io__write_string("struct "),
-	output_data_addr(ModuleName, DataVarName),
-	io__write_string("_struct\n"),
-	io__write_string(LaterIndent),
-	io__write_string("\t"),
-	output_data_addr(ModuleName, DataVarName).
+		io__write_string("struct "),
+		output_data_addr(ModuleName, DataVarName),
+		io__write_string("_struct\n"),
+		io__write_string(LaterIndent),
+		io__write_string("\t"),
+		output_data_addr(ModuleName, DataVarName)
+	).
 
 :- pred data_name_linkage(data_name::in, linkage::out) is det.
 
@@ -3265,11 +3253,7 @@
 			% instance decls, even if they are in a different
 			% module
 		{ VarName = base_typeclass_info(ClassId, TypeNames) },
-		{ llds_out__make_base_typeclass_info_name(ClassId, TypeNames,
-			Str) },
-		io__write_string(mercury_data_prefix),
-		io__write_string("__"),
-		io__write_string(Str)
+		output_base_typeclass_info_name(ClassId, TypeNames)
 	;
 		{ VarName = module_layout },
 		io__write_string(mercury_data_prefix),
@@ -4230,6 +4214,12 @@
 	llds_out__name_mangle(TypeNames, MangledTypeNames),
 	string__append_list(["base_typeclass_info_", MangledClassString,
 		"__arity", ArityString, "__", MangledTypeNames], Str).
+
+output_base_typeclass_info_name(ClassId, TypeNames) -->
+	{ llds_out__make_base_typeclass_info_name(ClassId, TypeNames, Str) },
+	io__write_string(mercury_data_prefix),
+	io__write_string("__"),
+	io__write_string(Str).
 
 %-----------------------------------------------------------------------------%
 
-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3        |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list