[m-rev.] for review: multiple foreign language support
Peter Ross
peter.ross at miscrit.be
Wed Jul 18 21:36:07 AEST 2001
On Wed, Jul 18, 2001 at 03:00:02AM +1000, Tyson Dowd wrote:
> Hi,
>
> There is a small outstanding bug in this change that you can write
> multiple foreign_procs in a single language and the compiler won't
> complain.
>
> Apart than that, it seems to work pretty well.
>
> (there is no support for "il" as a foreign language because that change
> isn't commited yet).
>
> ===================================================================
>
>
> Estimated hours taken: 8
> Branches: main
>
> Support multiple language foreign_proc in the one file.
>
> If there is more then one applicable foreign_proc for a
> given clause, select the most "preferred" programming
> language to use as the implementation.
>
> Currently only the IL backend has multiple languages
> supported by the backend, and C# is preferred over MC++.
>
> compiler/assertion.m:
> compiler/clause_to_proc.m:
> compiler/dead_proc_elim.m:
> compiler/goal_util.m:
> compiler/hlds_out.m:
> compiler/inlining.m:
> compiler/intermod.m:
> compiler/modes.m:
> compiler/polymorphism.m:
> compiler/purity.m:
> compiler/typecheck.m:
> compiler/unify_proc.m:
> Handle the extra field in clause.
>
Place this comment after the comment to hlds_pred.
> compiler/hlds_pred.m:
> Add an extra field to clause which records which language this
> clause has been implemented in.
>
> compiler/options.m:
> compiler/handle_options.m:
> Rename backend_foreign_language as backend_foreign_languages,
> and use it to record the list of foreign languages the selected
> backend can handle.
>
> compiler/foreign.m:
> Update code to use the list of backend foreing languages.
> Add compare_Foreign_language function, to compute the preferred
> foreign language ordering for each backend.
> (much of the rest of the code in this module is intended to deal with
> the case where the backend *doesn't* handle the foreign
> language, but we don't have any working support for that at the
> moment).
>
s/foreing/foreign/
s/_Foreign/_foreign/
> compiler/globals.m:
> Add globals__io_get_backend_foreign_languages.
>
> compiler/make_hlds.m:
> Handle selection foreign_proc code depending upon the preferred
> language.
> Rename a few *_foreign_code predicates as *_foreign_proc
> predicates.
> Handle the extra field in clause.
>
s/Handle selection/Handle selection of/
>
> compiler/ml_code_gen.m:
> compiler/mlds.m:
> Generate different mlds__foreign_code for each language, put
> them in a map which is indexed on foreign language.
>
I think you should make clear that only one foreign code version of a
predicate is generated. ie if you have a predicate implemented in two
languages only foreign code for the preferred one is generated.
>
> compiler/mlds_to_c.m:
> compiler/mlds_to_csharp.m:
> compiler/mlds_to_gcc.m:
> compiler/mlds_to_mcpp.m:
> Select the appropriate mlds__foreign code from the map and
> generate code for it.
>
> Index: compiler/foreign.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/foreign.m,v
> retrieving revision 1.4
> diff -u -r1.4 foreign.m
> --- compiler/foreign.m 2001/05/02 11:36:34 1.4
> +++ compiler/foreign.m 2001/07/17 16:51:25
> @@ -89,69 +141,86 @@
> WantedLang = Lang),
> Bodys0, LangBodys, NotLangBodys).
>
> -foreign__extrude_pragma_implementation(TargetLang, _PragmaVars,
> - _PredName, _PredOrFunc, _Context,
> +foreign__extrude_pragma_implementation([], _PragmaVars,
> + _PredName, _PredOrFunc, _Context, _ModuleInfo0, _Attributes, _Impl0,
> + _ModuleInfo, _NewAttributes, _Impl) :-
> + error("foreign__extrude_pragma_implementation:"
> + ++ "no target languages available.").
> +
I would use unexpected here instead.
> Index: compiler/make_hlds.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/make_hlds.m,v
> retrieving revision 1.375
> diff -u -r1.375 make_hlds.m
> --- compiler/make_hlds.m 2001/07/07 09:05:19 1.375
> +++ compiler/make_hlds.m 2001/07/17 16:51:31
> @@ -694,7 +694,7 @@
> { Pragma = foreign_proc(Attributes, Pred, PredOrFunc,
> Vars, VarSet, PragmaImpl) }
> ->
> - module_add_pragma_foreign_code(Attributes,
> + module_add_pragma_foreign_proc(Attributes,
> Pred, PredOrFunc, Vars, VarSet, PragmaImpl,
> Status, Context, Module0, Module, Info0, Info)
> ;
> @@ -981,7 +981,7 @@
> %
> do_construct_pred_or_func_call(PredId, PredOrFunc, SymName,
> Args, GoalInfo, Goal),
> - Clause = clause(ProcIds, Goal, Context),
> + Clause = clause(ProcIds, Goal, mercury, Context),
> map__init(TI_VarMap),
> map__init(TCI_VarMap),
> map__init(TVarNameMap),
> @@ -2938,7 +2938,7 @@
> set__list_to_set(HeadVars, NonLocals),
> goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo),
> Goal = Call - GoalInfo,
> - Clause = clause([], Goal, Context),
> + Clause = clause([], Goal, mercury, Context),
>
> %
> % put the clause we just built into the pred_info,
> @@ -3863,7 +3863,7 @@
> InstancePredName, HeadVars, GoalInfo, IntroducedGoal,
> transform_info(ModuleInfo0, QualInfo0),
> transform_info(ModuleInfo, QualInfo)),
> - IntroducedClause = clause([], IntroducedGoal, Context),
> + IntroducedClause = clause([], IntroducedGoal, mercury, Context),
>
> map__from_corresponding_lists(HeadVars, ArgTypes, VarTypes),
> map__init(TVarNameMap),
> @@ -4079,7 +4079,7 @@
> %
> % Add the code for this `pragma import' to the clauses_info
> %
> - clauses_info_add_pragma_foreign_code(Clauses0, Purity, Attributes,
> + clauses_info_add_pragma_foreign_proc(Clauses0, Purity, Attributes,
> PredId, ProcId, VarSet, PragmaVars, ArgTypes, PragmaImpl,
> Context, PredOrFunc, qualified(PredModule, PredName),
> Arity, Clauses, ModuleInfo0, ModuleInfo, Info0, Info),
> @@ -4091,15 +4091,15 @@
>
> %-----------------------------------------------------------------------------%
>
> -:- pred module_add_pragma_foreign_code(pragma_foreign_proc_attributes,
> +:- pred module_add_pragma_foreign_proc(pragma_foreign_proc_attributes,
> sym_name, pred_or_func, list(pragma_var), prog_varset,
> pragma_foreign_code_impl, import_status, prog_context,
> module_info, module_info, qual_info, qual_info, io__state,
> io__state).
> -:- mode module_add_pragma_foreign_code(in, in, in, in, in, in, in, in,
> +:- mode module_add_pragma_foreign_proc(in, in, in, in, in, in, in, in,
> in, out, in, out, di, uo) is det.
>
> -module_add_pragma_foreign_code(Attributes, PredName, PredOrFunc,
> +module_add_pragma_foreign_proc(Attributes, PredName, PredOrFunc,
> PVars, VarSet, PragmaImpl, Status, Context,
> ModuleInfo0, ModuleInfo, Info0, Info) -->
> { module_info_name(ModuleInfo0, ModuleName) },
> @@ -4117,8 +4117,7 @@
> []
> ),
>
> - globals__io_lookup_foreign_language_option(use_foreign_language,
> - UseForeignLang),
> + globals__io_get_backend_foreign_languages(BackendForeignLangs),
>
> % Lookup the pred declaration in the predicate table.
> % (If it's not there, print an error message and insert
> @@ -4175,9 +4174,10 @@
> io__write_string(" with preceding clauses.\n"),
> { Info = Info0 }
> ;
> +
> % Don't add clauses for foreign languages other
> - % than the one we are using.
> - { UseForeignLang \= PragmaForeignLanguage }
> + % than the ones we can generate code for.
> + { not list__member(PragmaForeignLanguage, BackendForeignLangs) }
> ->
> { ModuleInfo = ModuleInfo1 },
> { Info = Info0 }
> @@ -4191,9 +4191,10 @@
> ModuleInfo1, ProcId) }
> ->
> { pred_info_clauses_info(PredInfo1, Clauses0) },
> +
> { pred_info_arg_types(PredInfo1, ArgTypes) },
> { pred_info_get_purity(PredInfo1, Purity) },
> - clauses_info_add_pragma_foreign_code(
> + clauses_info_add_pragma_foreign_proc(
> Clauses0, Purity, Attributes, PredId,
> ProcId, VarSet, PVars, ArgTypes,
> PragmaImpl, Context, PredOrFunc,
> @@ -4734,7 +4735,7 @@
> PragmaImpl), GoalInfo, _QuantVars, _VarSet, PredCallId, MI) -->
> { goal_info_get_context(GoalInfo, Context) },
> { foreign_language(Attrs, Lang) },
> - warn_singletons_in_pragma_foreign_code(PragmaImpl, Lang,
> + warn_singletons_in_pragma_foreign_proc(PragmaImpl, Lang,
> ArgInfo, Context, PredCallId, MI).
>
> warn_singletons_in_goal_2(shorthand(ShorthandGoal), GoalInfo, QuantVars,
> @@ -4829,13 +4830,13 @@
> maybe_warn_pragma_singletons(PragmaImpl, Lang, ArgInfo, Context, CallId, MI) -->
> globals__io_lookup_bool_option(warn_singleton_vars, WarnSingletonVars),
> ( { WarnSingletonVars = yes } ->
> - warn_singletons_in_pragma_foreign_code(PragmaImpl, Lang,
> + warn_singletons_in_pragma_foreign_proc(PragmaImpl, Lang,
> ArgInfo, Context, CallId, MI)
> ;
> []
> ).
>
> - % warn_singletons_in_pragma_foreign_code checks to see if each
> + % warn_singletons_in_pragma_foreign_proc checks to see if each
> % variable is mentioned at least once in the foreign code
> % fragments that ought to mention it. If not, it gives a
> % warning.
> @@ -4843,13 +4844,13 @@
> % appropriate to do this check, or you may need to add a
> % transformation to map Mercury variable names into identifiers
> % for that foreign language).
> -:- pred warn_singletons_in_pragma_foreign_code(pragma_foreign_code_impl,
> +:- pred warn_singletons_in_pragma_foreign_proc(pragma_foreign_code_impl,
> foreign_language, list(maybe(pair(string, mode))), prog_context,
> simple_call_id, module_info, io__state, io__state).
> -:- mode warn_singletons_in_pragma_foreign_code(in, in, in, in, in, in,
> +:- mode warn_singletons_in_pragma_foreign_proc(in, in, in, in, in, in,
> di, uo) is det.
>
> -warn_singletons_in_pragma_foreign_code(PragmaImpl, Lang, ArgInfo,
> +warn_singletons_in_pragma_foreign_proc(PragmaImpl, Lang, ArgInfo,
> Context, PredOrFuncCallId, ModuleInfo) -->
> { LangStr = foreign_language_string(Lang) },
> (
> @@ -5209,8 +5210,8 @@
> { Goal = Goal0 },
>
> % XXX we should avoid append - this gives O(N*N)
> - { list__append(ClauseList0, [clause(ModeIds, Goal, Context)],
> - ClauseList) },
> + { list__append(ClauseList0, [clause(ModeIds, Goal, mercury,
> + Context)], ClauseList) },
> { qual_info_get_var_types(Info, ExplicitVarTypes) },
> { ClausesInfo = clauses_info(VarSet, ExplicitVarTypes,
> TVarNameMap, InferredVarTypes, HeadVars,
> @@ -5219,12 +5220,12 @@
>
> %-----------------------------------------------------------------------------
>
> -% Add the pragma_foreign_code goal to the clauses_info for this procedure.
> +% Add the pragma_foreign_proc goal to the clauses_info for this procedure.
> % To do so, we must also insert unifications between the variables in the
> -% pragma foreign_code declaration and the head vars of the pred. Also
> +% pragma foreign_proc declaration and the head vars of the pred. Also
> % return the hlds_goal.
>
> -:- pred clauses_info_add_pragma_foreign_code(
> +:- pred clauses_info_add_pragma_foreign_proc(
> clauses_info::in, purity::in, pragma_foreign_proc_attributes::in,
> pred_id::in, proc_id::in, prog_varset::in, list(pragma_var)::in,
> list(type)::in, pragma_foreign_code_impl::in, prog_context::in,
> @@ -5232,24 +5233,102 @@
> module_info::in, module_info::out, qual_info::in,
> qual_info::out, io__state::di, io__state::uo) is det.
>
> -clauses_info_add_pragma_foreign_code(ClausesInfo0, Purity, Attributes0, PredId,
> - ModeId, PVarSet, PVars, OrigArgTypes, PragmaImpl0, Context,
> +clauses_info_add_pragma_foreign_proc(ClausesInfo0, Purity, Attributes0, PredId,
> + ProcId, PVarSet, PVars, OrigArgTypes, PragmaImpl0, Context,
> PredOrFunc, PredName, Arity, ClausesInfo, ModuleInfo0,
> ModuleInfo, Info0, Info) -->
> - globals__io_lookup_foreign_language_option(backend_foreign_language,
> - BackendForeignLanguage),
> - {
> - ClausesInfo0 = clauses_info(VarSet0, VarTypes, TVarNameMap, VarTypes1,
> - HeadVars, ClauseList, TI_VarMap, TCI_VarMap),
> +
> + { ClausesInfo0 = clauses_info(VarSet0, VarTypes, TVarNameMap,
> + VarTypes1, HeadVars, ClauseList, TI_VarMap, TCI_VarMap) },
> +
> +
> + % Find all the exising clauses for this mode, and
> + % extract their implementation language and clause number
> + % (that is, their index in the list).
> + { foreign_language(Attributes0, NewLang) },
> + { list__foldl2(
> + (pred(C::in, Res0::in, Res::out, N0::in, N::out) is det :-
> + (
> + C = clause(ProcIds, _, ClauseLang, _),
> + list__member(ProcId, ProcIds)
> + ->
> + Res = [ClauseLang - N0 | Res0],
> + N = N0 + 1
> + ;
> + Res = Res0,
> + N = N0 + 1
> + )
> + ), ClauseList, [], LangClauses, 0, _) },
> +
> + globals__io_get_globals(Globals),
> + globals__io_get_target(Target),
> +
> + % Figure out what to do with this new clause.
> + % We can either add it to the list of clauses, ignore it,
> + % or replace the existing clause with it.
> + %
> + % We create a closure called UpdateClauses which does the
> + % appropriate action at the end of this predicate.
> + %
> + % In the rare case of multiple foreign language
> + % implementations we might do some unnecessary work only
> + % to ignore the new clause.
> + {
> + % no clauses -- add it
> + LangClauses = [],
> + UpdateClauses = (pred(NewCl::in, Cs::out) is det :-
> + Cs = [NewCl|ClauseList])
> + ;
> + % was implemented in Mercury, do nothing
> + % XXX if we want to make Mercury implementations a fallback
> + % we should consider making this a replace instead of an
> + % ignore.
I don't think this XXX is correct. You already have a suitable
foreign_proc to use, so you would never want to use the mercury code.
What you want to do is ignore attempts to add mercury clauses when you
already have a foreign_code implementation.
> + LangClauses = [mercury - _ | Rest],
> + ( Rest = [] ->
> + UpdateClauses = (pred(_NewCl::in, Cs::out) is det :-
> + Cs = ClauseList)
> + ;
> + error("unexpected: multiple matches for foreign " ++
> + "language clauses")
> + )
> +
> + ;
> + LangClauses = [ForeignLang - ClauseNumber | Rest],
> + ForeignLang = foreign_language(OldLang),
> + ( Rest = [] ->
> + Compare = foreign__compare_foreign_language(Globals,
> + Target, OldLang, NewLang),
> + (
> + % This language is preferred to the old
> + % language, so we should replace it
> + Compare = (<) ->
> + UpdateClauses =
> + (pred(NewCl::in, Cs::out) is det :-
> + list__replace_nth_det(ClauseList,
> + ClauseNumber, NewCl, Cs))
> + ;
> + % Just ignore it.
> + UpdateClauses =
> + (pred(_NewCl::in, Cs::out) is det :-
> + Cs = ClauseList)
> + )
> + ;
> + error("unexpected: multiple matches for foreign " ++
> + "language clauses")
> + )
> + },
> +
> + globals__io_get_backend_foreign_languages(BackendForeignLanguages),
> + {
> pragma_get_vars(PVars, Args0),
> pragma_get_var_infos(PVars, ArgInfo),
>
> %
> - % If the foreign language is different to the backend
> - % language, we will have to generate an interface to it in the
> + % If the foreign language not one of the backend
> + % languages, we will have to generate an interface to it in a
> % backend language.
> %
> - foreign__extrude_pragma_implementation(BackendForeignLanguage,
> + foreign__extrude_pragma_implementation(BackendForeignLanguages,
> PVars, PredName, PredOrFunc, Context,
> ModuleInfo0, Attributes0, PragmaImpl0,
> ModuleInfo1, Attributes, PragmaImpl),
> @@ -5274,7 +5353,7 @@
> { Info = Info0 },
> prog_out__write_context(Context),
> io__write_string(
> - "In `:- pragma foreign_code' declaration for "),
> + "In `:- pragma foreign_proc' declaration for "),
> { adjust_func_arity(PredOrFunc, OrigArity, Arity) },
> hlds_out__write_simple_call_id(
> PredOrFunc - PredName/OrigArity),
> @@ -5310,7 +5389,7 @@
> % this foreign code is inlined
> add_goal_info_purity_feature(GoalInfo1, Purity, GoalInfo),
> HldsGoal0 = foreign_proc(Attributes, PredId,
> - ModeId, Args, ArgInfo, OrigArgTypes, PragmaImpl)
> + ProcId, Args, ArgInfo, OrigArgTypes, PragmaImpl)
> - GoalInfo
> },
> % Apply unifications with the head args.
> @@ -5327,9 +5406,11 @@
> implicitly_quantify_clause_body(HeadVars,
> HldsGoal1, VarSet2, EmptyVarTypes,
> HldsGoal, VarSet, _, _Warnings),
> - NewClause = clause([ModeId], HldsGoal, Context),
> + NewClause = clause([ProcId], HldsGoal,
> + foreign_language(NewLang), Context),
> + UpdateClauses(NewClause, NewClauseList),
> ClausesInfo = clauses_info(VarSet, VarTypes, TVarNameMap,
> - VarTypes1, HeadVars, [NewClause|ClauseList],
> + VarTypes1, HeadVars, NewClauseList,
> TI_VarMap, TCI_VarMap)
> }
> ).
> @@ -8118,7 +8199,7 @@
> { default_attributes(c, Attrs0) },
> { set_may_call_mercury(Attrs0, will_not_call_mercury, Attrs1) },
> { set_thread_safe(Attrs1, thread_safe, Attrs) },
> - module_add_pragma_foreign_code(Attrs, SymName, PredOrFunc,
> + module_add_pragma_foreign_proc(Attrs, SymName, PredOrFunc,
> PragmaVars, VarSet, ordinary(C_ProcCode, no),
> Status, Context, Module0, Module1, Info0, Info),
> {
> Index: compiler/mlds_to_csharp.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/mlds_to_csharp.m,v
> retrieving revision 1.10
> diff -u -r1.10 mlds_to_csharp.m
> --- compiler/mlds_to_csharp.m 2001/07/12 15:44:55 1.10
> +++ compiler/mlds_to_csharp.m 2001/07/17 16:51:35
> @@ -473,8 +475,14 @@
> { sorry(this_file, "value classes") }.
> write_il_simple_type_as_csharp_type(interface(_ClassName)) -->
> { sorry(this_file, "interfaces") }.
> -write_il_simple_type_as_csharp_type('[]'(_Type, _Bounds)) -->
> - { sorry(this_file, "arrays") }.
> +write_il_simple_type_as_csharp_type('[]'(Type, Bounds)) -->
> + write_il_type_as_csharp_type(Type),
> + io__write_string("[]"),
> + ( { Bounds = [] } ->
> + []
> + ;
> + { sorry(this_file, "arrays with bounds") }
> + ).
> write_il_simple_type_as_csharp_type('&'(Type)) -->
> % XXX is this always right?
> io__write_string("ref "),
This change is not mentioned in the log file.
Otherwise this diff looks fine.
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list