for review: Aditi [5]

Simon Taylor stayl at cs.mu.OZ.AU
Tue Jul 7 13:44:09 AEST 1998


%-----------------------------------------------------------------------------%
% Copyright (C) 1998 University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: magic.m
% Main author: stayl
%
% Supplementary magic sets transformation, sort of as described in 
% 	Beeri, C. and Ramakrishnan, R.,
% 	On the power of magic,
% 	Journal of Logic Programming,
%	volume 10, 1991, pp. 255-299.
%
%-----------------------------------------------------------------------------%
% Short example:
%
%:- module a.
%
%:- interface.
%
%:- import_module aditi.
%
% :- pred call_anc(aditi__state::in, int::out) is nondet.
%:- pragma aditi(call_anc/3).
%
%:- implementation.
%
%:- pred anc(aditi__state::in, int::in, int::out) is nondet.
%:- pragma aditi(anc/3).
%
%:- pred p(aditi__state::in, int::out, int::out) is nondet.
%:- pragma base_relation(p/3).
%
%anc(DB, X, Y) :- 
%	p(DB, X, Y).
%anc(DB, X, Y) :-
%	p(DB, X, Z),
%	anc(DB, Z, Y).
%
%-----------------------------------------------------------------------------%
% Transformed version:
%
% % The original predicate is converted into a goal which
% % calls do_call_aditi_nondet to do all the work.
% % The type_infos are used for data conversion.
% % The base relation p/3 is given an interface procedure
% % which looks basically the same as this one, except that
% % there are no inputs and two outputs.
% % This procedure is compiled to C, not Aditi-RL.
% anc(HeadVar__1, HeadVar__2, HeadVar__3) :-
%       V_15 = "stayl/a/a__anc__c_interface_2_0/2",	% RL proc name
%       V_16 = 1,					% number of inputs
%       V_17 = "(:I)",					% input relation schema
%       V_18 = 1,					% number of outputs
%       TypeInfo_13 = base_type_info("", "int", 0),	% input type_info
%       TypeInfo_14 = base_type_info("", "int", 0),	% output type_info
%
%	% anc__do_aditi_call has the `aditi_interface' marker,
%	% which causes call_gen.m to generate it as a call
% 	% to `do_nondet_aditi_call', which is hand-coded C
% 	% in extras/aditi/aditi.m
%       anc__do_aditi_call(V_15, V_16, V_17, V_18,
%		 TypeInfo_13, HeadVar__2,
% 		 TypeInfo_14, HeadVar__3).
%
% :- pred anc__c_interface(pred(int)::(pred(out) is nondet),
% 		 int::out) is nondet.
% :- pragma aditi(anc__c_interface/2).
%
% % This predicate calls the Aditi version of anc, joins the result
% % with the input to the calls and then projects the join result onto
% % the output arguments.
% anc__c_interface(InAnc, Y) :-
%	anc__c_interface__supp1(InAnc, X),
% 	V_15 = anc__c_interface__supp1(InAnc),
% 	anc__aditi0(InAnc, V_1, Y),
% 	X == V_1.
%
% 	% The aditi__state arguments are removed and all modes are
%	% converted to output. An input closure is added for each
%	% predicate in the SCC.
% :- pred anc__aditi0(pred(int)::(pred(out) is nondet),
% 		int::out, int::out) is nondet.
% :- pragma aditi(anc__aditi0/3).
%
% anc__aditi0(InAnc, X, Y) :- 
%	anc__magic(InAnc, X),
%	p(V_1, Y),
%	V1 == X.
% anc__aditi0(InAnc, X, Y) :-
%	anc__supp1(InAnc, X),
%	anc__aditi0(InAnc, V1, Y),
%	V1 == X.
%
%	% `anc__magic' collects all tuples which could be input to
%	% a call to `anc' in a top-down execution.
% :- pred anc__magic(pred(int)::(pred(out) is nondet), int::out) is nondet.
% :- pragma aditi(anc__magic/2).
%
% anc__magic(InAnc, X) :-
%	% Collect the input from a higher sub-module.
%	call(InAnc, X).
% anc__magic(InAnc, Z) :-
%	% Collect the input from recursive calls.
% 	anc__supp1(InAnc, _, Z).
%
%	% `anc__supp1' is introduced to do common sub-expression - 
%	% this join would otherwise be done in both `anc__aditi0'
%	% and `anc__magic'. This is also necessary because rl_gen.m
%	% only handles rules with at most two database calls.
% :- pred anc__supp1(pred(int)::(pred(out) is nondet, int::out) is nondet.
% :- pragma aditi(anc__supp1/2).
%
% anc__supp1(InAnc, Z) :-
% 	anc__magic(InAnc, X),
%	p(V_1, Z),
% 	X == V1.
%
%-----------------------------------------------------------------------------%
%
%
% context.m is called to handle predicates with a `:- pragma context'
% declaration.
%
% Input relations are explicitly passed using closures.
%
% While it processes the module it checks that there are no higher-order,
% partially instantiated, polymorphic or abstract arguments since Aditi
% cannot handle these.
% 
% Any closures occurring in Aditi procedures must not have curried arguments.
% Closures may only be used for aggregates.
%
% This should attempt to reorder within rules so that no supplementary
% predicates are created with partially instantiated arguments, since Aditi
% can only handle ground terms in relations. The problem occurs if there are
% partially instantiated terms live across a database predicate call. At the
% moment an error is reported.
% 
%-----------------------------------------------------------------------------%
:- module magic.

:- interface.

:- import_module hlds_module.
:- import_module io.

:- pred magic__process_module(module_info, module_info, io__state, io__state).
:- mode magic__process_module(in, out, di, uo) is det.

%-----------------------------------------------------------------------------%
:- implementation.

:- import_module magic_util, context.
:- import_module dependency_graph, hlds_pred, hlds_goal, hlds_data, prog_data.
:- import_module passes_aux, mode_util, (inst), instmap, rl_gen, rl, rl_out.
:- import_module globals, options, hlds_out, prog_out, goal_util, type_util.
:- import_module polymorphism.

:- import_module int, list, map, require, set, std_util, string, term, varset.
:- import_module assoc_list, bool, simplify.

magic__process_module(ModuleInfo0, ModuleInfo) -->
	{ module_info_ensure_aditi_dependency_info(ModuleInfo0, ModuleInfo1) },
	{ module_info_aditi_dependency_ordering(ModuleInfo1, Ordering) },
	{ magic_info_init(ModuleInfo1, Info0) },
	{ module_info_predids(ModuleInfo1, PredIds) },
	{ magic__process_imported_procs(PredIds, Info0, Info1) },
	globals__io_lookup_bool_option(very_verbose, Verbose),

		% Add magic procedures, do some transformation on the goals.
	maybe_write_string(Verbose, "% preprocessing module\n"),
	maybe_flush_output(Verbose),
	{ list__foldl(magic__preprocess_sub_module, Ordering, Info1, Info2) },

		% Do the transformation.
	maybe_write_string(Verbose, "% processing module\n"),
	maybe_flush_output(Verbose),
	list__foldl2(magic__process_sub_module, Ordering, Info2, Info3),

	{ magic_info_get_module_info(ModuleInfo2, Info3, Info) },
	{ magic_info_get_errors(Errors, Info, _) },
	{ set__to_sorted_list(Errors, ErrorList) },
	( { ErrorList = [] } ->
		{ ModuleInfo3 = ModuleInfo2 }
	;
		globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
		magic_util__report_errors(ErrorList,
			ModuleInfo2, VerboseErrors),
		{ module_info_incr_errors(ModuleInfo2, ModuleInfo3) },
		io__set_exit_status(1)
	),

		% New procedures were created, so the dependency_info
		% is out of date.
	{ module_info_clobber_dependency_info(ModuleInfo3, ModuleInfo) }.

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

	% Convert imported Aditi procedures for the magic sets interface.
:- pred magic__process_imported_procs(list(pred_id)::in,
		magic_info::in, magic_info::out) is det.

magic__process_imported_procs([]) --> [].
magic__process_imported_procs([PredId | PredIds]) -->
	magic_info_get_module_info(ModuleInfo),
	{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
	(
		{ pred_info_is_imported(PredInfo) },
		{ hlds_pred__is_derived_relation(ModuleInfo, PredId) }
	->
		{ pred_info_procids(PredInfo, ProcIds) },
		magic__process_imported_procs_2(PredId, ProcIds)
	;
		{ hlds_pred__pred_info_is_base_relation(PredInfo) }
	->
		{ pred_info_procids(PredInfo, ProcIds) },
		list__foldl(magic__process_base_relation(PredId), ProcIds)
	;
		[]
	),
	magic__process_imported_procs(PredIds).

:- pred magic__process_imported_procs_2(pred_id::in, list(proc_id)::in,
		magic_info::in, magic_info::out) is det.

magic__process_imported_procs_2(_, []) --> [].
magic__process_imported_procs_2(PredId, [ProcId | ProcIds]) -->
	{ PredProcId = proc(PredId, ProcId) },
	magic__get_sub_module_inputs([PredProcId], InputTypes, InputModes),
	magic__adjust_pred_info([PredProcId], InputTypes, 
		InputModes, PredProcId),
	magic__process_imported_procs_2(PredId, ProcIds).

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

	% Create a version without the aditi__states, and with
	% all modes output.
:- pred magic__process_base_relation(pred_id::in, proc_id::in,
		magic_info::in, magic_info::out) is det.

magic__process_base_relation(PredId0, ProcId0) -->
	magic__separate_proc(PredId0, ProcId0),
	magic_info_get_pred_map(PredMap),
	{ map__search(PredMap, proc(PredId0, ProcId0), PredProcId1) ->
		PredProcId = PredProcId1
	;
		PredProcId = proc(PredId0, ProcId0)
	},
	magic__do_process_base_relation(proc(PredId0, ProcId0), PredProcId).
		
:- pred magic__do_process_base_relation(pred_proc_id::in, pred_proc_id::in,
		magic_info::in, magic_info::out) is det.

magic__do_process_base_relation(CPredProcId, PredProcId) -->
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
		PredInfo0, ProcInfo0) },

	% Remove aditi:states, convert arguments to output.
	{ pred_info_arg_types(PredInfo0, TVarSet, ArgTypes0) },
	{ proc_info_argmodes(ProcInfo0, ArgModes0) },
	{ proc_info_headvars(ProcInfo0, HeadVars0) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes1) },
	{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
		ArgModes1, ArgModes) },
	{ magic_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars) },
	{ pred_info_set_arg_types(PredInfo0, TVarSet, ArgTypes, PredInfo) },
	{ proc_info_set_argmodes(ProcInfo0, ArgModes, ProcInfo1) },
	{ proc_info_set_headvars(ProcInfo1, HeadVars, ProcInfo) },
	{ module_info_set_pred_proc_info(ModuleInfo0,
		PredProcId, PredInfo, ProcInfo, ModuleInfo) },
	magic_info_set_module_info(ModuleInfo),
	magic__interface_from_c([CPredProcId], CPredProcId, PredProcId).

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

	% Go over each sub-module adding in the input arguments for each 
	% procedure, allocating the magic predicates, filling in the 
	% magic_map, pred_map and magic_proc_info fields of the magic_info.
