Files
mercury/compiler/clause_to_proc.m
Simon Taylor 7828cb3587 Allow separate mode declarations for predicates with no arguments
Estimated hours taken: 2

Allow separate mode declarations for predicates with no arguments
(previously they were considered to be duplicate mode declarations).
This avoids the need to special case such predicates when writing
out declarations for inter-module optimization.

If there is no mode declaration and the `:- pred' declaration does
not include the determinism, a default mode `:- mode foo.' is added.

Compilation of the browser directory with --intermodule-optimization
fails with a duplicate mode error without this change.

compiler/make_hlds.m:
	Handle default modes for zero arity predicates using a similar
	method to that used for the `in, in, ..., out' modes for functions,
	except that there will still be an error message if determinism
	inference is not enabled.

compiler/clause_to_proc.m:
	Rename `add_default_modes' to `add_default_func_modes',
	to avoid confusion with `add_default_zero_arity_pred_modes'
	in make_hlds.m.

compiler/modecheck_call.m:
compiler/post_typecheck.m:
	Remove some unnecessary calls to `add_default_mode'.
	Default modes are added in make_hlds.m.

tests/valid/Mmakefile:
tests/valid/zero_arity.m:
	Test case.

tests/invalid/Mmakefile:
	Use `--no-infer-det' when compiling missing_det_decls.m so that
	error messages for missing determinism declarations for local
	predicates are reported (one of the local predicates in that test
	case has no arguments).

tests/invalid/bigtest.err_exp:
	Remove the error message for a missing mode declaration for a zero
	arity predicate.

tests/invalid/duplicate_modes.err_exp:
	Remove a duplicate mode error for a zero arity predicate with
	a separate mode declaration.
1999-08-31 05:26:10 +00:00

228 lines
7.8 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1995-1999 The 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.
%-----------------------------------------------------------------------------%
:- module clause_to_proc.
:- interface.
:- import_module hlds_pred, hlds_module.
:- import_module list, std_util.
% In the hlds, we initially record the clauses for a predicate
% in the clauses_info data structure which is part of the
% pred_info data structure. But once the clauses have been
% type-checked, we want to have a separate copy of each clause
% for each different mode of the predicate, since we may
% end up reordering the clauses differently in different modes.
% Here we copy the clauses from the clause_info data structure
% into the proc_info data structure. Each clause is marked
% with a list of the modes for which it applies, so that
% there can be different code to implement different modes
% of a predicate (e.g. sort). For each mode of the predicate,
% we select the clauses for that mode, disjoin them together,
% and save this in the proc_info.
:- pred copy_module_clauses_to_procs(list(pred_id), module_info, module_info).
:- mode copy_module_clauses_to_procs(in, in, out) is det.
:- pred copy_clauses_to_procs(pred_info, pred_info).
:- mode copy_clauses_to_procs(in, out) is det.
:- pred copy_clauses_to_proc(proc_id, clauses_info, proc_info, proc_info).
:- mode copy_clauses_to_proc(in, in, in, out) is det.
% Before copying the clauses to the procs, we need to add
% a default mode of `:- mode foo(in, in, ..., in) = out is det.'
% for functions that don't have an explicit mode declaration.
:- pred maybe_add_default_func_modes(list(pred_id), pred_table, pred_table).
:- mode maybe_add_default_func_modes(in, in, out) is det.
:- pred maybe_add_default_func_mode(pred_info, pred_info, maybe(proc_id)).
:- mode maybe_add_default_func_mode(in, out, out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds_goal, hlds_data, prog_data, mode_util, make_hlds, purity.
:- import_module globals.
:- import_module bool, int, set, map.
maybe_add_default_func_modes([], Preds, Preds).
maybe_add_default_func_modes([PredId | PredIds], Preds0, Preds) :-
map__lookup(Preds0, PredId, PredInfo0),
maybe_add_default_func_mode(PredInfo0, PredInfo, _),
map__det_update(Preds0, PredId, PredInfo, Preds1),
maybe_add_default_func_modes(PredIds, Preds1, Preds).
maybe_add_default_func_mode(PredInfo0, PredInfo, MaybeProcId) :-
pred_info_procedures(PredInfo0, Procs0),
pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc),
(
%
% Is this a function with no modes?
%
PredOrFunc = function,
map__is_empty(Procs0)
->
%
% If so, add a default mode of
%
% :- mode foo(in, in, ..., in) = out is det.
%
% for this function. (N.B. functions which can
% fail must be explicitly declared as semidet.)
%
pred_info_arity(PredInfo0, PredArity),
FuncArity is PredArity - 1,
in_mode(InMode),
out_mode(OutMode),
list__duplicate(FuncArity, InMode, FuncArgModes),
FuncRetMode = OutMode,
list__append(FuncArgModes, [FuncRetMode], PredArgModes),
Determinism = det,
pred_info_context(PredInfo0, Context),
MaybePredArgLives = no,
add_new_proc(PredInfo0, PredArity, PredArgModes,
yes(PredArgModes), MaybePredArgLives, yes(Determinism),
Context, address_is_not_taken, PredInfo, ProcId),
MaybeProcId = yes(ProcId)
;
PredInfo = PredInfo0,
MaybeProcId = no
).
copy_module_clauses_to_procs(PredIds, ModuleInfo0, ModuleInfo) :-
module_info_preds(ModuleInfo0, Preds0),
copy_module_clauses_to_procs_2(PredIds, Preds0, Preds),
module_info_set_preds(ModuleInfo0, Preds, ModuleInfo).
:- pred copy_module_clauses_to_procs_2(list(pred_id), pred_table, pred_table).
:- mode copy_module_clauses_to_procs_2(in, in, out) is det.
copy_module_clauses_to_procs_2([], Preds, Preds).
copy_module_clauses_to_procs_2([PredId | PredIds], Preds0, Preds) :-
map__lookup(Preds0, PredId, PredInfo0),
(
% don't process typeclass methods, because their proc_infos
% are generated already mode-correct
pred_info_get_markers(PredInfo0, PredMarkers),
check_marker(PredMarkers, class_method)
->
Preds1 = Preds0
;
copy_clauses_to_procs(PredInfo0, PredInfo),
map__det_update(Preds0, PredId, PredInfo, Preds1)
),
copy_module_clauses_to_procs_2(PredIds, Preds1, Preds).
copy_clauses_to_procs(PredInfo0, PredInfo) :-
pred_info_procedures(PredInfo0, Procs0),
pred_info_clauses_info(PredInfo0, ClausesInfo),
pred_info_non_imported_procids(PredInfo0, ProcIds),
copy_clauses_to_procs_2(ProcIds, ClausesInfo, Procs0, Procs),
pred_info_set_procedures(PredInfo0, Procs, PredInfo).
:- pred copy_clauses_to_procs_2(list(proc_id), clauses_info,
proc_table, proc_table).
:- mode copy_clauses_to_procs_2(in, in, in, out) is det.
copy_clauses_to_procs_2([], _, Procs, Procs).
copy_clauses_to_procs_2([ProcId | ProcIds], ClausesInfo, Procs0, Procs) :-
map__lookup(Procs0, ProcId, Proc0),
copy_clauses_to_proc(ProcId, ClausesInfo, Proc0, Proc),
map__det_update(Procs0, ProcId, Proc, Procs1),
copy_clauses_to_procs_2(ProcIds, ClausesInfo, Procs1, Procs).
copy_clauses_to_proc(ProcId, ClausesInfo, Proc0, Proc) :-
ClausesInfo = clauses_info(VarSet, _, VarTypes, HeadVars, Clauses,
TI_VarMap, TCI_VarMap),
select_matching_clauses(Clauses, ProcId, MatchingClauses),
get_clause_goals(MatchingClauses, GoalList),
( GoalList = [SingleGoal] ->
Goal = SingleGoal
;
%
% Convert the list of clauses into a disjunction,
% and construct a goal_info for the disjunction.
%
%
% We use the context of the first clause, unless
% there weren't any clauses at all, in which case
% we use the context of the mode declaration.
%
goal_info_init(GoalInfo0),
( GoalList = [FirstGoal | _] ->
FirstGoal = _ - FirstGoalInfo,
goal_info_get_context(FirstGoalInfo, Context)
;
proc_info_context(Proc0, Context)
),
goal_info_set_context(GoalInfo0, Context, GoalInfo1),
%
% The non-local vars are just the head variables.
%
set__list_to_set(HeadVars, NonLocalVars),
goal_info_set_nonlocals(GoalInfo1, NonLocalVars, GoalInfo2),
%
% The disjunction is impure/semipure if any of the disjuncts
% is impure/semipure.
%
(
list__member(_SubGoal - SubGoalInfo, GoalList),
\+ goal_info_is_pure(SubGoalInfo)
->
list__map(get_purity, GoalList, PurityList),
list__foldl(worst_purity, PurityList, (pure), Purity),
add_goal_info_purity_feature(GoalInfo2, Purity,
GoalInfo)
;
GoalInfo2 = GoalInfo
),
map__init(Empty),
Goal = disj(GoalList, Empty) - GoalInfo
),
proc_info_set_body(Proc0, VarSet, VarTypes, HeadVars, Goal,
TI_VarMap, TCI_VarMap, Proc).
:- pred get_purity(hlds_goal, purity).
:- mode get_purity(in, out) is det.
get_purity(_Goal - GoalInfo, Purity) :-
infer_goal_info_purity(GoalInfo, Purity).
:- pred select_matching_clauses(list(clause), proc_id, list(clause)).
:- mode select_matching_clauses(in, in, out) is det.
select_matching_clauses([], _, []).
select_matching_clauses([Clause | Clauses], ProcId, MatchingClauses) :-
Clause = clause(ProcIds, _, _),
% an empty list here means that the clause applies to all procs
( ProcIds = [] ->
MatchingClauses = [Clause | MatchingClauses1]
; list__member(ProcId, ProcIds) ->
MatchingClauses = [Clause | MatchingClauses1]
;
MatchingClauses = MatchingClauses1
),
select_matching_clauses(Clauses, ProcId, MatchingClauses1).
:- pred get_clause_goals(list(clause)::in, list(hlds_goal)::out) is det.
get_clause_goals([], []).
get_clause_goals([Clause | Clauses], Goals) :-
Clause = clause(_, Goal, _),
goal_to_disj_list(Goal, GoalList),
list__append(GoalList, Goals1, Goals),
get_clause_goals(Clauses, Goals1).