:- pred magic__preprocess_sub_module(aditi_sub_module::in,
		magic_info::in, magic_info::out) is det.

magic__preprocess_sub_module(aditi_sub_module(SubModule0, EntryPoints)) --> 
	list__foldl(magic__check_scc, SubModule0),
	{ list__condense(SubModule0, SubModule) },
	magic__get_sub_module_inputs(EntryPoints, InputTypes, InputModes),
	list__foldl(magic__adjust_pred_info(EntryPoints,
		InputTypes, InputModes), SubModule).

:- pred magic__check_scc(list(pred_proc_id)::in,
		magic_info::in, magic_info::out) is det.

magic__check_scc(SCC) -->
	magic_info_get_errors(Errors0),
	magic_info_get_module_info(ModuleInfo),
	{ SCC = [_] ->
		Errors1 = Errors0
	;
		% Add errors for context procedures which are mutually
		% recursive with other procedures.
		solutions(
			lambda([ProcAndContext::out] is nondet, (
				list__member(ContextProc, SCC),
				ContextProc = proc(ContextPredId, _),
				module_info_pred_info(ModuleInfo,
					ContextPredId, ContextPredInfo),
				pred_info_get_markers(ContextPredInfo,
					ContextMarkers),
				check_marker(ContextMarkers, context),
				pred_info_context(ContextPredInfo,
					Context),
				ProcAndContext = ContextProc - Context
			)), ContextProcs),
		list__map(
			lambda([BadContextProc::in, Error::out] is det, (
				BadContextProc = TheContextProc - TheContext,
				Error = mutually_recursive_context(
					TheContextProc, SCC) - TheContext
			)), ContextProcs, ContextErrors),
		set__insert_list(Errors0, ContextErrors, Errors1)
	},
	{
		% Add errors if a procedure compiled to C is mutually
		% recursive with an Aditi procedure.
		list__member(proc(PredId, _), SCC),
		module_info_pred_info(ModuleInfo, PredId, PredInfo),
		pred_info_get_markers(PredInfo, Markers),
		\+ check_marker(Markers, aditi)
	->
		term__context_init(InitContext),
		set__insert(Errors1, mixed_scc(SCC) - InitContext, Errors)
	;		
		Errors = Errors1
	},
	magic_info_set_errors(Errors).

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

	% Work out the types and modes of the input relations that need
	% to be passed around the sub-module.
:- pred magic__get_sub_module_inputs(list(pred_proc_id)::in, list(type)::out,
		list(mode)::out, magic_info::in, magic_info::out) is det.

magic__get_sub_module_inputs([], [], []) --> [].
magic__get_sub_module_inputs([PredProcId | PredProcIds],
		[Type | Types], [Mode | Modes]) -->
	magic_info_get_module_info(ModuleInfo),
	{ module_info_pred_proc_info(ModuleInfo, PredProcId,
		PredInfo, ProcInfo) },
	{ proc_info_argmodes(ProcInfo, ArgModes0) },
	{ pred_info_arg_types(PredInfo, _, ArgTypes0) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes) },
	{ partition_args(ModuleInfo, ArgModes, ArgModes, InputModes, _) },
	{ partition_args(ModuleInfo, ArgModes, ArgTypes, InputTypes, _) },
	{ term__context_init(Context) },
	{ Type = term__functor(term__atom("pred"), InputTypes, Context) },
	{ GetOutputMode = lambda([ArgMode::in, OutputMode::out] is det, (
			mode_get_insts(ModuleInfo, ArgMode, _, OutputInst),
			OutputMode = (free -> OutputInst)
		)) },
	{ list__map(GetOutputMode, InputModes, InputRelModes) },
 	{ Inst = ground(unique, yes(pred_inst_info(predicate, 
				InputRelModes, nondet))) },
	{ Mode = (Inst -> Inst) },
	magic__get_sub_module_inputs(PredProcIds, Types, Modes).

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

:- pred magic__adjust_pred_info(list(pred_proc_id)::in, list(type)::in,
		list(mode)::in, pred_proc_id::in,
		magic_info::in, magic_info::out) is det.

magic__adjust_pred_info(EntryPoints, MagicTypes, 
		MagicModes, PredProcId0) -->
	{ PredProcId0 = proc(PredId0, ProcId0) },
	magic__separate_proc(PredId0, ProcId0),
	magic_info_get_pred_map(PredMap1),
	{ map__lookup(PredMap1, PredProcId0, PredProcId) },
	magic__adjust_proc_info(EntryPoints, PredProcId0, PredProcId, 
		MagicTypes, MagicModes).

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

	% Separate out the procedures for each predicate so that each
	% pred_info for a derived database predicate contains only one
	% proc_info. This is necessary because the different procedures
	% have different numbers of input arguments and are members of
	% different sub-modules, so the transformed procedures will have 
	% different numbers and types of input relation arguments. We also need
	% to leave the original declarations so that predicates compiled
	% to C can call the procedure.
:- pred magic__separate_proc(pred_id::in, proc_id::in,
		magic_info::in, magic_info::out) is det.

magic__separate_proc(PredId, ProcId) -->
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_info(ModuleInfo0, PredId, PredInfo0) },
	magic_info_set_curr_pred_proc_id(proc(PredId, ProcId)),

	{ pred_info_procedures(PredInfo0, Procs0) },
	{ map__lookup(Procs0, ProcId, ProcInfo0) },

	% 
	% Create a new pred_info for the procedure.
	%

	% Produce a unique name for the procedure.
	{ pred_info_module(PredInfo0, Module) },
	{ pred_info_name(PredInfo0, Name) },
	{ pred_info_get_markers(PredInfo0, Markers) },
	{ proc_id_to_int(ProcId, ProcInt) },

	( { check_marker(Markers, base_relation) } ->
		{ NewName = Name }
	;
		{ string__format("%s__aditi%i",
			[s(Name), i(ProcInt)], NewName) }
	),

	{ pred_info_arg_types(PredInfo0, TVarSet, ArgTypes) },
	{ pred_info_context(PredInfo0, Context) },
	{ pred_info_import_status(PredInfo0, Status) },
	{ pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc) },
	{ pred_info_get_aditi_owner(PredInfo0, Owner) },
		% type classes aren't supported in Aditi.
	{ ClassConstraints = [] },
	{ pred_info_create(Module, qualified(Module, NewName), 
		TVarSet, ArgTypes, true, Context, Status, Markers, 
		PredOrFunc, ClassConstraints, Owner, ProcInfo0, 
		NewProcId, NewPredInfo) },

	{ module_info_get_predicate_table(ModuleInfo0, PredTable0) },
	{ predicate_table_insert(PredTable0, NewPredInfo, NewPredId, 
		PredTable) },
	{ module_info_set_predicate_table(ModuleInfo0, PredTable,
		ModuleInfo) },
	magic_info_set_module_info(ModuleInfo),

	%
	% Later we need to convert all calls to the old 
	% procedure to calls to the new.
	%
	magic_info_get_pred_map(PredMap0),
	{ map__det_insert(PredMap0, proc(PredId, ProcId),
		proc(NewPredId, NewProcId), PredMap) },
	magic_info_set_pred_map(PredMap).

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

	% Preprocess the procedure
	% - preprocess the goal
	% - convert input arguments to output
	% - add input closure arguments
:- pred magic__adjust_proc_info(list(pred_proc_id)::in, pred_proc_id::in,
		pred_proc_id::in, list(type)::in, list(mode)::in,
		magic_info::in, magic_info::out) is det.

magic__adjust_proc_info(EntryPoints, CPredProcId, AditiPredProcId, 
		MagicTypes, MagicModes) -->
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_proc_info(ModuleInfo0, AditiPredProcId,
		PredInfo0, ProcInfo0) },

	magic__preprocess_proc(CPredProcId, PredInfo0,
		ProcInfo0, ProcInfo1),

	%
	% Find which of the arguments of the SCC carries the
	% input for the current procedure.
	%
	{ list__nth_member_search(EntryPoints, CPredProcId, N) ->
		Index = yes(N),
		( EntryPoints \= [_], pred_info_is_exported(PredInfo0) ->
			InterfaceRequired = yes(N)
		;
			InterfaceRequired = no
		)
	;
		Index = no,
		InterfaceRequired = no
	},

	magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
		Index, MagicTypes, MagicModes, PredInfo0, ProcInfo1,
		InputArgTypes, InputArgModes),

	( { pred_info_is_imported(PredInfo0) } ->
		[]
	;
			% Create a new procedure to collect the input
			% for the current procedure.
		magic__create_magic_pred(CPredProcId, AditiPredProcId,
			MagicTypes, MagicModes, InputArgTypes, InputArgModes, 
			Index)
	),

	%
	% Replace the goal for the C procedure with a goal to
	% interface with the Aditi procedure from C, unless --aditi-only
	% was specified or the procedure is imported.
	% 	
	magic__interface_from_c(EntryPoints, CPredProcId, AditiPredProcId).

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

	% Given a pred_info and a proc_info return two versions of each, one
	% with just the `aditi:state' removed, and one with the `aditi:state'
	% removed and the magic input arguments for the sub-module added.
:- pred magic__adjust_args(pred_proc_id::in, pred_proc_id::in, maybe(int)::in,
		maybe(int)::in, list(type)::in, list(mode)::in,
		pred_info::in, proc_info::in, list(type)::out, list(mode)::out,
		magic_info::in, magic_info::out) is det.

magic__adjust_args(CPredProcId, AditiPredProcId, InterfaceRequired,
		MaybeIndex, MagicTypes, MagicModes, PredInfo0, ProcInfo0, 
		InputArgTypes, InputArgModes) -->

	%
	% Check that the argument types and modes 
	% are legal for Aditi procedures.
	%
	{ pred_info_arg_types(PredInfo0, TVarSet, ArgTypes0) },
	{ proc_info_headvars(ProcInfo0, HeadVars0) },
	{ pred_info_context(PredInfo0, Context) },
	{ proc_info_argmodes(ProcInfo0, ArgModes0) },
	magic_util__check_args(HeadVars0, ArgModes0, ArgTypes0, Context,
		arg_number),

	%
	% Strip out the aditi__state argument.
	%
	{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes1) },
	{ magic_util__remove_aditi_state(ArgTypes0, HeadVars0, HeadVars1) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes1) },

	%
	% Convert all of the original modes to output. The input
	% will be carried in with the input closures.
	%
	magic_info_get_module_info(ModuleInfo0),
	{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
		ArgModes1, ArgModes2) },

	% Create variables for the magic input.
	{ proc_info_create_vars_from_types(ProcInfo0, MagicTypes,
		MagicVars, ProcInfo1) },

	%
	% Add the input relation variables to the arguments.
	%
	{ list__append(MagicVars, HeadVars1, HeadVars) },
	{ list__append(MagicModes, ArgModes2, ArgModes) },
	{ list__append(MagicTypes, ArgTypes1, ArgTypes) },

	%
	% Ensure that the exported interface procedure gets the 
	% correct argmodes.
	%
	{ instmap_delta_from_mode_list(HeadVars, ArgModes, 
		ModuleInfo0, InstMapDelta) },
	{ proc_info_goal(ProcInfo1, Goal0 - GoalInfo0) },
	{ goal_info_set_instmap_delta(GoalInfo0, InstMapDelta, GoalInfo) },
	{ proc_info_set_goal(ProcInfo1, Goal0 - GoalInfo, ProcInfo2) },

	% All Aditi procedures are considered nondet. The C interface
	% procedures retain the old determinism, and abort if the number
	% of answers returned doesn't match the determinism.
	{ proc_info_set_inferred_determinism(ProcInfo2, nondet, ProcInfo3) },

	{ partition_args(ModuleInfo0, ArgModes1,
		ArgModes1, InputArgModes, _) },
	{ partition_args(ModuleInfo0, ArgModes1,
		ArgTypes1, InputArgTypes, _) },

	{ ThisProcInfo = magic_proc_info(ArgModes1, MagicVars, 
		MagicTypes, MagicModes, MaybeIndex) },
	magic_info_get_magic_proc_info(MagicProcInfo0),
	{ map__det_insert(MagicProcInfo0, AditiPredProcId,
		ThisProcInfo, MagicProcInfo) },
	magic_info_set_magic_proc_info(MagicProcInfo),

	{ pred_info_set_arg_types(PredInfo0, TVarSet, ArgTypes, PredInfo) },
	
	{ proc_info_set_headvars(ProcInfo3, HeadVars, ProcInfo4) },
	{ proc_info_set_argmodes(ProcInfo4, ArgModes, ProcInfo) },

	( { InterfaceRequired = yes(Index) } ->
		magic__create_interface_proc(Index, CPredProcId,
			AditiPredProcId, PredInfo0, ProcInfo3, ProcInfo,
			HeadVars1, ArgTypes1, ArgModes1,
			MagicVars, MagicTypes, MagicModes)
	;
		magic_info_get_module_info(ModuleInfo5),
		{ module_info_set_pred_proc_info(ModuleInfo5, AditiPredProcId,
			PredInfo, ProcInfo, ModuleInfo) },
		magic_info_set_module_info(ModuleInfo)
	).

	%
	% Create an interface procedure to a sub-module for a particular
	% entry-point, used by Mercury compiled to C and Aditi procedures
	% in other modules.
	% A local version is created which takes all the input
	% arguments. The exported version calls this version
	% with empty relations for all except one of the
	% input arguments.
	%
:- pred magic__create_interface_proc(int::in, pred_proc_id::in,
		pred_proc_id::in, pred_info::in, proc_info::in, proc_info::in,
		list(var)::in, list(type)::in, list(mode)::in,
		list(var)::in, list(type)::in, list(mode)::in,
		magic_info::in, magic_info::out) is det.
	
magic__create_interface_proc(Index, CPredProcId, AditiPredProcId,
		ExportedPredInfo0, ExportedProcInfo0, LocalProcInfo,
		HeadVars1, ArgTypes1, ArgModes1, MagicVars,
		MagicTypes, MagicModes) -->

	%
	% Create the local version.
	%
	{ proc_info_goal(LocalProcInfo, Goal) },
	magic_info_get_module_info(ModuleInfo1),
	{ proc_info_get_initial_instmap(LocalProcInfo, ModuleInfo1, InstMap) },
	{ pred_info_name(ExportedPredInfo0, PredName0) },
	{ string__append(PredName0, "__local", PredName) },
	{ proc_info_headvars(LocalProcInfo, HeadVars) },
	{ proc_info_vartypes(LocalProcInfo, VarTypes) },
	{ proc_info_varset(LocalProcInfo, VarSet) },
	{ pred_info_get_markers(ExportedPredInfo0, Markers) },
	{ pred_info_get_aditi_owner(ExportedPredInfo0, Owner) },

	{ ClassContext = [] },
	{ map__init(TVarMap) },
	{ map__init(TCVarMap) },
	{ varset__init(TVarSet) },
	{ hlds_pred__define_new_pred(Goal, CallGoal, HeadVars, InstMap,
		PredName, TVarSet, VarTypes, ClassContext, TVarMap,
		TCVarMap, VarSet, Markers, Owner,
		ModuleInfo1, ModuleInfo2, LocalPredProcId) },
	magic_info_set_module_info(ModuleInfo2),

	% Calls in this module should be redirected to point to
	% the local version.
	magic_info_get_pred_map(PredMap0),
	{ map__det_update(PredMap0, CPredProcId, LocalPredProcId, PredMap) },
	magic_info_set_pred_map(PredMap),

	%
	% Add the single magic input relation to the argument list of
	% the exported version.
	%
	{ list__index1_det(MagicVars, Index, MagicInputVar) },
	{ list__index1_det(MagicTypes, Index, MagicInputType) },
	{ list__index1_det(MagicModes, Index, MagicInputMode) },
	{ ExportedArgModes = [MagicInputMode | ArgModes1] },
	{ ExportedArgTypes = [MagicInputType | ArgTypes1] },
	{ ExportedHeadVars = [MagicInputVar | HeadVars1] },
	{ proc_info_set_headvars(ExportedProcInfo0,
		ExportedHeadVars, ExportedProcInfo1) },
	{ proc_info_set_argmodes(ExportedProcInfo1,
		ExportedArgModes, ExportedProcInfo2) },
	{ pred_info_set_arg_types(ExportedPredInfo0, TVarSet, ExportedArgTypes, 
		ExportedPredInfo1) },

	%
	% Construct the input for the call to the local version.
	%
	magic_info_set_pred_info(ExportedPredInfo1),
	magic_info_set_proc_info(ExportedProcInfo2),
	magic__interface_call_args(MagicVars, MagicTypes, MagicModes,	
		Index, 1, InputGoals),
	magic_info_get_pred_info(ExportedPredInfo2),
	magic_info_get_proc_info(ExportedProcInfo3),
	{ CallGoal = _ - CallGoalInfo },
	{ list__append(InputGoals, [CallGoal], ExportedConj) },
	{ conj_list_to_goal(ExportedConj, CallGoalInfo, ExportedGoal) },
	{ proc_info_set_goal(ExportedProcInfo3,
		ExportedGoal, ExportedProcInfo) },

	{ pred_info_set_import_status(ExportedPredInfo2, exported,
		ExportedPredInfo) },
	magic_info_get_module_info(ModuleInfo5),
	{ module_info_set_pred_proc_info(ModuleInfo5, AditiPredProcId,
		ExportedPredInfo, ExportedProcInfo, ModuleInfo6) },
	magic_info_set_module_info(ModuleInfo6).

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

:- pred magic__interface_call_args(list(var)::in, list(type)::in, 
	list(mode)::in, int::in, int::in, list(hlds_goal)::out,
	magic_info::in, magic_info::out) is det.

magic__interface_call_args([], _, _, _, _, []) --> [].
magic__interface_call_args([MagicInput | MagicInputs], MagicTypes, MagicModes,	
		CalledPredIndex, CurrVar, InputGoals) -->
	{ NextVar is CurrVar + 1 },
	magic__interface_call_args(MagicInputs, MagicTypes, MagicModes,
		CalledPredIndex, NextVar, InputGoals1),
	( { CurrVar = CalledPredIndex } ->
		%
		% Just pass through the closure passed in 
		% from the calling module.
		%
		{ InputGoals = InputGoals1 }
	;
		%
		% Create an empty input closure.
		%
		{ list__index1_det(MagicTypes, CurrVar, MagicType) },
		{ type_is_higher_order(MagicType, predicate, ArgTypes1) ->
			ArgTypes = ArgTypes1
		;
			error("magic__interface_call_args")
		},
		magic_info_get_proc_info(ProcInfo0),
		{ proc_info_create_vars_from_types(ProcInfo0, ArgTypes, 
			Args, ProcInfo) },
		magic_info_set_proc_info(ProcInfo),
		{ fail_goal(LambdaGoal) },
		{ list__index1_det(MagicModes, CurrVar, InputMode) },
		magic_util__create_closure(CurrVar, MagicInput, InputMode,
			LambdaGoal, [], Args, InputGoal),
		{ InputGoals = [InputGoal | InputGoals1] }
	).	

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

:- pred magic__interface_from_c(list(pred_proc_id)::in, pred_proc_id::in,
		pred_proc_id::in, magic_info::in, magic_info::out) is det.

magic__interface_from_c(EntryPoints, CPredProcId, AditiPredProcId) -->
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_globals(ModuleInfo0, Globals) },
	{ globals__lookup_bool_option(Globals, aditi_only, AditiOnly) },
	{ module_info_pred_proc_info(ModuleInfo0,
		CPredProcId, PredInfo0, ProcInfo0) },

	{ pred_info_get_markers(PredInfo0, Markers) },
	{ module_info_name(ModuleInfo0, ModuleName) },
	(
		{ check_marker(Markers, base_relation) },
		{ pred_info_module(PredInfo0, ModuleName) }
	->
		{ pred_info_set_import_status(PredInfo0,
			exported, PredInfo1) },
		{ module_info_set_pred_proc_info(ModuleInfo0, CPredProcId,
			PredInfo1, ProcInfo0, ModuleInfo1) },
		magic_info_set_module_info(ModuleInfo1)
	;
		{ PredInfo1 = PredInfo0 },
		{ ModuleInfo1 = ModuleInfo0 }
	),
	magic_info_get_errors(Errors),
	( { pred_info_is_imported(PredInfo1) } ->
		[]
	; { \+ set__empty(Errors) } ->
		[]
	; { AditiOnly = yes ; \+ list__member(CPredProcId, EntryPoints) } ->
		%
		% If no interface procedure is required we just throw
		% away the goal and set the import_status to imported.
		%
		{ true_goal(Goal) },
		{ proc_info_set_goal(ProcInfo0, Goal, ProcInfo) },
		{ pred_info_set_import_status(PredInfo1, imported, PredInfo) },
		{ module_info_set_pred_proc_info(ModuleInfo1, CPredProcId,
			PredInfo, ProcInfo, ModuleInfo) },
		magic_info_set_module_info(ModuleInfo)
	;
		magic__create_input_join_proc(CPredProcId, AditiPredProcId,
			JoinPredProcId),
		
		%
		% Create a procedure which is just a synonym
		% for do_*_aditi_call.
		%
		magic__create_aditi_call_proc(CPredProcId, JoinPredProcId)
	).

	% Make a procedure which calls the Aditi predicate, then joins
	% the result with the input and projects out the input arguments.
:- pred magic__create_input_join_proc(pred_proc_id::in, pred_proc_id::in,
		pred_proc_id::out, magic_info::in, magic_info::out) is det.		
magic__create_input_join_proc(CPredProcId, AditiPredProcId, JoinPredProcId) -->
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_proc_info(ModuleInfo0, CPredProcId,
		CPredInfo, CProcInfo) },
	{ proc_info_argmodes(CProcInfo, ArgModes0) },
	{ pred_info_arg_types(CPredInfo, _, ArgTypes) },
	{ magic_util__remove_aditi_state(ArgTypes, ArgModes0, ArgModes) },
	{ partition_args(ModuleInfo0, ArgModes, ArgModes,
		InputArgModes, OutputArgModes) },
	(
		% If the Aditi procedure has no inputs we don't need to
		% do anything unless it is a base relation.
		{ InputArgModes = [] },
		{ \+ hlds_pred__pred_info_is_base_relation(CPredInfo) }
	->
		{ JoinPredProcId = AditiPredProcId }
	;	
		{ InputArgModes = [] }
	->
		% No join with the input is needed for a base relation
		% with no input arguments.
		{ proc_info_headvars(CProcInfo, HeadVars0) },
		{ magic_util__remove_aditi_state(ArgTypes,
			HeadVars0, HeadVars) },

		% Build a call to the original C proc.
		{ set__list_to_set(HeadVars, NonLocals) },
		{ instmap_delta_from_mode_list(HeadVars0, ArgModes0,
			ModuleInfo0, Delta) },
		{ proc_info_inferred_determinism(CProcInfo, Detism) },
		{ goal_info_init(NonLocals, Delta, Detism, CallGoalInfo) },
		{ pred_info_module(CPredInfo, PredModule) },
		{ pred_info_name(CPredInfo, PredName) },
		{ AditiPredProcId = proc(AditiPredId, AditiProcId) },
		{ CallGoal = call(AditiPredId, AditiProcId, HeadVars,
			not_builtin, no, qualified(PredModule, PredName))
				- CallGoalInfo },

		{ JoinProcInfo0 = CProcInfo },
		{ proc_info_set_headvars(JoinProcInfo0,
			HeadVars, JoinProcInfo1) },
		{ proc_info_set_argmodes(JoinProcInfo1,
			ArgModes, JoinProcInfo2) },

		% Imported procedures such as base relations have nothing
		% in their vartypes field, so create it here.
		{ map__from_corresponding_lists(HeadVars0,
			ArgTypes, VarTypes) },
		{ proc_info_set_vartypes(JoinProcInfo2,
			VarTypes, JoinProcInfo3) },
		{ proc_info_set_goal(JoinProcInfo3, CallGoal, JoinProcInfo) },
		magic__build_join_pred_info(CPredInfo, JoinProcInfo,
			HeadVars, JoinPredProcId, _JoinPredInfo1)
	;
		% The interface procedure on the Aditi side must have
		% only one input closure argument.
		{ proc_info_vartypes(CProcInfo, VarTypes0) },
		{ proc_info_headvars(CProcInfo, HeadVars0) },
		{ magic_util__remove_aditi_state(ArgTypes,
			HeadVars0, HeadVars) },

		{ partition_args(ModuleInfo0, ArgModes, HeadVars,
			InputArgs, OutputArgs) },

		{ map__apply_to_list(InputArgs, VarTypes0, InputVarTypes) },

		{ list__length(InputVarTypes, NumInputVars) },
		{ construct_type(unqualified("pred") - NumInputVars,
			InputVarTypes, ClosureVarType) },
		{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
			InputArgModes, MagicArgModes) },
	
		{ JoinProcInfo0 = CProcInfo },
		{ proc_info_create_var_from_type(JoinProcInfo0,
			ClosureVarType, ClosureVar, JoinProcInfo1) },

		% Build a goal to call the input closure.
		{ set__list_to_set([ClosureVar | InputArgs],
			HOCallNonLocals) },
		{ instmap_delta_from_mode_list(InputArgs, MagicArgModes,
			ModuleInfo0, HOCallDelta) },
		{ goal_info_init(HOCallNonLocals, HOCallDelta, nondet,
			InputGoalInfo) },
		{ InputGoal = higher_order_call(ClosureVar,
			InputArgs, InputVarTypes, MagicArgModes,
			nondet, predicate) - InputGoalInfo },

		% Build a call to the original C proc.
		{ CPredProcId = proc(CPredId, CProcId) },
		{ proc_info_goal(CProcInfo, _ - CallGoalInfo) },
		{ pred_info_module(CPredInfo, PredModule) },
		{ pred_info_name(CPredInfo, PredName) },
		{ CallGoal = call(CPredId, CProcId, HeadVars0, not_builtin,
			no, qualified(PredModule, PredName)) - CallGoalInfo },
		magic_info_get_magic_map(MagicMap),
		(
			{ magic_util__goal_is_aditi_call(ModuleInfo0,
				MagicMap, CallGoal, DBCall0, _) }
		->
			{ DBCall = DBCall0 }	
		;
			{ error("magic__create_input_join_proc: not db_call") }
		),

		{ ClosureInst = ground(shared,
		    yes(pred_inst_info(predicate, MagicArgModes, nondet))) },
		{ ClosureMode = (ClosureInst -> ClosureInst) },
		{ proc_info_set_argmodes(JoinProcInfo1,
			[ClosureMode | OutputArgModes], JoinProcInfo2) },
		{ proc_info_set_headvars(JoinProcInfo2,
			[ClosureVar | OutputArgs], JoinProcInfo3) },

		magic__build_join_pred_info(CPredInfo, JoinProcInfo3,
			[ClosureVar | OutputArgs], JoinPredProcId,
			JoinPredInfo1),

		% Transform the new goal.
		% We don't want the call to be treated as recursive.
		magic_info_set_magic_vars([ClosureVar]),
		magic_info_set_sub_module([]),	
		magic_info_set_pred_info(JoinPredInfo1),
		magic_info_set_proc_info(JoinProcInfo3),

		% Use the ho-call goal as input to the C proc, transforming
		% the call to the C proc into a call to the corresponding
		% Aditi proc.
		{ set__list_to_set([ClosureVar | HeadVars], CallNonLocals) },
		magic_util__setup_call([InputGoal], DBCall, 
			CallNonLocals, Goals),

		magic_info_get_module_info(ModuleInfo10),
		{ instmap_delta_from_mode_list(OutputArgs, OutputArgModes,
			ModuleInfo10, GoalDelta) },
		{ set__list_to_set([ClosureVar | OutputArgs],
			GoalNonLocals) },
		{ goal_info_init(GoalNonLocals, GoalDelta, nondet, GoalInfo) },
		{ conj_list_to_goal(Goals, GoalInfo, Goal) },
		magic_info_get_proc_info(JoinProcInfo10),
		{ proc_info_set_goal(JoinProcInfo10, Goal, JoinProcInfo) },
		magic_info_get_pred_info(JoinPredInfo),
		{ module_info_set_pred_proc_info(ModuleInfo10, JoinPredProcId,
			JoinPredInfo, JoinProcInfo, ModuleInfo) },
		magic_info_set_module_info(ModuleInfo)
	).

:- pred magic__build_join_pred_info(pred_info::in, proc_info::in,
		list(var)::in, pred_proc_id::out, pred_info::out,
		magic_info::in, magic_info::out) is det.

magic__build_join_pred_info(CPredInfo, JoinProcInfo,
		Args, JoinPredProcId, JoinPredInfo1) -->
	{ proc_info_vartypes(JoinProcInfo, JoinVarTypes) },
	{ map__apply_to_list(Args, JoinVarTypes,
		NewArgTypes) },
	{ pred_info_module(CPredInfo, PredModule) },
	{ pred_info_name(CPredInfo, PredName) },
	{ string__append(PredName, "__c_interface", NewPredName) },
	{ init_markers(Markers0) },
	{ add_marker(Markers0, aditi, Markers1) },
	{ add_marker(Markers1, no_memo, Markers2) },
	{ add_marker(Markers2, naive, Markers) },
	{ ClassContext = [] },
	{ pred_info_get_aditi_owner(CPredInfo, User) },
	{ varset__init(TVarSet) },	% must be empty.
	{ term__context_init(DummyContext) },
	{ pred_info_create(PredModule,
		qualified(PredModule, NewPredName),
		TVarSet, NewArgTypes, true, DummyContext,
		exported, Markers, predicate, ClassContext, User,
		JoinProcInfo, JoinProcId, JoinPredInfo1) },
	{ JoinPredProcId = proc(JoinPredId, JoinProcId) },

	magic_info_get_module_info(ModuleInfo0),
	{ module_info_get_predicate_table(ModuleInfo0, Preds0) },
	{ predicate_table_insert(Preds0, JoinPredInfo1,
		JoinPredId, Preds) },
	{ module_info_set_predicate_table(ModuleInfo0,
		Preds, ModuleInfo) },
	magic_info_set_module_info(ModuleInfo).

	% The new procedure consists of a single call to
	% <predName>__aditi_interface, which call_gen generates
	% as a call to do_*_aditi_aditi in extras/aditi/aditi.m.
	% This procedure must use the `compact' argument convention.
	% The arguments are:
	% 	1 -> RL procedure name
	% 	2 -> number of input arguments
	% 	3 -> input schema
	% 	4 -> number of output arguments
	%	type_infos for input arguments
	% 	input arguments
	%	type_infos for output arguments
	%	output arguments
:- pred magic__create_aditi_call_proc(pred_proc_id::in, pred_proc_id::in,
		magic_info::in, magic_info::out) is det.		

magic__create_aditi_call_proc(CPredProcId, AditiPredProcId) -->
	magic__interface_pred_info(CPredProcId, AditiPredProcId,
		AditiProcName, InputSchema),

	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_proc_info(ModuleInfo0, CPredProcId,
		CPredInfo0, CProcInfo0) },
	{ pred_info_module(CPredInfo0, PredModule) },
	{ pred_info_name(CPredInfo0, PredName) },
	{ pred_info_arg_types(CPredInfo0, TVarSet, ArgTypes) },
	{ proc_info_argmodes(CProcInfo0, ArgModes) },
	{ proc_info_headvars(CProcInfo0, HeadVars) },

	% Base relations will have an empty vartypes field, so fill it in here.
	{ map__from_corresponding_lists(HeadVars, ArgTypes, VarTypes0) },
	{ proc_info_set_vartypes(CProcInfo0, VarTypes0, CProcInfo0a) },

	%
	% Build type-infos for the arguments so do_*_aditi_call
	% can do the required data conversions.
	%

	{ magic_util__remove_aditi_state(ArgTypes, ArgTypes, ArgTypes1) },
	{ magic_util__remove_aditi_state(ArgTypes, ArgModes, ArgModes1) },
	{ magic_util__remove_aditi_state(ArgTypes, HeadVars, HeadVars1) },

	magic__make_type_info_vars(ArgTypes1, TypeInfoVars, TypeInfoGoals,
		CPredInfo0, CPredInfo1, CProcInfo0a, CProcInfo1),

	magic_info_get_module_info(ModuleInfo1),

	{ partition_args(ModuleInfo1, ArgModes1, ArgTypes1,
		InputArgTypes, OutputArgTypes) },
	{ partition_args(ModuleInfo1, ArgModes1, ArgModes1,
		InputArgModes, OutputArgModes) },
	{ partition_args(ModuleInfo1, ArgModes1, TypeInfoVars,
		InputTypeInfoVars, OutputTypeInfoVars) },
	{ partition_args(ModuleInfo1, ArgModes1,
		HeadVars1, InputArgs, OutputArgs) },

	{ construct_type(unqualified("int") - 0, [], IntType) },
	{ construct_type(unqualified("string") - 0, [], StringType) },

	%
	% Build up some other information that do_*_aditi_call needs.
	% 

	% Procedure name.
	{ magic__make_const(StringType, string_const(AditiProcName),
		ProcNameVar, ProcNameGoal, CProcInfo1, CProcInfo2) },

	% Number of input arguments.
	{ list__length(InputArgTypes, NumInputArgs) },
	{ magic__make_const(IntType, int_const(NumInputArgs), InputArgsVar,
		InputArgsGoal, CProcInfo2, CProcInfo3) },

	% Input schema.
	{ magic__make_const(StringType, string_const(InputSchema),
		InputSchemaVar, InputSchemaGoal, CProcInfo3, CProcInfo4) },
	{ list__length(OutputArgTypes, NumOutputArgs) },

	% Number of output arguments.
	{ magic__make_const(IntType, int_const(NumOutputArgs), OutputArgsVar,
		OutputArgsGoal, CProcInfo4, CProcInfo5) },

	% Argument variables.
	{ list__condense([[ProcNameVar, InputArgsVar, InputSchemaVar,
		OutputArgsVar], InputTypeInfoVars, InputArgs,
		OutputTypeInfoVars, OutputArgs], DoCallAditiArgs) },

	% Argument types.
	{ proc_info_vartypes(CProcInfo5, VarTypes) },
	{ map__apply_to_list(InputTypeInfoVars,
		VarTypes, InputTypeInfoTypes) },
	{ map__apply_to_list(OutputTypeInfoVars,
		VarTypes, OutputTypeInfoTypes) },
	{ list__condense([[StringType, IntType, StringType, IntType],
		InputTypeInfoTypes, InputArgTypes, OutputTypeInfoTypes,
		OutputArgTypes], DoCallAditiArgTypes) },

	% Argument modes.
	{ in_mode(InMode) },
	{ list__duplicate(NumInputArgs, InMode, InputTypeInfoModes) },
	{ list__duplicate(NumOutputArgs, InMode, OutputTypeInfoModes) },
	{ list__condense([[InMode, InMode, InMode, InMode],
		InputTypeInfoModes, InputArgModes, OutputTypeInfoModes,
		OutputArgModes], DoCallAditiArgModes) },

	%
	% Create a new procedure which is just an alias for do_*_aditi_call.
	%
	{ varset__init(VarSet0) },
	{ list__length(DoCallAditiArgTypes, Arity) },
	{ varset__new_vars(VarSet0, Arity, DoCallAditiHeadVars, VarSet) },
	{ map__from_corresponding_lists(DoCallAditiHeadVars,
		DoCallAditiArgTypes, DoCallAditiVarTypes) },
	{ true_goal(DummyGoal) },
	{ term__context_init(DummyContext) },
	{ proc_info_inferred_determinism(CProcInfo5, Detism) },
	{ map__init(TVarMap) },
	{ map__init(TCVarMap) },
	{ proc_info_create(VarSet, DoCallAditiVarTypes, DoCallAditiHeadVars,
		DoCallAditiArgModes, Detism, DummyGoal, DummyContext,
		TVarMap, TCVarMap, compact, DoCallAditiProcInfo) },

	{ string__append(PredName, "__do_aditi_call", CallPredName) },
	{ init_markers(Markers0) },
	{ add_marker(Markers0, aditi_interface, Markers) },
	{ ClassContext = [] },
	{ pred_info_get_aditi_owner(CPredInfo1, User) },
	{ pred_info_create(PredModule, qualified(PredModule, CallPredName),
		TVarSet, DoCallAditiArgTypes, true, DummyContext,
		imported, Markers, predicate, ClassContext, User,
		DoCallAditiProcInfo, DoCallAditiProcId, DoCallAditiPredInfo) },

	{ module_info_get_predicate_table(ModuleInfo1, PredTable0) },
	{ predicate_table_insert(PredTable0, DoCallAditiPredInfo,
		DoCallAditiPredId, PredTable) },
	{ module_info_set_predicate_table(ModuleInfo1,
		PredTable, ModuleInfo2) },

	%
	% Make the C procedure call the new alias for do_*_aditi_call.
	%
	{ set__list_to_set(DoCallAditiArgs, CallNonLocals) },
	{ instmap_delta_from_mode_list(DoCallAditiArgs, DoCallAditiArgModes,
		ModuleInfo2, GoalDelta) },
	{ goal_info_init(CallNonLocals, GoalDelta, Detism, CallGoalInfo) },
	{ DoCallAditiGoal = call(DoCallAditiPredId, DoCallAditiProcId,
		DoCallAditiArgs, not_builtin, no,
		qualified(PredModule, CallPredName)) - CallGoalInfo },
	{ list__condense([[ProcNameGoal, InputArgsGoal, InputSchemaGoal,
		OutputArgsGoal], TypeInfoGoals, [DoCallAditiGoal]], Goals) },
	{ set__list_to_set(HeadVars, GoalNonLocals) },
	{ goal_list_determinism(Goals, GoalDetism) },
	{ goal_info_init(GoalNonLocals, GoalDelta, GoalDetism, GoalInfo) },
	{ Goal = conj(Goals) - GoalInfo },
	{ proc_info_set_goal(CProcInfo5, Goal, CProcInfo) },

	% The procedure is now just a normal C procedure.
	{ pred_info_get_markers(CPredInfo1, CMarkers1) },
	{ remove_marker(CMarkers1, aditi, CMarkers2) },
	{ remove_marker(CMarkers2, base_relation, CMarkers) },
	{ pred_info_set_markers(CPredInfo1, CMarkers, CPredInfo) },

	{ CPredProcId = proc(CPredId, CProcId) },
	{ module_info_set_pred_proc_info(ModuleInfo2, CPredId, CProcId,
		CPredInfo, CProcInfo, ModuleInfo) },
	magic_info_set_module_info(ModuleInfo).

:- pred magic__interface_pred_info(pred_proc_id::in, pred_proc_id::in,
	string::out, string::out, magic_info::in, magic_info::out) is det.

magic__interface_pred_info(CPredProcId, PredProcId,
		ProcNameStr, InputSchema) -->
	magic_info_get_module_info(ModuleInfo),
	{ rl_gen__get_entry_proc_name(ModuleInfo, PredProcId, ProcName) },
	{ rl__proc_name_to_string(ProcName, ProcNameStr) },
	{ module_info_pred_proc_info(ModuleInfo, CPredProcId,
		CPredInfo, CProcInfo) },
	{ pred_info_arg_types(CPredInfo, _, ArgTypes0) },
	{ proc_info_argmodes(CProcInfo, ArgModes0) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgTypes0, ArgTypes) },
	{ magic_util__remove_aditi_state(ArgTypes0, ArgModes0, ArgModes) },
	{ partition_args(ModuleInfo, ArgModes, ArgTypes, InputArgTypes, _) },
	{ rl_out__schema_to_string(ModuleInfo, InputArgTypes, InputSchema) }.

:- pred magic__make_type_info_vars(list(type)::in, list(var)::out,
	list(hlds_goal)::out, pred_info::in, pred_info::out,
	proc_info::in, proc_info::out, magic_info::in, magic_info::out) is det.

magic__make_type_info_vars(Types, TypeInfoVars, TypeInfoGoals,
		PredInfo0, PredInfo, ProcInfo0, ProcInfo) -->
	magic_info_get_module_info(ModuleInfo0),
	{ poly_info_init(ModuleInfo0, PredInfo0, ProcInfo0, PolyInfo0) },
	{ polymorphism__make_type_info_vars(Types,
		TypeInfoVars, TypeInfoGoals, PolyInfo0, PolyInfo) },
	{ poly_info_extract(PolyInfo, PredInfo0, PredInfo,
		ProcInfo0, ProcInfo, ModuleInfo) },
	magic_info_set_module_info(ModuleInfo).

:- pred magic__make_const((type)::in, cons_id::in, var::out, hlds_goal::out,
		proc_info::in, proc_info::out) is det.

magic__make_const(Type, ConsId, Var, Goal, ProcInfo0, ProcInfo) :-
	proc_info_create_var_from_type(ProcInfo0, Type, Var, ProcInfo),
	set__singleton_set(NonLocals, Var),
	Inst = bound(unique, [functor(ConsId, [])]),
	instmap_delta_init_reachable(Delta0),
	instmap_delta_insert(Delta0, Var, Inst, Delta),
	UnifyMode = (free -> Inst) - (Inst -> Inst),
	Uni = construct(Var, ConsId, [], []),
	Context = unify_context(explicit, []),
	goal_info_init(NonLocals, Delta, det, GoalInfo),
	Goal = unify(Var, functor(ConsId, []), UnifyMode, Uni, Context) -
			GoalInfo.
		
%-----------------------------------------------------------------------------%

	% Allocate a predicate to collect the input for the current predicate.
:- pred magic__create_magic_pred(pred_proc_id::in, pred_proc_id::in, 
		list(type)::in, list(mode)::in, list(type)::in, 
		list(mode)::in, maybe(int)::in,
		magic_info::in, magic_info::out) is det.

magic__create_magic_pred(CPredProcId, PredProcId, MagicTypes, MagicModes, 
		InputTypes0, InputModes0, Index) -->

	magic_info_get_module_info(ModuleInfo0),

	{ varset__init(VarSet0) },
	{ map__init(VarTypes0) },

		% Get some new vars to carry the magic input.
	{ list__length(MagicTypes, NumMagicArgs) },
	{ varset__new_vars(VarSet0, NumMagicArgs, MagicArgs, VarSet1) },
	{ map__det_insert_from_corresponding_lists(VarTypes0, MagicArgs, 
		MagicTypes, VarTypes1) },

		% Get some new vars for the outputs.
	{ list__length(InputModes0, NumInputArgs) },
	{ varset__new_vars(VarSet1, NumInputArgs, InputArgs0, VarSet2) },
	{ map__det_insert_from_corresponding_lists(VarTypes1, InputArgs0, 
		InputTypes0, VarTypes2) },

	{ list__map(magic_util__mode_to_output_mode(ModuleInfo0),
		InputModes0, OutputModes0) },

	{ module_info_pred_proc_info(ModuleInfo0, PredProcId,
		PredInfo, _) },
	{ pred_info_get_markers(PredInfo, Markers) },
	{ check_marker(Markers, context) ->
		% For magic context predicates, we get two copies of
		% the outputs. (See the paper cited at the top of context.m)
		varset__new_vars(VarSet2, NumInputArgs, InputArgs1, VarSet),
		map__det_insert_from_corresponding_lists(VarTypes2,
			InputArgs1, InputTypes0, VarTypes),
		list__append(InputArgs0, InputArgs1, InputArgs),
		list__append(InputTypes0, InputTypes0, InputTypes),
		list__append(OutputModes0, OutputModes0, OutputModes),
		assoc_list__from_corresponding_lists(InputArgs0, InputArgs1,
			ArgsAL0),
		IsContext = yes(ArgsAL0)	
	;
		VarSet = VarSet2,
		VarTypes = VarTypes2,
		InputArgs = InputArgs0,
		InputTypes = InputTypes0,
		OutputModes = OutputModes0,
		IsContext = no
	},

	{ list__append(MagicArgs, InputArgs, AllArgs) },
	( { Index = yes(N) } ->
		%
		% If this predicate is an entry point to the sub-module,
		% create a rule in the magic predicate to collect
		% the input relation.
		%
		{ list__index1_det(MagicArgs, N, CurrPredVar) },
		{ set__list_to_set([CurrPredVar | InputArgs0], NonLocals0) },
		{ mode_list_get_final_insts(OutputModes0, ModuleInfo0,
			OutputInsts0) },
		{ assoc_list__from_corresponding_lists(InputArgs0, 
			OutputInsts0, InstAL0) },
		{ instmap_delta_from_assoc_list(InstAL0, InstMapDelta0) },
		{ goal_info_init(NonLocals0, InstMapDelta0,
			nondet, GoalInfo0) },
		{ Goal0 = higher_order_call(CurrPredVar, InputArgs0,
			InputTypes, OutputModes0,
			nondet, predicate) - GoalInfo0 },
		( { IsContext = yes(ArgsAL) } ->
			% Create assignments to assign to the extra arguments.
			{ magic__create_assignments(ModuleInfo0, ArgsAL,
				OutputModes0, Assigns) },
			{ list__append(OutputInsts0,
				OutputInsts0, OutputInsts) },
			{ assoc_list__from_corresponding_lists(InputArgs, 
				OutputInsts, InstAL) },
			{ instmap_delta_from_assoc_list(InstAL, InstMapDelta) },
			{ set__list_to_set([CurrPredVar | InputArgs],
				NonLocals) },
			{ goal_info_init(NonLocals, InstMapDelta, nondet,
				GoalInfo) },
			{ conj_list_to_goal([Goal0 | Assigns],
				GoalInfo, Goal) }
		;
			{ Goal = Goal0 }
		)
	;
		% This predicate is not an entry point, so there's
		% no input to collect.
		{ fail_goal(Goal) }
	),

	{ list__append(MagicModes, OutputModes, AllArgModes) },

	{ term__context_init(Context) },

	% types must all be ground.
	{ map__init(TVarMap) },
	{ map__init(TCVarMap) },

	{ DummyArgsMethod = compact }, % never used
	{ proc_info_create(VarSet, VarTypes, AllArgs, AllArgModes, nondet,
		Goal, Context, TVarMap, TCVarMap, DummyArgsMethod, ProcInfo) },
	
	%
	% Fill in the pred_info.
	%

	{ CPredProcId = proc(CPredId, _) },
	{ module_info_name(ModuleInfo0, ModuleName) },
	{ predicate_module(ModuleInfo0, CPredId, PredModule) },
	{ predicate_name(ModuleInfo0, CPredId, PredName) },
	{ string__append(PredName, "__magic", NewPredName) },
	{ SymName = qualified(PredModule, NewPredName) },

	{ list__append(MagicTypes, InputTypes, AllArgTypes) },
	{ varset__init(TVarSet) },
	{ pred_info_get_aditi_owner(PredInfo, Owner) },
	{ ClassConstraints = [] }, % types must all be ground
	{ pred_info_create(ModuleName, SymName, TVarSet, AllArgTypes, 
		true, Context, local, Markers, predicate, ClassConstraints,
		Owner, ProcInfo, MagicProcId, MagicPredInfo) },

	{ module_info_get_predicate_table(ModuleInfo0, PredTable0) },
	{ predicate_table_insert(PredTable0, 
		MagicPredInfo, MagicPredId, PredTable) },
	{ module_info_set_predicate_table(ModuleInfo0, PredTable,
		ModuleInfo) },
	magic_info_set_module_info(ModuleInfo),

	% Record that the magic predicate in the magic_info.
	{ MagicPredProcId = proc(MagicPredId, MagicProcId) },
	magic_info_get_magic_map(MagicMap0),
	{ map__det_insert(MagicMap0, PredProcId, MagicPredProcId, MagicMap) },
	magic_info_set_magic_map(MagicMap).
	

	% Produce assignments to the duplicate outputs
	% of a context magic predicate.
:- pred magic__create_assignments(module_info::in, assoc_list(var, var)::in,
		list(mode)::in, list(hlds_goal)::out) is det.	

magic__create_assignments(_, [], [], []).
magic__create_assignments(_, [], [_|_], _) :-
	error("magic__create_assignments").
magic__create_assignments(_, [_|_], [], _) :-
	error("magic__create_assignments").
magic__create_assignments(ModuleInfo, [Arg0 - Arg | ArgsAL],
		[Mode | Modes], [Goal - GoalInfo | Assigns]) :-
	mode_get_insts(ModuleInfo, Mode, _, Inst),
	Goal = unify(Arg, var(Arg0), (free -> Inst) - (Inst -> Inst),
			assign(Arg, Arg0), unify_context(explicit, [])),
	set__list_to_set([Arg0, Arg], NonLocals),
	instmap_delta_from_assoc_list([Arg - Inst], Delta),
	goal_info_init(NonLocals, Delta, det, GoalInfo),
	magic__create_assignments(ModuleInfo, ArgsAL, Modes, Assigns).	

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

	% Put the goal for a procedure in a form suitable for processing.
:- pred magic__preprocess_proc(pred_proc_id::in, pred_info::in,
		proc_info::in, proc_info::out,
		magic_info::in, magic_info::out) is det.

magic__preprocess_proc(PredProcId, PredInfo, ProcInfo0, ProcInfo) -->
	{ proc_info_goal(ProcInfo0, Goal0) },

	%
	% Convert if-then-elses and switches to disjunctions.
	%
	( { Goal0 = if_then_else(_Vars, Cond, Then, Else, _SM) - GoalInfo } ->
		{ goal_util__if_then_else_to_disjunction(Cond, Then, Else, 
			GoalInfo, Disj) },
		{ Goal1 = Disj - GoalInfo },
		{ ProcInfo2 = ProcInfo0 }
	; { Goal0 = switch(Var, _Canfail, Cases, _SM) - GoalInfo } ->
		{ proc_info_varset(ProcInfo0, VarSet0) },
		{ proc_info_vartypes(ProcInfo0, VarTypes0) },
		magic_info_get_module_info(ModuleInfo0),
		{ proc_info_get_initial_instmap(ProcInfo0, 
			ModuleInfo0, InstMap) },
		{ goal_util__switch_to_disjunction(Var, Cases,
			InstMap, Disjuncts, VarSet0, VarSet1, 
			VarTypes0, VarTypes1, ModuleInfo0, ModuleInfo1) },
		magic_info_set_module_info(ModuleInfo1),
		{ proc_info_set_varset(ProcInfo0, VarSet1, ProcInfo1) },
		{ proc_info_set_vartypes(ProcInfo1, VarTypes1, ProcInfo2) },
		{ map__init(SM) },
		{ Goal1 = disj(Disjuncts, SM) - GoalInfo }
	;
		{ Goal1 = Goal0 },
		{ ProcInfo2 = ProcInfo0 }
	),

	{ proc_info_set_goal(ProcInfo2, Goal1, ProcInfo3) },

	%
	% Run simplification, mainly to get rid of 
	% nested explicit quantifications.
	% 
	magic_info_get_module_info(ModuleInfo2),
	{ module_info_globals(ModuleInfo2, Globals) },
	{ simplify__find_simplifications(no, Globals, Simplifications) },
	{ PredProcId = proc(PredId, ProcId) },
	{ simplify__proc_2(Simplifications, PredId, ProcId, 
		ModuleInfo2, ModuleInfo3, ProcInfo3, ProcInfo4, _) },

	{ proc_info_goal(ProcInfo4, Goal2) },
	magic_info_set_curr_pred_proc_id(PredProcId),
	magic_info_set_pred_info(PredInfo),
	magic_info_set_proc_info(ProcInfo4),
	magic_info_set_module_info(ModuleInfo3),
	{ Goal2 = _ - GoalInfo2 },
	{ goal_to_disj_list(Goal2, GoalList2) },
	list__map_foldl(magic__preprocess_disjunct, 
			GoalList2, GoalList),
	{ disj_list_to_goal(GoalList, GoalInfo2, Goal) },
	magic_info_get_proc_info(ProcInfo5),
	{ proc_info_set_goal(ProcInfo5, Goal, ProcInfo) }.

	% Undo common structure elimination of higher-order terms in an
	% attempt to avoid creating procedures with higher-order arguments
	% in the case where one closure is used by multiple aggregate calls.
	% Also remove assignments of `aditi:state's and report errors
	% for goals other than database calls which have an `aditi:state'
	% as a nonlocal.
:- pred magic__preprocess_disjunct(hlds_goal::in, hlds_goal::out,
		magic_info::in, magic_info::out) is det.

magic__preprocess_disjunct(Disjunct0, Disjunct) -->
	{ map__init(HOMap0) },
	{ Disjunct0 = _ - DisjInfo },
	magic__preprocess_goal(Disjunct0, Disjunct1, HOMap0, _),
	{ conj_list_to_goal(Disjunct1, DisjInfo, Disjunct) }.

:- pred magic__preprocess_goal(hlds_goal::in, list(hlds_goal)::out,
		map(var, hlds_goal)::in, map(var, hlds_goal)::out,
		magic_info::in, magic_info::out) is det.

magic__preprocess_goal(Goal, Goals, HOMap0, HOMap) -->
	magic__preprocess_goal_2(Goal, Goals, HOMap0, HOMap),
	list__foldl(magic__check_goal_nonlocals, Goals). 

:- pred magic__preprocess_goal_2(hlds_goal::in, list(hlds_goal)::out,
		map(var, hlds_goal)::in, map(var, hlds_goal)::out,
		magic_info::in, magic_info::out) is det.

	% Switches, if-then-elses and disjunctions involving database calls
	% should have been transformed into separate procedures by dnf.m.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) -->
	{ Goal = disj(_, _) - _ }.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) -->
	{ Goal = switch(_, _, _, _) - _ }.
magic__preprocess_goal_2(Goal, [Goal], HOMap, HOMap) --> 
	{ Goal = if_then_else(_, _, _, _, _) - _ }.
magic__preprocess_goal_2(par_conj(_, _) - _, _, _, _) -->
	{ error("Sorry, not yet implemented: parallel conjunction in Aditi procedures") }.
magic__preprocess_goal_2(class_method_call(_, _, _, _, _, _) - _, _, _, _) -->
	{ error("Sorry, not yet implemented: class method calls in Aditi procedures") }.
magic__preprocess_goal_2(higher_order_call(_, _, _, _, _, _) - _, _, _, _) -->
	{ error("Sorry, not yet implemented: higher-order calls in Aditi procedures") }.
magic__preprocess_goal_2(pragma_c_code(_, _, _, _, _, _, _) - _, _, _, _) -->
	{ error("Sorry, not yet implemented: pragma c_code calls in Aditi procedures") }.

magic__preprocess_goal_2(conj(Goals0) - GoalInfo, [conj(Goals) - GoalInfo],
		HOMap0, HOMap) -->
	magic__preprocess_conj(Goals0, [], Goals, HOMap0, HOMap).
magic__preprocess_goal_2(Goal0, Goals, HOMap, HOMap) -->
	{ Goal0 = call(PredId, _, Args, _, _, _) - _ },
	magic_info_get_module_info(ModuleInfo),
	( { hlds_pred__is_aditi_aggregate(ModuleInfo, PredId) } ->
		% Put the closures and the aggregate call in a sub-conjunction
		% of the top-level conjunction.
		magic__rename_and_generate_closures(Args, ExtraGoals,
			Goal0, Goal1, HOMap),
		{ list__append(ExtraGoals, [Goal1], Goals1) },
		{ Goal0 = _ - GoalInfo0 },
		{ conj_list_to_goal(Goals1, GoalInfo0, Goal) },
		{ Goals = [Goal] }
	;
		{ Goals = [Goal0] }
	).
magic__preprocess_goal_2(some(Vars, Goal0) - Info, [some(Vars, Goal) - Info],
		HOMap0, HOMap) -->
	{ Goal0 = _ - SomeGoalInfo },
	magic__preprocess_goal(Goal0, SomeGoals, HOMap0, HOMap),
	{ conj_list_to_goal(SomeGoals, SomeGoalInfo, Goal) }.
magic__preprocess_goal_2(not(Goal0) - Info, [not(Goal) - Info],
		HOMap0, HOMap) -->
	{ Goal0 = _ - NegGoalInfo },
	magic__preprocess_goal(Goal0, NegGoals, HOMap0, HOMap),
	{ conj_list_to_goal(NegGoals, NegGoalInfo, Goal) }.
magic__preprocess_goal_2(Goal0, Goals, HOMap0, HOMap) -->
	{ Goal0 = unify(_, _, _, Uni, _) - GoalInfo },
	(
		{ Uni = construct(Var, pred_const(_, _), Args, _) }
	->
		% Collect up the closure construction so that it can be
		% placed next to the aggregate goal that uses it.
		% 
		% XXX What about if someone puts a closure inside
		% a structure? At the moment we don't handle it and
		% we don't give an error message.
		magic_info_get_proc_info(ProcInfo),
		(
			{ Args = [] }
		->
			[]
		; 
			{ Args = [Arg] },
			{ proc_info_vartypes(ProcInfo, VarTypes) },
			{ map__lookup(VarTypes, Arg, ArgType) },
			{ type_is_aditi_state(ArgType) }
		->
			[]
		;
			% XXX we don't yet allow curried arguments.
			{ goal_info_get_context(GoalInfo, Context) },
			magic_info_get_curr_pred_proc_id(PredProcId),
			magic_info_get_errors(Errors0),
			{ Error = curried_argument(PredProcId) - Context },
			{ set__insert(Errors0, Error, Errors) },
			magic_info_set_errors(Errors)
		),
		{ map__det_insert(HOMap0, Var, Goal0, HOMap) },
		{ Goals = [] }
	;
		{ Uni = assign(Var1, Var2) }
	->
		magic_info_get_proc_info(ProcInfo),
		{ proc_info_vartypes(ProcInfo, VarTypes) },	
		{ map__lookup(VarTypes, Var1, Var1Type) },
		( { type_is_aditi_state(Var1Type) } ->
			% Remove assignments of `aditi:state's.
			{ HOMap = HOMap0 },
			{ Goals = [] }
		; { map__search(HOMap0, Var2, Entry) } ->
			{ Goals = [] },
			{ map__det_insert(HOMap0, Var1, Entry, HOMap) }
		;
			{ Goals = [Goal0] },
			{ HOMap = HOMap0 }
		)
	;
		{ Goals = [Goal0] },
		{ HOMap = HOMap0 }
	).

:- pred magic__preprocess_conj(list(hlds_goal)::in, list(hlds_goal)::in,
	list(hlds_goal)::out, map(var, hlds_goal)::in,
	map(var, hlds_goal)::out, magic_info::in, magic_info::out) is det.

magic__preprocess_conj([], RevGoals, Goals, HOMap, HOMap) -->
	{ list__reverse(RevGoals, Goals) }.
magic__preprocess_conj([Goal0 | Goals0], RevGoals0, Goals, HOMap0, HOMap) -->
	magic__preprocess_goal(Goal0, Goals1, HOMap0, HOMap1),
	{ list__reverse(Goals1, RevGoals1) },
	{ list__append(RevGoals1, RevGoals0, RevGoals) },
	magic__preprocess_conj(Goals0, RevGoals, Goals, HOMap1, HOMap).

	% If the goal is not a database call and does not contain
	% a database call, it cannot have an `aditi:state' as a non-local.
:- pred magic__check_goal_nonlocals(hlds_goal::in,
		magic_info::in, magic_info::out) is det.

magic__check_goal_nonlocals(Goal) -->
	magic_info_get_module_info(ModuleInfo),
	magic_info_get_pred_map(PredMap),
	(
		% We check inside not, some and conj goals for calls, so don't
		% report errors at the top-level of those goals.
		{
			Goal = not(_) - _
		;
			Goal = some(_, _) - _
		;
			Goal = conj(_) - _
		; 	
			Goal = call(_, _, _, _, _, _) - _,
			magic_util__goal_is_aditi_call(ModuleInfo, PredMap,
				Goal, _, _)
		;
			Goal = unify(_, _, _, Uni, _) - _,
			Uni = construct(_, pred_const(PredId, ProcId), _, _),
			(
				map__contains(PredMap, proc(PredId, ProcId))
			;
				hlds_pred__is_aditi_relation(ModuleInfo,
					PredId)
			)	
		}
	->
		[]
	;
		{ Goal = _ - GoalInfo },
		{ goal_info_get_nonlocals(GoalInfo, NonLocals0) },
		{ set__to_sorted_list(NonLocals0, NonLocals) },
		magic_info_get_proc_info(ProcInfo),
		{ proc_info_vartypes(ProcInfo, VarTypes) },
		{ IsAditiVar = lambda([Var::in] is semidet, (
					map__lookup(VarTypes, Var, Type),
					type_is_aditi_state(Type)
				)) },
		{ list__filter(IsAditiVar, NonLocals, IllegalNonLocals) },
		( { IllegalNonLocals = [] } ->
			[]
		;
			magic_info_get_errors(Errors0),
			{ goal_info_get_context(GoalInfo, Context) },
			magic_info_get_curr_pred_proc_id(PredProcId),
			{ proc_info_varset(ProcInfo, VarSet) },
			{ Error = non_removeable_aditi_state(PredProcId,
				VarSet, IllegalNonLocals) - Context },
			{ set__insert(Errors0, Error, Errors) },
			magic_info_set_errors(Errors)			
		)
	).

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

	% Generate goals to create the closures needed by this call.
:- pred magic__rename_and_generate_closures(list(var)::in, 
	list(hlds_goal)::out, hlds_goal::in, hlds_goal::out,
	map(var, hlds_goal)::in, magic_info::in, magic_info::out) is det.

magic__rename_and_generate_closures([], [], Goal, Goal, _) --> [].
magic__rename_and_generate_closures([Arg | Args], ExtraGoals, 
		Goal0, Goal, HOMap) -->
	magic__rename_and_generate_closures(Args, ExtraGoals1, 
		Goal0, Goal1, HOMap), 
	( { map__search(HOMap, Arg, ClosureGoal0) } ->
		magic_info_get_proc_info(ProcInfo0),
		{ proc_info_vartypes(ProcInfo0, VarTypes0) },
		{ map__lookup(VarTypes0, Arg, Type) },
		{ proc_info_create_var_from_type(ProcInfo0, 
			Type, NewArg, ProcInfo) },
		magic_info_set_proc_info(ProcInfo),
		{ map__init(Subn0) },
		{ map__det_insert(Subn0, Arg, NewArg, Subn) },
		{ goal_util__rename_vars_in_goal(ClosureGoal0,
			Subn, ClosureGoal) },
		{ goal_util__rename_vars_in_goal(Goal1, Subn, Goal) },
		{ ExtraGoals = [ClosureGoal | ExtraGoals1] }
	;	
		{ ExtraGoals = ExtraGoals1 },
		{ Goal = Goal1 }
	).

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

:- pred magic__process_sub_module(aditi_sub_module::in, magic_info::in,
		magic_info::out, io__state::di, io__state::uo) is det.

magic__process_sub_module(aditi_sub_module(SubModule0, _), Info0, Info) -->
	{ list__condense(SubModule0, SubModule) },
	{ magic_info_set_sub_module(SubModule, Info0, Info1) },
	{ list__foldl(magic__process_proc, SubModule, Info1, Info) }.

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

:- pred magic__process_proc(pred_proc_id::in, 
		magic_info::in, magic_info::out) is det.

magic__process_proc(PredProcId0) -->
	magic_info_get_pred_map(PredMap),
	{ map__search(PredMap, PredProcId0, PredProcId1) ->
		PredProcId = PredProcId1
	;
		PredProcId = PredProcId0
	},
	magic_info_get_module_info(ModuleInfo0),
	{ module_info_pred_proc_info(ModuleInfo0, PredProcId, 
		PredInfo0, ProcInfo0) },
	(
		{ pred_info_is_imported(PredInfo0)
		; pred_info_is_pseudo_imported(PredInfo0)
		}
	->
		[]
	;
		magic_info_set_curr_pred_proc_id(PredProcId),
		magic_info_set_pred_info(PredInfo0),
		magic_info_set_proc_info(ProcInfo0),
		magic_info_get_magic_proc_info(MagicProcInfo),
		{ map__lookup(MagicProcInfo, PredProcId, ThisProcInfo) },
		{ ThisProcInfo = magic_proc_info(OldArgModes, MagicInputs,
					_, _, _) },
		magic_info_set_magic_vars(MagicInputs),
		{ set__init(ErrorVars) },
		magic_info_set_error_vars(ErrorVars),

		{ proc_info_headvars(ProcInfo0, HeadVars) },

		{ list__length(MagicInputs, NumMagicInputs) },
		{ list__drop(NumMagicInputs, HeadVars, OldHeadVars) ->
			partition_args(ModuleInfo0, 
				OldArgModes, OldHeadVars, Inputs, Outputs)
		;
			error("magic__process_proc: list__drop failed")
		},

		{ pred_info_get_markers(PredInfo0, Markers) },
		{ proc_info_goal(ProcInfo0, Goal0) },
		{ Goal0 = _ - GoalInfo0 },
		{ goal_to_disj_list(Goal0, DisjList0) },
		( { check_marker(Markers, context) } ->
			context__process_disjuncts(PredProcId0, Inputs,
				Outputs, DisjList0, DisjList)
		;
			{ set__list_to_set(Inputs, InputSet) },
			magic__process_disjuncts(InputSet, DisjList0, DisjList)
		),

		{ disj_list_to_goal(DisjList, GoalInfo0, Goal) },
		magic_info_get_pred_info(PredInfo),
		magic_info_get_proc_info(ProcInfo1),
		{ proc_info_set_goal(ProcInfo1, Goal, ProcInfo) },

		magic_info_get_module_info(ModuleInfo1),
		{ module_info_set_pred_proc_info(ModuleInfo1, PredProcId,
			PredInfo, ProcInfo, ModuleInfo) },
		magic_info_set_module_info(ModuleInfo)
	).

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

:- pred magic__process_disjuncts(set(var)::in, list(hlds_goal)::in,
		list(hlds_goal)::out, magic_info::in, magic_info::out) is det.	

magic__process_disjuncts(_, [], []) --> [].
magic__process_disjuncts(Inputs, [Disjunct0 | Disjuncts0],
		[Disjunct | Disjuncts]) -->
	magic__process_disjunct(Inputs, Disjunct0, Disjunct),
	magic__process_disjuncts(Inputs, Disjuncts0, Disjuncts).

:- pred magic__process_disjunct(set(var)::in, hlds_goal::in, hlds_goal::out,
		magic_info::in, magic_info::out) is det.	

magic__process_disjunct(_Inputs, Disjunct0, Disjunct) --> 
	{ Disjunct0 = _ - DisjInfo },
	{ goal_to_conj_list(Disjunct0, GoalList0) },
	{ list__reverse(GoalList0, RevGoalList0) },
	magic__get_next_db_pred(RevGoalList0, BeforeGoals, 
		MaybeDBCall, [], AfterGoals),

	( { MaybeDBCall = yes(DBCall1) } ->
		{ magic_util__db_call_nonlocals(DBCall1, NonLocals1) },
		{ goal_list_nonlocals(AfterGoals, AfterNonLocals) },
		{ goal_info_get_nonlocals(DisjInfo, NonLocals0) },
		{ set__union(NonLocals0, AfterNonLocals, SubConjNonLocals0) },
		{ set__union(SubConjNonLocals0, NonLocals1,
			SubConjNonLocals1) },
		magic_util__restrict_nonlocals(SubConjNonLocals1,
			SubConjNonLocals),
		magic__process_disjunct_2(BeforeGoals, DBCall1,
			SubConjNonLocals, GoalList1),
		{ list__append(GoalList1, AfterGoals, GoalList) }
	;
		magic__create_magic_call(MagicCall),
		{ GoalList = [MagicCall | GoalList0] }
	),
	{ conj_list_to_goal(GoalList, DisjInfo, Disjunct) }.

	% Search backwards through the goal list for a disjunct.
	% When a call is found, recursively process the goals before 
	% it. 
:- pred magic__process_disjunct_2(list(hlds_goal)::in, db_call::in,
		set(var)::in, list(hlds_goal)::out,
		magic_info::in, magic_info::out) is det.

magic__process_disjunct_2(RevBeforeGoals1, DBCall1, NonLocals0, Goals) -->

	% Work out the nonlocals of the goals after the part of the 
	% disjunct being processed here.

	% Find the next database call.
	magic__get_next_db_pred(RevBeforeGoals1, RevBeforeGoals2, 
		MaybeDBCall2, [], AfterGoals2),

	( { MaybeDBCall2 = yes(DBCall2) } ->

		% Recursively process the goals before the call we just found.
		{ magic_util__db_call_nonlocals(DBCall2, CallNonLocals2) },
		{ goal_list_nonlocals(AfterGoals2, AfterNonLocals) },
		{ set__union(NonLocals0, AfterNonLocals, NonLocals1) },
		{ set__union(NonLocals1, CallNonLocals2, NonLocals2) },
		magic_util__restrict_nonlocals(NonLocals2, NonLocals),
		magic__process_disjunct_2(RevBeforeGoals2, DBCall2,
			NonLocals, Goals2),
		{ list__append(Goals2, AfterGoals2, Goals3) },

		% Turn those goals into a supplementary predicate, and
		% use that to create the input for the first call.
		magic_util__setup_call(Goals3, DBCall1, NonLocals0, Goals)
	;
		% We've run out of calls to process, so get the magic 
		% input for this procedure to feed the other calls.
		magic__create_magic_call(MagicCall),
		{ list__reverse(RevBeforeGoals1, BeforeGoals1) },
		magic_util__setup_call([MagicCall | BeforeGoals1],
			DBCall1, NonLocals0, Goals)
	).

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

	% Skip along the reversed list of goals to the first database call,
	% returning the list of goals before and after the call as well.
:- pred magic__get_next_db_pred(list(hlds_goal)::in, list(hlds_goal)::out, 
		maybe(db_call)::out, list(hlds_goal)::in, 
		list(hlds_goal)::out, magic_info::in, magic_info::out) is det.

magic__get_next_db_pred([], [], no, Goals, Goals) --> [].
magic__get_next_db_pred([Goal | RevGoals], RevBeforeGoals, 
		MaybeCall, AfterGoals0, AfterGoals) -->
	magic_info_get_module_info(ModuleInfo),
	magic_info_get_pred_map(PredMap),
	(
		{ magic_util__goal_is_aditi_call(ModuleInfo, PredMap,
			Goal, Call, AfterGoals1) }
	->
		{ MaybeCall = yes(Call) },
		{ RevBeforeGoals = RevGoals },
		{ list__append(AfterGoals1, AfterGoals0, AfterGoals) }
	;
		magic__get_next_db_pred(RevGoals, RevBeforeGoals,
			MaybeCall, [Goal | AfterGoals0], AfterGoals)
	).

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

	% Create a call to the magic procedure for the current procedure.
:- pred magic__create_magic_call(hlds_goal::out,
		magic_info::in, magic_info::out) is det.

magic__create_magic_call(MagicCall) -->
	magic_util__magic_call_info(MagicPredId, MagicProcId, PredName,
		InputRels, InputArgs, MagicOutputModes),

	{ list__append(InputRels, InputArgs, MagicArgs) },

	{ set__list_to_set(MagicArgs, NonLocals) },
	magic_info_get_module_info(ModuleInfo),
	{ instmap_delta_from_mode_list(InputArgs, MagicOutputModes,
		ModuleInfo, InstMapDelta) },
	{ goal_info_init(NonLocals, InstMapDelta, nondet, GoalInfo) },

	{ MagicCall = call(MagicPredId, MagicProcId, MagicArgs,
			not_builtin, no, PredName) - GoalInfo }.

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



More information about the developers mailing list