%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1996-2012 The University of Melbourne. % Copyright (C) 2013-2018 The Mercury team. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: intermod.m. % Main author: stayl (the original intermod.m). % % This module writes out the first half of .opt files, which we use % to implement inter-module optimization. The second half is written out % by intermod_analysis.m. % % The first half of the .opt file includes: % - The clauses for exported preds that can be inlined. % - The clauses for exported preds that have higher-order pred arguments. % - The pred/mode declarations for local predicates that the % above clauses use. % - pragma declarations for the exported preds. % - Non-exported types, insts and modes used by the above. % - Pragma foreign_enum, or foreign_type declarations for % any types output due to the line above. % - :- import_module declarations to import stuff used by the above. % - pragma foreign_import_module declarations if any pragma foreign_proc % preds are written. % All these items should be module qualified. % % Note that predicates which call predicates that do not have mode or % determinism declarations do not have clauses exported, since this would % require running mode analysis and determinism analysis before writing the % .opt file, significantly increasing compile time for a very small gain. % % This module also contains predicates to adjust the import status % of local predicates which are exported for intermodule optimization. % %---------------------------------------------------------------------------% :- module transform_hlds.intermod. :- interface. :- import_module hlds. :- import_module hlds.hlds_module. :- import_module parse_tree. :- import_module parse_tree.prog_item. :- import_module io. %---------------------------------------------------------------------------% % A value of this type specifies the set of entities we opt-export % from a module. % :- type intermod_info. % Open the file ".opt.tmp", and write out the declarations % and clauses for intermodule optimization. % % Although this predicate creates the .opt.tmp file, it does not % necessarily create it in its final form. Later compiler passes % may append to this file using the append_analysis_pragmas_to_opt_file % predicate in intermod_analysis.m. % XXX This is not an elegant arrangement. % % Update_interface and touch_interface_datestamp are called from % mercury_compile_front_end.m, since they must be called after % the last time anything is appended to the .opt.tmp file. % :- pred write_initial_opt_file(io.text_output_stream::in, module_info::in, intermod_info::out, parse_tree_plain_opt::out, io::di, io::uo) is det. %---------------------------------------------------------------------------% % Find out which predicates would be opt-exported, and mark them % accordingly. (See the comment on do_maybe_opt_export_entities % for why we do this.) % :- pred maybe_opt_export_entities(module_info::in, module_info::out) is det. % Change the status of the entities (predicates, types, insts, modes, % classes and instances) listed as opt-exported in the given intermod_info % to opt-exported. This affects how the rest of the compiler treats % these entities. For example, the entry labels at the starts of % the C code fragments we generate for an opt-exported local predicate % needs to be exported from the .c file, and opt-exported procedures % should not be touched by dead proc elimination. % % The reason why we have a separate pass for this, instead of changing % the status of an item to reflect the fact that it is opt-exported % at the same time as we decide to opt-export it, is that the decision % to opt-export e.g. a procedure takes place inside invocations of % mmc --make-opt-int, but we also need the same status updates % in invocations of mmc that generate target language code. % :- pred maybe_opt_export_listed_entities(intermod_info::in, module_info::in, module_info::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module backend_libs. :- import_module backend_libs.foreign. :- import_module check_hlds. :- import_module check_hlds.mode_test. :- import_module check_hlds.type_util. :- import_module hlds.goal_form. :- import_module hlds.goal_util. :- import_module hlds.hlds_class. :- import_module hlds.hlds_clauses. :- import_module hlds.hlds_cons. :- import_module hlds.hlds_data. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_inst_mode. :- import_module hlds.hlds_out. :- import_module hlds.hlds_out.hlds_out_goal. :- import_module hlds.hlds_out.hlds_out_pred. :- import_module hlds.hlds_out.hlds_out_util. :- import_module hlds.hlds_pred. :- import_module hlds.hlds_promise. :- import_module hlds.passes_aux. :- import_module hlds.pred_name. :- import_module hlds.pred_table. :- import_module hlds.special_pred. :- import_module hlds.status. :- import_module libs. :- import_module libs.file_util. :- import_module libs.globals. :- import_module libs.optimization_options. :- import_module libs.options. :- import_module mdbcomp. :- import_module mdbcomp.prim_data. :- import_module mdbcomp.sym_name. :- import_module parse_tree.item_util. :- import_module parse_tree.parse_tree_out. :- import_module parse_tree.parse_tree_out_info. :- import_module parse_tree.parse_tree_out_misc. :- import_module parse_tree.parse_tree_out_pragma. :- import_module parse_tree.parse_tree_out_sym_name. :- import_module parse_tree.parse_tree_to_term. :- import_module parse_tree.prog_data. :- import_module parse_tree.prog_data_foreign. :- import_module parse_tree.prog_data_pragma. :- import_module parse_tree.prog_type. :- import_module parse_tree.prog_util. :- import_module parse_tree.var_db. :- import_module parse_tree.var_table. :- import_module parse_tree.vartypes. :- import_module transform_hlds.inlining. :- import_module transform_hlds.intermod_order_pred_info. :- import_module assoc_list. :- import_module bool. :- import_module cord. :- import_module int. :- import_module list. :- import_module map. :- import_module maybe. :- import_module multi_map. :- import_module one_or_more. :- import_module one_or_more_map. :- import_module pair. :- import_module require. :- import_module set. :- import_module string. :- import_module term. :- import_module term_context. :- import_module term_subst. :- import_module varset. %---------------------------------------------------------------------------% :- type intermod_params ---> intermod_params( ip_maybe_process_local_preds :: maybe_process_local_preds, ip_maybe_collect_types :: maybe_collect_types, ip_maybe_deforest :: maybe_deforest, ip_inline_simple_threshold :: int, ip_higher_order_size_limit :: int ). :- type maybe_collect_types ---> do_not_collect_types ; do_collect_types. :- type maybe_process_local_preds ---> do_not_process_local_preds ; do_process_local_preds. %---------------------------------------------------------------------------% write_initial_opt_file(TmpOptStream, ModuleInfo, IntermodInfo, ParseTreePlainOpt, !IO) :- decide_what_to_opt_export(ModuleInfo, IntermodInfo), write_opt_file_initial(TmpOptStream, IntermodInfo, ParseTreePlainOpt, !IO). %---------------------------------------------------------------------------% % % Predicates to gather items to output to .opt file. % :- pred decide_what_to_opt_export(module_info::in, intermod_info::out) is det. decide_what_to_opt_export(ModuleInfo, !:IntermodInfo) :- module_info_get_globals(ModuleInfo, Globals), globals.get_opt_tuple(Globals, OptTuple), InlineSimpleThreshold = OptTuple ^ ot_intermod_inline_simple_threshold, HigherOrderSizeLimit = OptTuple ^ ot_higher_order_size_limit, Deforest = OptTuple ^ ot_deforest, module_info_get_valid_pred_ids(ModuleInfo, RealPredIds), module_info_get_assertion_table(ModuleInfo, AssertionTable), assertion_table_pred_ids(AssertionTable, AssertPredIds), PredIds = AssertPredIds ++ RealPredIds, Params = intermod_params(do_not_process_local_preds, do_collect_types, Deforest, InlineSimpleThreshold, HigherOrderSizeLimit), init_intermod_info(ModuleInfo, !:IntermodInfo), gather_opt_export_preds(Params, PredIds, !IntermodInfo), gather_opt_export_instances(!IntermodInfo), gather_opt_export_types(!IntermodInfo). %---------------------------------------------------------------------------% :- pred gather_opt_export_preds(intermod_params::in, list(pred_id)::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_preds(Params0, AllPredIds, !IntermodInfo) :- % First gather exported preds. gather_opt_export_preds_in_list(Params0, AllPredIds, !IntermodInfo), % Then gather preds used by exported preds (recursively). Params = Params0 ^ ip_maybe_process_local_preds := do_process_local_preds, set.init(ExtraExportedPreds0), gather_opt_export_preds_fixpoint(Params, ExtraExportedPreds0, !IntermodInfo). :- pred gather_opt_export_preds_fixpoint(intermod_params::in, set(pred_id)::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_preds_fixpoint(Params, ExtraExportedPreds0, !IntermodInfo) :- intermod_info_get_pred_decls(!.IntermodInfo, ExtraExportedPreds), NewlyExportedPreds = set.to_sorted_list( set.difference(ExtraExportedPreds, ExtraExportedPreds0)), ( NewlyExportedPreds = [] ; NewlyExportedPreds = [_ | _], gather_opt_export_preds_in_list(Params, NewlyExportedPreds, !IntermodInfo), gather_opt_export_preds_fixpoint(Params, ExtraExportedPreds, !IntermodInfo) ). :- pred gather_opt_export_preds_in_list(intermod_params::in, list(pred_id)::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_preds_in_list(_, [], !IntermodInfo). gather_opt_export_preds_in_list(Params, [PredId | PredIds], !IntermodInfo) :- intermod_info_get_module_info(!.IntermodInfo, ModuleInfo), module_info_pred_info(ModuleInfo, PredId, PredInfo), module_info_get_type_spec_info(ModuleInfo, TypeSpecInfo), TypeSpecInfo = type_spec_info(_, TypeSpecForcePreds, _, _), pred_info_get_clauses_info(PredInfo, ClausesInfo), ( if clauses_info_get_explicit_vartypes(ClausesInfo, ExplicitVarTypes), vartypes_is_empty(ExplicitVarTypes), should_opt_export_pred(ModuleInfo, PredId, PredInfo, Params, TypeSpecForcePreds) then SavedIntermodInfo = !.IntermodInfo, % Write a declaration to the `.opt' file for % `exported_to_submodules' predicates. intermod_add_pred(PredId, MayOptExportPred0, !IntermodInfo), clauses_info_get_clauses_rep(ClausesInfo, ClausesRep, _ItemNumbers), ( MayOptExportPred0 = may_opt_export_pred, get_clause_list_for_replacement(ClausesRep, Clauses), gather_entities_to_opt_export_in_clauses(Clauses, MayOptExportPred, !IntermodInfo) ; MayOptExportPred0 = may_not_opt_export_pred, MayOptExportPred = may_not_opt_export_pred ), ( MayOptExportPred = may_opt_export_pred, ( if pred_info_defn_has_foreign_proc(PredInfo) then % The foreign code of this predicate may refer to entities % in the foreign language that are defined in a foreign module % that is imported by a foreign_import_module declaration. intermod_info_set_need_foreign_import_modules(!IntermodInfo) else true ), intermod_info_get_pred_defns(!.IntermodInfo, PredDefns0), set.insert(PredId, PredDefns0, PredDefns), intermod_info_set_pred_defns(PredDefns, !IntermodInfo) ; MayOptExportPred = may_not_opt_export_pred, % Remove any items added for the clauses for this predicate. !:IntermodInfo = SavedIntermodInfo ) else true ), gather_opt_export_preds_in_list(Params, PredIds, !IntermodInfo). :- pred should_opt_export_pred(module_info::in, pred_id::in, pred_info::in, intermod_params::in, set(pred_id)::in) is semidet. should_opt_export_pred(ModuleInfo, PredId, PredInfo, Params, TypeSpecForcePreds) :- ProcessLocalPreds = Params ^ ip_maybe_process_local_preds, ( ProcessLocalPreds = do_not_process_local_preds, ( pred_info_is_exported(PredInfo) ; pred_info_is_exported_to_submodules(PredInfo) ) ; ProcessLocalPreds = do_process_local_preds, pred_info_get_status(PredInfo, pred_status(status_local)) ), ( % Allow all promises to be opt-exported. % (may_opt_export_pred should succeed for all promises.) pred_info_is_promise(PredInfo, _) ; may_opt_export_pred(PredId, PredInfo, TypeSpecForcePreds), opt_exporting_pred_is_likely_worthwhile(Params, ModuleInfo, PredId, PredInfo) ). :- pred opt_exporting_pred_is_likely_worthwhile(intermod_params::in, module_info::in, pred_id::in, pred_info::in) is semidet. opt_exporting_pred_is_likely_worthwhile(Params, ModuleInfo, PredId, PredInfo) :- pred_info_get_clauses_info(PredInfo, ClauseInfo), clauses_info_get_clauses_rep(ClauseInfo, ClausesRep, _ItemNumbers), get_clause_list_maybe_repeated(ClausesRep, Clauses), % At this point, the goal size includes some dummy unifications % HeadVar1 = X, HeadVar2 = Y, etc. which will be optimized away % later. To account for this, we add the arity to the size thresholds. Arity = pred_info_orig_arity(PredInfo), ( inlining.is_simple_clause_list(Clauses, Params ^ ip_inline_simple_threshold + Arity) ; pred_info_requested_inlining(PredInfo) ; % Mutable access preds should always be included in .opt files. pred_info_get_markers(PredInfo, Markers), check_marker(Markers, marker_mutable_access_pred) ; pred_has_a_higher_order_input_arg(ModuleInfo, PredInfo), clause_list_size(Clauses, GoalSize), GoalSize =< Params ^ ip_higher_order_size_limit + Arity ; Params ^ ip_maybe_deforest = deforest, % Double the inline-threshold since goals we want to deforest % will have at least two disjuncts. This allows one simple goal % in each disjunct. The disjunction adds one to the goal size, % hence the `+1'. DeforestThreshold = (Params ^ ip_inline_simple_threshold * 2) + 1, inlining.is_simple_clause_list(Clauses, DeforestThreshold + Arity), clause_list_is_deforestable(PredId, Clauses) ). :- pred may_opt_export_pred(pred_id::in, pred_info::in, set(pred_id)::in) is semidet. may_opt_export_pred(PredId, PredInfo, TypeSpecForcePreds) :- % Predicates with `class_method' markers contain class_method_call % goals which cannot be written to `.opt' files (they cannot be read % back in). They will be recreated in the importing module. pred_info_get_markers(PredInfo, Markers), not check_marker(Markers, marker_class_method), not check_marker(Markers, marker_class_instance_method), % Don't write stub clauses to `.opt' files. not check_marker(Markers, marker_stub), % Don't export builtins, since they will be recreated in the % importing module anyway. not is_unify_index_or_compare_pred(PredInfo), not pred_info_is_builtin(PredInfo), % These will be recreated in the importing module. not set.member(PredId, TypeSpecForcePreds), % Don't export non-inlinable predicates. not check_marker(Markers, marker_user_marked_no_inline), not check_marker(Markers, marker_mmc_marked_no_inline), % Don't export tabled predicates, since they are not inlinable. pred_info_get_proc_table(PredInfo, ProcTable), map.values(ProcTable, ProcInfos), list.all_true(proc_eval_method_is_normal, ProcInfos). :- pred proc_eval_method_is_normal(proc_info::in) is semidet. proc_eval_method_is_normal(ProcInfo) :- proc_info_get_eval_method(ProcInfo, eval_normal). :- pred gather_entities_to_opt_export_in_clauses(list(clause)::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_clauses([], may_opt_export_pred, !IntermodInfo). gather_entities_to_opt_export_in_clauses([Clause | Clauses], MayOptExportPred, !IntermodInfo) :- gather_entities_to_opt_export_in_goal(Clause ^ clause_body, MayOptExportPred1, !IntermodInfo), ( MayOptExportPred1 = may_opt_export_pred, gather_entities_to_opt_export_in_clauses(Clauses, MayOptExportPred, !IntermodInfo) ; MayOptExportPred1 = may_not_opt_export_pred, MayOptExportPred = may_not_opt_export_pred ). :- pred pred_has_a_higher_order_input_arg(module_info::in, pred_info::in) is semidet. pred_has_a_higher_order_input_arg(ModuleInfo, PredInfo) :- pred_info_get_proc_table(PredInfo, ProcTable), map.values(ProcTable, ProcInfos), list.find_first_match(proc_has_a_higher_order_input_arg(ModuleInfo), ProcInfos, _FirstProcInfoWithHoInput). :- pred proc_has_a_higher_order_input_arg(module_info::in, proc_info::in) is semidet. proc_has_a_higher_order_input_arg(ModuleInfo, ProcInfo) :- proc_info_get_headvars(ProcInfo, HeadVars), proc_info_get_argmodes(ProcInfo, ArgModes), proc_info_get_var_table(ProcInfo, VarTable), some_input_arg_is_higher_order(ModuleInfo, VarTable, HeadVars, ArgModes). :- pred some_input_arg_is_higher_order(module_info::in, var_table::in, list(prog_var)::in, list(mer_mode)::in) is semidet. some_input_arg_is_higher_order(ModuleInfo, VarTable, [HeadVar | HeadVars], [ArgMode | ArgModes]) :- ( if mode_is_input(ModuleInfo, ArgMode), lookup_var_type(VarTable, HeadVar, Type), classify_type(ModuleInfo, Type) = ctor_cat_higher_order then true else some_input_arg_is_higher_order(ModuleInfo, VarTable, HeadVars, ArgModes) ). % Rough guess: a goal is deforestable if it contains a single % top-level branched goal and is recursive. % :- pred clause_list_is_deforestable(pred_id::in, list(clause)::in) is semidet. clause_list_is_deforestable(PredId, Clauses) :- some [Clause1] ( list.member(Clause1, Clauses), Goal1 = Clause1 ^ clause_body, goal_calls_pred_id(Goal1, PredId) ), ( Clauses = [_, _ | _] ; Clauses = [Clause2], Goal2 = Clause2 ^ clause_body, goal_to_conj_list(Goal2, GoalList), goal_contains_one_branched_goal(GoalList) ). :- pred goal_contains_one_branched_goal(list(hlds_goal)::in) is semidet. goal_contains_one_branched_goal(GoalList) :- goal_contains_one_branched_goal_acc(GoalList, no). :- pred goal_contains_one_branched_goal_acc(list(hlds_goal)::in, bool::in) is semidet. goal_contains_one_branched_goal_acc([], yes). goal_contains_one_branched_goal_acc([Goal | Goals], FoundBranch0) :- Goal = hlds_goal(GoalExpr, _), ( goal_is_branched(GoalExpr), FoundBranch0 = no, FoundBranch = yes ; goal_expr_has_subgoals(GoalExpr) = does_not_have_subgoals, FoundBranch = FoundBranch0 ), goal_contains_one_branched_goal_acc(Goals, FoundBranch). % Go over the goal of an exported proc looking for proc decls, types, % insts and modes that we need to write to the optfile. % :- pred gather_entities_to_opt_export_in_goal(hlds_goal::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_goal(Goal, MayOptExportPred, !IntermodInfo) :- Goal = hlds_goal(GoalExpr, _GoalInfo), gather_entities_to_opt_export_in_goal_expr(GoalExpr, MayOptExportPred, !IntermodInfo). :- pred gather_entities_to_opt_export_in_goal_expr(hlds_goal_expr::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_goal_expr(GoalExpr, MayOptExportPred, !IntermodInfo) :- ( GoalExpr = unify(_LHSVar, RHS, _Mode, _Kind, _UnifyContext), % Export declarations for preds used in higher order pred constants % or function calls. gather_entities_to_opt_export_in_unify_rhs(RHS, MayOptExportPred, !IntermodInfo) ; GoalExpr = plain_call(PredId, _, _, _, _, _), % Ensure that the called predicate will be exported. intermod_add_pred(PredId, MayOptExportPred, !IntermodInfo) ; GoalExpr = generic_call(CallType, _, _, _, _), ( CallType = higher_order(_, _, _, _), MayOptExportPred = may_opt_export_pred ; CallType = class_method(_, _, _, _), MayOptExportPred = may_not_opt_export_pred ; CallType = event_call(_), MayOptExportPred = may_not_opt_export_pred ; CallType = cast(CastType), ( ( CastType = unsafe_type_cast ; CastType = unsafe_type_inst_cast ; CastType = equiv_type_cast ; CastType = exists_cast ), MayOptExportPred = may_not_opt_export_pred ; CastType = subtype_coerce, MayOptExportPred = may_opt_export_pred ) ) ; GoalExpr = call_foreign_proc(Attrs, _, _, _, _, _, _), % Inlineable exported pragma_foreign_code goals cannot use any % non-exported types, so we just write out the clauses. MaybeMayDuplicate = get_may_duplicate(Attrs), MaybeMayExportBody = get_may_export_body(Attrs), ( if ( MaybeMayDuplicate = yes(proc_may_not_duplicate) ; MaybeMayExportBody = yes(proc_may_not_export_body) ) then MayOptExportPred = may_not_opt_export_pred else MayOptExportPred = may_opt_export_pred ) ; GoalExpr = conj(_ConjType, Goals), gather_entities_to_opt_export_in_goals(Goals, MayOptExportPred, !IntermodInfo) ; GoalExpr = disj(Goals), gather_entities_to_opt_export_in_goals(Goals, MayOptExportPred, !IntermodInfo) ; GoalExpr = switch(_Var, _CanFail, Cases), gather_entities_to_opt_export_in_cases(Cases, MayOptExportPred, !IntermodInfo) ; GoalExpr = if_then_else(_Vars, Cond, Then, Else), gather_entities_to_opt_export_in_goal(Cond, MayOptExportPredCond, !IntermodInfo), gather_entities_to_opt_export_in_goal(Then, MayOptExportPredThen, !IntermodInfo), gather_entities_to_opt_export_in_goal(Else, MayOptExportPredElse, !IntermodInfo), ( if MayOptExportPredCond = may_opt_export_pred, MayOptExportPredThen = may_opt_export_pred, MayOptExportPredElse = may_opt_export_pred then MayOptExportPred = may_opt_export_pred else MayOptExportPred = may_not_opt_export_pred ) ; GoalExpr = negation(SubGoal), gather_entities_to_opt_export_in_goal(SubGoal, MayOptExportPred, !IntermodInfo) ; GoalExpr = scope(_Reason, SubGoal), % Mode analysis hasn't been run yet, so we don't know yet whether % from_ground_term_construct scopes actually satisfy their invariants, % specifically the invariant that say they contain no calls or % higher-order constants. We therefore cannot special-case them here. % % XXX Actually it wouldn't be hard to arrange to get this code to run % *after* mode analysis. gather_entities_to_opt_export_in_goal(SubGoal, MayOptExportPred, !IntermodInfo) ; GoalExpr = shorthand(ShortHand), ( ShortHand = atomic_goal(_GoalType, _Outer, _Inner, _MaybeOutputVars, MainGoal, OrElseGoals, _OrElseInners), gather_entities_to_opt_export_in_goal(MainGoal, MayOptExportPredMain, !IntermodInfo), gather_entities_to_opt_export_in_goals(OrElseGoals, MayOptExportPredOrElse, !IntermodInfo), ( if MayOptExportPredMain = may_opt_export_pred, MayOptExportPredOrElse = may_opt_export_pred then MayOptExportPred = may_opt_export_pred else MayOptExportPred = may_not_opt_export_pred ) ; ShortHand = try_goal(_MaybeIO, _ResultVar, _SubGoal), % hlds_out_goal.m does not write out `try' goals properly. MayOptExportPred = may_not_opt_export_pred ; ShortHand = bi_implication(_, _), % These should have been expanded out by now. unexpected($pred, "bi_implication") ) ). :- pred gather_entities_to_opt_export_in_goals(list(hlds_goal)::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_goals([], may_opt_export_pred, !IntermodInfo). gather_entities_to_opt_export_in_goals([Goal | Goals], !:MayOptExportPred, !IntermodInfo) :- gather_entities_to_opt_export_in_goal(Goal, !:MayOptExportPred, !IntermodInfo), ( !.MayOptExportPred = may_opt_export_pred, gather_entities_to_opt_export_in_goals(Goals, !:MayOptExportPred, !IntermodInfo) ; !.MayOptExportPred = may_not_opt_export_pred ). :- pred gather_entities_to_opt_export_in_cases(list(case)::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_cases([], may_opt_export_pred, !IntermodInfo). gather_entities_to_opt_export_in_cases([Case | Cases], !:MayOptExportPred, !IntermodInfo) :- Case = case(_MainConsId, _OtherConsIds, Goal), gather_entities_to_opt_export_in_goal(Goal, !:MayOptExportPred, !IntermodInfo), ( !.MayOptExportPred = may_opt_export_pred, gather_entities_to_opt_export_in_cases(Cases, !:MayOptExportPred, !IntermodInfo) ; !.MayOptExportPred = may_not_opt_export_pred ). %---------------------------------------------------------------------------% :- type may_opt_export_pred ---> may_not_opt_export_pred ; may_opt_export_pred. % intermod_add_pred/4 tries to do what ever is necessary to ensure that the % specified predicate will be exported, so that it can be called from % clauses in the `.opt' file. If it can't, then it returns % MayOptExportPred = may_not_opt_export_pred, % which will prevent the caller from being included in the `.opt' file. % % If a proc called within an exported proc is local, we need to add % a declaration for the called proc to the .opt file. If a proc called % within an exported proc is from a different module, we need to include % an `:- import_module' declaration to import that module in the `.opt' % file. % :- pred intermod_add_pred(pred_id::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. intermod_add_pred(PredId, MayOptExportPred, !IntermodInfo) :- ( if PredId = invalid_pred_id then % This will happen for type class instance methods defined using % the clause syntax. Currently we cannot handle intermodule % optimization of those. MayOptExportPred = may_not_opt_export_pred else intermod_do_add_pred(PredId, MayOptExportPred, !IntermodInfo) ). :- pred intermod_do_add_pred(pred_id::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. intermod_do_add_pred(PredId, MayOptExportPred, !IntermodInfo) :- intermod_info_get_module_info(!.IntermodInfo, ModuleInfo), module_info_pred_info(ModuleInfo, PredId, PredInfo), pred_info_get_status(PredInfo, PredStatus), pred_info_get_markers(PredInfo, Markers), ( if % Calling compiler-generated procedures is fine; we don't need % to output declarations for them to the `.opt' file, since they % will be recreated every time anyway. We don't want declarations % for predicates representing promises either. ( is_unify_index_or_compare_pred(PredInfo) ; pred_info_is_promise(PredInfo, _) ) then MayOptExportPred = may_opt_export_pred else if % Don't write the caller to the `.opt' file if it calls a pred % without mode or determinism decls, because then we would need % to include the mode decls for the callee in the `.opt' file and % (since writing the `.opt' file happens before mode inference) % we can't do that because we don't know what the modes are. % % XXX This prevents intermodule optimizations in such cases, % which is a pity. % % XXX Actually it wouldn't be hard to arrange to get this code to run % *after* mode analysis, so this restriction is likely to be % unnecessary. ( check_marker(Markers, marker_infer_modes) ; pred_info_get_proc_table(PredInfo, Procs), ProcIds = pred_info_all_procids(PredInfo), list.member(ProcId, ProcIds), map.lookup(Procs, ProcId, ProcInfo), proc_info_get_declared_determinism(ProcInfo, no) ) then MayOptExportPred = may_not_opt_export_pred else if % Goals which call impure predicates cannot be written due to % limitations in mode analysis. The problem is that only head % unifications are allowed to be reordered with impure goals. % For example, % % p(A::in, B::in, C::out) :- impure foo(A, B, C). % % becomes % % p(HeadVar1, HeadVar2, HeadVar3) :- % A = HeadVar1, B = HeadVar2, C = HeadVar3, % impure foo(A, B, C). % % In the clauses written to `.opt' files, the head unifications % are already expanded, and are expanded again when the `.opt' file % is read in. The `C = HeadVar3' unification cannot be reordered % with the impure goal, resulting in a mode error. Fixing this % in mode analysis would be tricky. % See tests/valid/impure_intermod.m. % % NOTE: the above restriction applies to user predicates. % For compiler generated mutable access predicates, we can ensure % that reordering is not necessary by construction, so it is safe % to include them in .opt files. pred_info_get_purity(PredInfo, purity_impure), not check_marker(Markers, marker_mutable_access_pred) then MayOptExportPred = may_not_opt_export_pred else if % If a pred whose code we are going to put in the .opt file calls % a predicate which is exported, then we do not need to do anything % special. ( PredStatus = pred_status(status_exported) ; PredStatus = pred_status(status_external(OldExternalStatus)), old_status_is_exported(OldExternalStatus) = yes ) then MayOptExportPred = may_opt_export_pred else if % Declarations for class methods will be recreated from the class % declaration in the `.opt' file. Declarations for local classes % are always written to the `.opt' file. pred_info_get_markers(PredInfo, Markers), check_marker(Markers, marker_class_method) then MayOptExportPred = may_opt_export_pred else if % If a pred whose code we are going to put in the `.opt' file calls % a predicate which is local to that module, then we need to put % the declaration for the called predicate in the `.opt' file. pred_status_to_write(PredStatus) = yes then MayOptExportPred = may_opt_export_pred, intermod_info_get_pred_decls(!.IntermodInfo, PredDecls0), set.insert(PredId, PredDecls0, PredDecls), intermod_info_set_pred_decls(PredDecls, !IntermodInfo) else if ( PredStatus = pred_status(status_imported(_)) ; PredStatus = pred_status(status_opt_imported) ) then % Imported pred - add import for module. MayOptExportPred = may_opt_export_pred, PredModule = pred_info_module(PredInfo), intermod_info_get_use_modules(!.IntermodInfo, Modules0), set.insert(PredModule, Modules0, Modules), intermod_info_set_use_modules(Modules, !IntermodInfo) else unexpected($pred, "unexpected status") ). % Resolve overloading and module qualify everything in a unify_rhs. % Fully module-qualify the right-hand-side of a unification. % For function calls and higher-order terms, call intermod_add_pred % so that the predicate or function will be exported if necessary. % :- pred gather_entities_to_opt_export_in_unify_rhs(unify_rhs::in, may_opt_export_pred::out, intermod_info::in, intermod_info::out) is det. gather_entities_to_opt_export_in_unify_rhs(RHS, MayOptExportPred, !IntermodInfo) :- ( RHS = rhs_var(_), MayOptExportPred = may_opt_export_pred ; RHS = rhs_lambda_goal(_Purity, _HOGroundness, _PorF, _EvalMethod, _NonLocals, _ArgVarsModes, _Detism, Goal), gather_entities_to_opt_export_in_goal(Goal, MayOptExportPred, !IntermodInfo) ; RHS = rhs_functor(Functor, _Exist, _Vars), % Is this a higher-order predicate or higher-order function term? ( if Functor = closure_cons(ShroudedPredProcId, _) then % Yes, the unification creates a higher-order term. % Make sure that the predicate/function is exported. proc(PredId, _) = unshroud_pred_proc_id(ShroudedPredProcId), intermod_add_pred(PredId, MayOptExportPred, !IntermodInfo) else % It is an ordinary constructor, or a constant of a builtin type, % so just leave it alone. % % Function calls and higher-order function applications % are transformed into ordinary calls and higher-order calls % by post_typecheck.m, so they cannot occur here. MayOptExportPred = may_opt_export_pred ) ). %---------------------------------------------------------------------------% :- pred gather_opt_export_instances(intermod_info::in, intermod_info::out) is det. gather_opt_export_instances(!IntermodInfo) :- intermod_info_get_module_info(!.IntermodInfo, ModuleInfo), module_info_get_instance_table(ModuleInfo, Instances), map.foldl(gather_opt_export_instances_in_class(ModuleInfo), Instances, !IntermodInfo). :- pred gather_opt_export_instances_in_class(module_info::in, class_id::in, list(hlds_instance_defn)::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_instances_in_class(ModuleInfo, ClassId, InstanceDefns, !IntermodInfo) :- list.foldl( gather_opt_export_instance_in_instance_defn(ModuleInfo, ClassId), InstanceDefns, !IntermodInfo). :- pred gather_opt_export_instance_in_instance_defn(module_info::in, class_id::in, hlds_instance_defn::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_instance_in_instance_defn(ModuleInfo, ClassId, InstanceDefn, !IntermodInfo) :- InstanceDefn = hlds_instance_defn(ModuleName, InstanceStatus, TVarSet, OriginalTypes, Types, InstanceConstraints, MaybeSubsumedContext, Proofs, InstanceBody0, MaybeMethodInfos, Context), DefinedThisModule = instance_status_defined_in_this_module(InstanceStatus), ( DefinedThisModule = yes, % The bodies are always stripped from instance declarations % before writing them to *.int* files, so the full instance % declaration should be written to the .opt file even for % exported instances, if this is possible. SavedIntermodInfo = !.IntermodInfo, ( InstanceBody0 = instance_body_concrete(Methods0), ( MaybeMethodInfos = yes(MethodInfos) ; MaybeMethodInfos = no, unexpected($pred, "method infos not filled in") ), AddMethodInfoToMap = ( pred(MI::in, Map0::in, Map::out) is det :- MethodName = MI ^ method_pred_name, proc(PredId, _) = MI ^ method_orig_proc, ( if map.insert(MethodName, PredId, Map0, Map1) then Map = Map1 else Map = Map0 ) ), list.foldl(AddMethodInfoToMap, MethodInfos, map.init, MethodNameToPredIdMap), list.map_foldl( intermod_qualify_instance_method(ModuleInfo, MethodNameToPredIdMap), Methods0, Methods, [], PredIds), list.map_foldl(intermod_add_pred, PredIds, MethodMayOptExportPreds, !IntermodInfo), ( if list.all_true(unify(may_opt_export_pred), MethodMayOptExportPreds) then InstanceBody = instance_body_concrete(Methods) else % Write an abstract instance declaration if any of the methods % cannot be written to the `.opt' file for any reason. InstanceBody = instance_body_abstract, % Do not write declarations for any of the methods if one % cannot be written. !:IntermodInfo = SavedIntermodInfo ) ; InstanceBody0 = instance_body_abstract, InstanceBody = InstanceBody0 ), ( if % Don't write an abstract instance declaration % if the declaration is already in the `.int' file. ( InstanceBody = instance_body_abstract => instance_status_is_exported(InstanceStatus) = no ) then InstanceDefnToWrite = hlds_instance_defn(ModuleName, InstanceStatus, TVarSet, OriginalTypes, Types, InstanceConstraints, MaybeSubsumedContext, Proofs, InstanceBody, MaybeMethodInfos, Context), intermod_info_get_instances(!.IntermodInfo, Instances0), Instances = [ClassId - InstanceDefnToWrite | Instances0], intermod_info_set_instances(Instances, !IntermodInfo) else true ) ; DefinedThisModule = no ). % Resolve overloading of instance methods before writing them % to the `.opt' file. % :- pred intermod_qualify_instance_method(module_info::in, map(pred_pf_name_arity, pred_id)::in, instance_method::in, instance_method::out, list(pred_id)::in, list(pred_id)::out) is det. intermod_qualify_instance_method(ModuleInfo, MethodNameToPredIdMap, InstanceMethod0, InstanceMethod, PredIds0, PredIds) :- InstanceMethod0 = instance_method(MethodName, InstanceMethodDefn0, MethodContext), MethodName = pred_pf_name_arity(PredOrFunc, _MethodSymName, MethodUserArity), map.lookup(MethodNameToPredIdMap, MethodName, MethodPredId), module_info_pred_info(ModuleInfo, MethodPredId, MethodPredInfo), pred_info_get_arg_types(MethodPredInfo, MethodTVarSet, MethodExistQTVars, MethodArgTypes), pred_info_get_external_type_params(MethodPredInfo, MethodExternalTypeParams), ( InstanceMethodDefn0 = instance_proc_def_name(InstanceMethodName0), PredOrFunc = pf_function, ( if find_func_matching_instance_method(ModuleInfo, InstanceMethodName0, MethodUserArity, MethodTVarSet, MethodExistQTVars, MethodArgTypes, MethodExternalTypeParams, MethodContext, MaybePredId, InstanceMethodName) then ( MaybePredId = yes(PredId), PredIds = [PredId | PredIds0] ; MaybePredId = no, PredIds = PredIds0 ), InstanceMethodDefn = instance_proc_def_name(InstanceMethodName) else % This will force intermod_add_pred to return % MayOptExportPred = may_not_opt_export_pred. PredId = invalid_pred_id, PredIds = [PredId | PredIds0], % We can just leave the method definition unchanged. InstanceMethodDefn = InstanceMethodDefn0 ) ; InstanceMethodDefn0 = instance_proc_def_name(InstanceMethodName0), PredOrFunc = pf_predicate, init_markers(Markers), resolve_pred_overloading(ModuleInfo, Markers, MethodTVarSet, MethodExistQTVars, MethodArgTypes, MethodExternalTypeParams, MethodContext, InstanceMethodName0, InstanceMethodName, PredId, _ResolveSpecs), % Any errors in _ResolveSpecs will be reported when a later compiler % invocation attempts to generate target language code for this module. PredIds = [PredId | PredIds0], InstanceMethodDefn = instance_proc_def_name(InstanceMethodName) ; InstanceMethodDefn0 = instance_proc_def_clauses(_ItemList), % XXX For methods defined using this syntax it is a little tricky % to write out the .opt files, so for now I've just disabled % intermodule optimization for type class instance declarations % using the new syntax. % % This will force intermod_add_pred to return % MayOptExportPred = may_not_opt_export_pred. PredId = invalid_pred_id, PredIds = [PredId | PredIds0], % We can just leave the method definition unchanged. InstanceMethodDefn = InstanceMethodDefn0 ), InstanceMethod = instance_method(MethodName, InstanceMethodDefn, MethodContext). % A `func(x/n) is y' method implementation can match an ordinary function, % a field access function or a constructor. For now, if there are multiple % possible matches, we don't write the instance method. % :- pred find_func_matching_instance_method(module_info::in, sym_name::in, user_arity::in, tvarset::in, existq_tvars::in, list(mer_type)::in, external_type_params::in, prog_context::in, maybe(pred_id)::out, sym_name::out) is semidet. find_func_matching_instance_method(ModuleInfo, InstanceMethodName0, MethodUserArity, MethodCallTVarSet, MethodCallExistQTVars, MethodCallArgTypes, MethodCallExternalTypeParams, MethodContext, MaybePredId, InstanceMethodName) :- module_info_get_ctor_field_table(ModuleInfo, CtorFieldTable), MethodUserArity = user_arity(MethodUserArityInt), ( if % XXX ARITY is_field_access_function_name can take user_arity % XXX ARITY is_field_access_function_name can return FieldDefns is_field_access_function_name(ModuleInfo, InstanceMethodName0, MethodUserArityInt, _, FieldName), map.search(CtorFieldTable, FieldName, FieldDefns) then TypeCtors0 = list.map( ( func(FieldDefn) = TypeCtor :- FieldDefn = hlds_ctor_field_defn(_, _, TypeCtor, _, _) ), FieldDefns) else TypeCtors0 = [] ), module_info_get_cons_table(ModuleInfo, Ctors), ( if ConsId = cons(InstanceMethodName0, MethodUserArityInt, cons_id_dummy_type_ctor), search_cons_table(Ctors, ConsId, MatchingConstructors) then TypeCtors1 = list.map( ( func(ConsDefn) = TypeCtor :- ConsDefn ^ cons_type_ctor = TypeCtor ), MatchingConstructors) else TypeCtors1 = [] ), TypeCtors = TypeCtors0 ++ TypeCtors1, module_info_get_predicate_table(ModuleInfo, PredicateTable), predicate_table_lookup_func_sym_arity(PredicateTable, may_be_partially_qualified, InstanceMethodName0, MethodUserArity, PredIds), ( if PredIds = [_ | _], find_matching_pred_id(ModuleInfo, PredIds, MethodCallTVarSet, MethodCallExistQTVars, MethodCallArgTypes, MethodCallExternalTypeParams, no, MethodContext, PredId, InstanceMethodFuncName, _ResolveSpecs) % Any errors in _ResolveSpecs will be reported when a later compiler % invocation attempts to generate target language code for this module. then TypeCtors = [], MaybePredId = yes(PredId), InstanceMethodName = InstanceMethodFuncName else TypeCtors = [TheTypeCtor], MaybePredId = no, TheTypeCtor = type_ctor(TypeCtorSymName, _), ( TypeCtorSymName = qualified(TypeModule, _), UnqualMethodName = unqualify_name(InstanceMethodName0), InstanceMethodName = qualified(TypeModule, UnqualMethodName) ; TypeCtorSymName = unqualified(_), unexpected($pred, "unqualified type_ctor in " ++ "hlds_cons_defn or hlds_ctor_field_defn") ) ). %---------------------------------------------------------------------------% :- pred gather_opt_export_types(intermod_info::in, intermod_info::out) is det. gather_opt_export_types(!IntermodInfo) :- intermod_info_get_module_info(!.IntermodInfo, ModuleInfo), module_info_get_type_table(ModuleInfo, TypeTable), foldl_over_type_ctor_defns(gather_opt_export_types_in_type_defn, TypeTable, !IntermodInfo). :- pred gather_opt_export_types_in_type_defn(type_ctor::in, hlds_type_defn::in, intermod_info::in, intermod_info::out) is det. gather_opt_export_types_in_type_defn(TypeCtor, TypeDefn0, !IntermodInfo) :- intermod_info_get_module_info(!.IntermodInfo, ModuleInfo), module_info_get_name(ModuleInfo, ModuleName), ( if should_opt_export_type_defn(ModuleName, TypeCtor, TypeDefn0) then hlds_data.get_type_defn_body(TypeDefn0, TypeBody0), ( TypeBody0 = hlds_du_type(TypeBodyDu0), TypeBodyDu0 = type_body_du(Ctors, MaybeSuperType, MaybeUserEqComp0, MaybeRepn, MaybeForeign0), module_info_get_globals(ModuleInfo, Globals), globals.get_target(Globals, Target), % Note that we don't resolve overloading for the definitions % which won't be used on this back-end, because their unification % and comparison predicates have not been typechecked. They are % only written to the `.opt' it can be handy when building % against a workspace for the other definitions to be present % (e.g. when testing compiling a module to IL when the workspace % was compiled to C). % XXX The above sentence doesn't make sense, and never did % (even in the first CVS version in which it appears). ( if MaybeForeign0 = yes(ForeignTypeBody0), have_foreign_type_for_backend(Target, ForeignTypeBody0, yes) then % The foreign type may be defined in one of the foreign % modules we import. intermod_info_set_need_foreign_import_modules(!IntermodInfo), resolve_foreign_type_body_overloading(ModuleInfo, TypeCtor, ForeignTypeBody0, ForeignTypeBody, !IntermodInfo), MaybeForeign = yes(ForeignTypeBody), MaybeUserEqComp = MaybeUserEqComp0 else resolve_unify_compare_overloading(ModuleInfo, TypeCtor, MaybeUserEqComp0, MaybeUserEqComp, !IntermodInfo), MaybeForeign = MaybeForeign0 ), TypeBodyDu = type_body_du(Ctors, MaybeSuperType, MaybeUserEqComp, MaybeRepn, MaybeForeign), TypeBody = hlds_du_type(TypeBodyDu), hlds_data.set_type_defn_body(TypeBody, TypeDefn0, TypeDefn) ; TypeBody0 = hlds_foreign_type(ForeignTypeBody0), % The foreign type may be defined in one of the foreign % modules we import. intermod_info_set_need_foreign_import_modules(!IntermodInfo), resolve_foreign_type_body_overloading(ModuleInfo, TypeCtor, ForeignTypeBody0, ForeignTypeBody, !IntermodInfo), TypeBody = hlds_foreign_type(ForeignTypeBody), hlds_data.set_type_defn_body(TypeBody, TypeDefn0, TypeDefn) ; ( TypeBody0 = hlds_eqv_type(_) ; TypeBody0 = hlds_solver_type(_) ; TypeBody0 = hlds_abstract_type(_) ), TypeDefn = TypeDefn0 ), intermod_info_get_types(!.IntermodInfo, Types0), intermod_info_set_types([TypeCtor - TypeDefn | Types0], !IntermodInfo) else true ). :- pred resolve_foreign_type_body_overloading(module_info::in, type_ctor::in, foreign_type_body::in, foreign_type_body::out, intermod_info::in, intermod_info::out) is det. resolve_foreign_type_body_overloading(ModuleInfo, TypeCtor, ForeignTypeBody0, ForeignTypeBody, !IntermodInfo) :- ForeignTypeBody0 = foreign_type_body(MaybeC0, MaybeJava0, MaybeCSharp0), module_info_get_globals(ModuleInfo, Globals), globals.get_target(Globals, Target), % Note that we don't resolve overloading for the foreign definitions % which won't be used on this back-end, because their unification and % comparison predicates have not been typechecked. They are only written % to the `.opt' it can be handy when building against a workspace % for the other definitions to be present (e.g. when testing compiling % a module to IL when the workspace was compiled to C). ( Target = target_c, resolve_foreign_type_body_overloading_2(ModuleInfo, TypeCtor, MaybeC0, MaybeC, !IntermodInfo) ; ( Target = target_csharp ; Target = target_java ), MaybeC = MaybeC0 ), ( Target = target_csharp, resolve_foreign_type_body_overloading_2(ModuleInfo, TypeCtor, MaybeCSharp0, MaybeCSharp, !IntermodInfo) ; ( Target = target_c ; Target = target_java ), MaybeCSharp = MaybeCSharp0 ), ( Target = target_java, resolve_foreign_type_body_overloading_2(ModuleInfo, TypeCtor, MaybeJava0, MaybeJava, !IntermodInfo) ; ( Target = target_c ; Target = target_csharp ), MaybeJava = MaybeJava0 ), ForeignTypeBody = foreign_type_body(MaybeC, MaybeJava, MaybeCSharp). :- pred resolve_foreign_type_body_overloading_2(module_info::in, type_ctor::in, foreign_type_lang_body(T)::in, foreign_type_lang_body(T)::out, intermod_info::in, intermod_info::out) is det. resolve_foreign_type_body_overloading_2(ModuleInfo, TypeCtor, MaybeForeignTypeLangData0, MaybeForeignTypeLangData, !IntermodInfo) :- ( MaybeForeignTypeLangData0 = no, MaybeForeignTypeLangData = no ; MaybeForeignTypeLangData0 = yes(type_details_foreign(Body, MaybeUserEqComp0, Assertions)), resolve_unify_compare_overloading(ModuleInfo, TypeCtor, MaybeUserEqComp0, MaybeUserEqComp, !IntermodInfo), MaybeForeignTypeLangData = yes(type_details_foreign(Body, MaybeUserEqComp, Assertions)) ). :- pred resolve_unify_compare_overloading(module_info::in, type_ctor::in, maybe_canonical::in, maybe_canonical::out, intermod_info::in, intermod_info::out) is det. resolve_unify_compare_overloading(ModuleInfo, TypeCtor, MaybeCanonical0, MaybeCanonical, !IntermodInfo) :- ( MaybeCanonical0 = canon, MaybeCanonical = MaybeCanonical0 ; MaybeCanonical0 = noncanon(NonCanonical0), ( ( NonCanonical0 = noncanon_abstract(_IsSolverType) ; NonCanonical0 = noncanon_subtype ), MaybeCanonical = MaybeCanonical0 ; NonCanonical0 = noncanon_uni_cmp(Uni0, Cmp0), resolve_user_special_pred_overloading(ModuleInfo, spec_pred_unify, TypeCtor, Uni0, Uni, !IntermodInfo), resolve_user_special_pred_overloading(ModuleInfo, spec_pred_compare, TypeCtor, Cmp0, Cmp, !IntermodInfo), NonCanonical = noncanon_uni_cmp(Uni, Cmp), MaybeCanonical = noncanon(NonCanonical) ; NonCanonical0 = noncanon_uni_only(Uni0), resolve_user_special_pred_overloading(ModuleInfo, spec_pred_unify, TypeCtor, Uni0, Uni, !IntermodInfo), NonCanonical = noncanon_uni_only(Uni), MaybeCanonical = noncanon(NonCanonical) ; NonCanonical0 = noncanon_cmp_only(Cmp0), resolve_user_special_pred_overloading(ModuleInfo, spec_pred_compare, TypeCtor, Cmp0, Cmp, !IntermodInfo), NonCanonical = noncanon_cmp_only(Cmp), MaybeCanonical = noncanon(NonCanonical) ) ). :- pred resolve_user_special_pred_overloading(module_info::in, special_pred_id::in, type_ctor::in, sym_name::in, sym_name::out, intermod_info::in, intermod_info::out) is det. resolve_user_special_pred_overloading(ModuleInfo, SpecialId, TypeCtor, Pred0, Pred, !IntermodInfo) :- module_info_get_special_pred_maps(ModuleInfo, SpecialPredMaps), lookup_special_pred_maps(SpecialPredMaps, SpecialId, TypeCtor, SpecialPredId), module_info_pred_info(ModuleInfo, SpecialPredId, SpecialPredInfo), pred_info_get_arg_types(SpecialPredInfo, TVarSet, ExistQVars, ArgTypes), pred_info_get_external_type_params(SpecialPredInfo, ExternalTypeParams), init_markers(Markers0), add_marker(marker_calls_are_fully_qualified, Markers0, Markers), pred_info_get_context(SpecialPredInfo, Context), resolve_pred_overloading(ModuleInfo, Markers, TVarSet, ExistQVars, ArgTypes, ExternalTypeParams, Context, Pred0, Pred, UserEqPredId, _ResolveSpecs), % Any errors in _ResolveSpecs will be reported when a later compiler % invocation attempts to generate target language code for this module. intermod_add_pred(UserEqPredId, _, !IntermodInfo). :- pred should_opt_export_type_defn(module_name::in, type_ctor::in, hlds_type_defn::in) is semidet. should_opt_export_type_defn(ModuleName, TypeCtor, TypeDefn) :- hlds_data.get_type_defn_status(TypeDefn, TypeStatus), TypeCtor = type_ctor(Name, _Arity), Name = qualified(ModuleName, _), type_status_to_write(TypeStatus) = yes. %---------------------------------------------------------------------------% % Output module imports, types, modes, insts and predicates. % :- pred write_opt_file_initial(io.text_output_stream::in, intermod_info::in, parse_tree_plain_opt::out, io::di, io::uo) is det. write_opt_file_initial(Stream, IntermodInfo, ParseTreePlainOpt, !IO) :- intermod_info_get_module_info(IntermodInfo, ModuleInfo), module_info_get_name(ModuleInfo, ModuleName), ModuleNameStr = mercury_bracketed_sym_name_to_string(ModuleName), io.format(Stream, ":- module %s.\n", [s(ModuleNameStr)], !IO), intermod_info_get_pred_decls(IntermodInfo, PredDecls), intermod_info_get_pred_defns(IntermodInfo, PredDefns), intermod_info_get_instances(IntermodInfo, Instances), ( if % If none of these item types need writing, nothing else % needs to be written. set.is_empty(PredDecls), set.is_empty(PredDefns), Instances = [], module_info_get_type_table(ModuleInfo, TypeTable), get_all_type_ctor_defns(TypeTable, TypeCtorsDefns), some_type_needs_to_be_written(TypeCtorsDefns, no) then ParseTreePlainOpt = parse_tree_plain_opt(ModuleName, dummy_context, map.init, set.init, [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []) else write_opt_file_initial_body(Stream, IntermodInfo, ParseTreePlainOpt, !IO) ). :- pred some_type_needs_to_be_written( assoc_list(type_ctor, hlds_type_defn)::in, bool::out) is det. some_type_needs_to_be_written([], no). some_type_needs_to_be_written([_ - TypeDefn | TypeCtorDefns], NeedWrite) :- hlds_data.get_type_defn_status(TypeDefn, TypeStatus), ( if ( TypeStatus = type_status(status_abstract_exported) ; TypeStatus = type_status(status_exported_to_submodules) ) then NeedWrite = yes else some_type_needs_to_be_written(TypeCtorDefns, NeedWrite) ). :- pred write_opt_file_initial_body(io.text_output_stream::in, intermod_info::in, parse_tree_plain_opt::out, io::di, io::uo) is det. write_opt_file_initial_body(Stream, IntermodInfo, ParseTreePlainOpt, !IO) :- IntermodInfo = intermod_info(ModuleInfo, _, WriteDeclPredIdSet, WriteDefnPredIdSet, InstanceDefns, Types, NeedFIMs), set.to_sorted_list(WriteDeclPredIdSet, WriteDeclPredIds), set.to_sorted_list(WriteDefnPredIdSet, WriteDefnPredIds), module_info_get_avail_module_map(ModuleInfo, AvailModuleMap), % XXX CLEANUP We could and should reduce AvailModules to the set of modules % that are *actually needed* by the items being written. % XXX CLEANUP And even if builtin.m and/or private_builtin.m is needed % by an item, we *still* shouldn't include them, since the importing % module will import and use them respectively anyway. map.keys(AvailModuleMap, UsedModuleNames), AddToUseMap = ( pred(MN::in, UM0::in, UM::out) is det :- % We don't have a context for any use_module declaration % of this module (since it may have a import_module declaration % instead), which is why we specify a dummy context. % However, these contexts are used only when the .opt file % is read in, not when it is being generated. one_or_more_map.add(MN, dummy_context, UM0, UM) ), list.foldl(AddToUseMap, UsedModuleNames, one_or_more_map.init, UseMap), ( NeedFIMs = do_need_foreign_import_modules, module_info_get_c_j_cs_fims(ModuleInfo, CJCsFIMs), FIMSpecsSet = get_all_fim_specs(CJCsFIMs), FIMSpecs = set.to_sorted_list(FIMSpecsSet) ; NeedFIMs = do_not_need_foreign_import_modules, set.init(FIMSpecsSet), FIMSpecs = [] ), module_info_get_globals(ModuleInfo, Globals), OutInfo0 = init_hlds_out_info(Globals, output_mercury), % We don't want to write line numbers from the source file to .opt files, % because that causes spurious changes to the .opt files % when you make trivial changes (e.g. add comments) to the source files. MercInfo0 = OutInfo0 ^ hoi_merc_out_info, MercInfo = merc_out_info_disable_line_numbers(MercInfo0), OutInfo = OutInfo0 ^ hoi_merc_out_info := MercInfo, % Disable verbose dumping of clauses. OutInfoForPreds = OutInfo ^ hoi_dump_hlds_options := "", intermod_gather_types(Types, TypeDefns, ForeignEnums), intermod_gather_insts(ModuleInfo, InstDefns), intermod_gather_modes(ModuleInfo, ModeDefns), intermod_gather_classes(ModuleInfo, TypeClasses), intermod_gather_instances(InstanceDefns, Instances), list.foldl(mercury_output_module_decl(Stream, "use_module"), UsedModuleNames, !IO), maybe_write_block_start_blank_line(Stream, FIMSpecs, !IO), list.foldl(mercury_output_fim_spec(Stream), FIMSpecs, !IO), maybe_write_block_start_blank_line(Stream, TypeDefns, !IO), list.foldl(mercury_output_item_type_defn(MercInfo, Stream), TypeDefns, !IO), maybe_write_block_start_blank_line(Stream, ForeignEnums, !IO), list.foldl(mercury_format_item_foreign_enum(MercInfo, Stream), ForeignEnums, !IO), maybe_write_block_start_blank_line(Stream, InstDefns, !IO), list.foldl(mercury_output_item_inst_defn(MercInfo, Stream), InstDefns, !IO), maybe_write_block_start_blank_line(Stream, ModeDefns, !IO), list.foldl(mercury_output_item_mode_defn(MercInfo, Stream), ModeDefns, !IO), maybe_write_block_start_blank_line(Stream, TypeClasses, !IO), list.foldl(mercury_output_item_typeclass(MercInfo, Stream), TypeClasses, !IO), maybe_write_block_start_blank_line(Stream, Instances, !IO), list.foldl(mercury_output_item_instance(MercInfo, Stream), Instances, !IO), generate_order_pred_infos(ModuleInfo, WriteDeclPredIds, DeclOrderPredInfos), generate_order_pred_infos(ModuleInfo, WriteDefnPredIds, DefnOrderPredInfos), PredMarkerPragmasCord0 = cord.init, ( DeclOrderPredInfos = [], PredDecls = [], ModeDecls = [], PredMarkerPragmasCord1 = PredMarkerPragmasCord0, TypeSpecPragmas = [] ; DeclOrderPredInfos = [_ | _], io.nl(Stream, !IO), intermod_write_pred_decls(MercInfo, Stream, ModuleInfo, DeclOrderPredInfos, cord.init, PredDeclsCord, cord.init, ModeDeclsCord, PredMarkerPragmasCord0, PredMarkerPragmasCord1, cord.init, TypeSpecPragmasCord, !IO), PredDecls = cord.list(PredDeclsCord), ModeDecls = cord.list(ModeDeclsCord), TypeSpecPragmas = list.map(wrap_dummy_pragma_item, cord.list(TypeSpecPragmasCord)) ), % Each of these writes a newline at the start. intermod_write_pred_defns(OutInfoForPreds, Stream, ModuleInfo, DefnOrderPredInfos, PredMarkerPragmasCord1, PredMarkerPragmasCord, !IO), PredMarkerPragmas = list.map(wrap_dummy_pragma_item, cord.list(PredMarkerPragmasCord)), Clauses = [], ForeignProcs = [], % XXX CLEANUP This *may* be a lie, in that some of the predicates we have % written out above *may* have goal_type_promise. However, until % we switch over completely to creating .opt files purely by building up % and then writing out a parse_tree_plain_opt, this shouldn't matter. Promises = [], module_info_get_name(ModuleInfo, ModuleName), ParseTreePlainOpt = parse_tree_plain_opt(ModuleName, dummy_context, UseMap, FIMSpecsSet, TypeDefns, ForeignEnums, InstDefns, ModeDefns, TypeClasses, Instances, PredDecls, ModeDecls, Clauses, ForeignProcs, Promises, PredMarkerPragmas, TypeSpecPragmas, [], [], [], [], [], [], [], []). :- type maybe_first ---> is_not_first ; is_first. %---------------------------------------------------------------------------% :- pred intermod_gather_types(assoc_list(type_ctor, hlds_type_defn)::in, list(item_type_defn_info)::out, list(item_foreign_enum_info)::out) is det. intermod_gather_types(Types, TypeDefns, ForeignEnums) :- list.sort(Types, SortedTypes), list.foldl2(intermod_gather_type, SortedTypes, cord.init, TypeDefnsCord, cord.init, ForeignEnumsCord), TypeDefns = cord.list(TypeDefnsCord), ForeignEnums = cord.list(ForeignEnumsCord). :- pred intermod_gather_type(pair(type_ctor, hlds_type_defn)::in, cord(item_type_defn_info)::in, cord(item_type_defn_info)::out, cord(item_foreign_enum_info)::in, cord(item_foreign_enum_info)::out) is det. intermod_gather_type(TypeCtor - TypeDefn, !TypeDefnsCord, !ForeignEnumsCord) :- hlds_data.get_type_defn_tvarset(TypeDefn, TVarSet), hlds_data.get_type_defn_tparams(TypeDefn, TypeParams), hlds_data.get_type_defn_body(TypeDefn, Body), hlds_data.get_type_defn_context(TypeDefn, Context), TypeCtor = type_ctor(TypeSymName, _Arity), ( Body = hlds_du_type(TypeBodyDu), TypeBodyDu = type_body_du(Ctors, MaybeSubType, MaybeCanon, MaybeRepnA, MaybeForeignTypeBody), ( MaybeRepnA = no, unexpected($pred, "MaybeRepnA = no") ; MaybeRepnA = yes(RepnA), MaybeDirectArgCtors = RepnA ^ dur_direct_arg_ctors ), ( MaybeSubType = subtype_of(SuperType), % TypeCtor may be noncanonical, and MaybeDirectArgCtors may be % nonempty, but any reader of the .opt file has to find out % both those facts from the base type of this subtype. DetailsSub = type_details_sub(SuperType, Ctors), TypeBody = parse_tree_sub_type(DetailsSub) ; MaybeSubType = not_a_subtype, % XXX TYPE_REPN We should output information about any direct args % as a separate type_repn item. DetailsDu = type_details_du(Ctors, MaybeCanon, MaybeDirectArgCtors), TypeBody = parse_tree_du_type(DetailsDu) ) ; Body = hlds_eqv_type(EqvType), TypeBody = parse_tree_eqv_type(type_details_eqv(EqvType)), MaybeForeignTypeBody = no ; Body = hlds_abstract_type(Details), TypeBody = parse_tree_abstract_type(Details), MaybeForeignTypeBody = no ; Body = hlds_foreign_type(ForeignTypeBody0), TypeBody = parse_tree_abstract_type(abstract_type_general), MaybeForeignTypeBody = yes(ForeignTypeBody0) ; Body = hlds_solver_type(DetailsSolver), TypeBody = parse_tree_solver_type(DetailsSolver), MaybeForeignTypeBody = no ), MainItemTypeDefn = item_type_defn_info(TypeSymName, TypeParams, TypeBody, TVarSet, Context, item_no_seq_num), cord.snoc(MainItemTypeDefn, !TypeDefnsCord), ( MaybeForeignTypeBody = no ; MaybeForeignTypeBody = yes(ForeignTypeBody), ForeignTypeBody = foreign_type_body(MaybeC, MaybeJava, MaybeCsharp), maybe_acc_foreign_type_defn_info(TypeSymName, TypeParams, TVarSet, Context, (func(FT) = c(FT)), MaybeC, !TypeDefnsCord), maybe_acc_foreign_type_defn_info(TypeSymName, TypeParams, TVarSet, Context, (func(FT) = java(FT)), MaybeJava, !TypeDefnsCord), maybe_acc_foreign_type_defn_info(TypeSymName, TypeParams, TVarSet, Context, (func(FT) = csharp(FT)), MaybeCsharp, !TypeDefnsCord) ), ( if Body = hlds_du_type(type_body_du(_, _, _, MaybeRepnB, _)), MaybeRepnB = yes(RepnB), RepnB = du_type_repn(CtorRepns, _, _, DuTypeKind, _), DuTypeKind = du_type_kind_foreign_enum(Lang) then % XXX TYPE_REPN This code puts into the .opt file the foreign enum % specification for this type_ctor ONLY for the foreign language % used by the current target platform. We cannot fix this until % we preserve the same information for all the other foreign languages % as well. list.foldl(gather_foreign_enum_value_pair, CtorRepns, [], RevForeignEnumVals), list.reverse(RevForeignEnumVals, ForeignEnumVals), ( ForeignEnumVals = [] % This can only happen if the type has no function symbols. % which should have been detected and reported by now. ; ForeignEnumVals = [HeadForeignEnumVal | TailForeignEnumVals], OoMForeignEnumVals = one_or_more(HeadForeignEnumVal, TailForeignEnumVals), ForeignEnum = item_foreign_enum_info(Lang, TypeCtor, OoMForeignEnumVals, Context, item_no_seq_num), cord.snoc(ForeignEnum, !ForeignEnumsCord) ) else true ). :- pred maybe_acc_foreign_type_defn_info(sym_name::in, list(type_param)::in, tvarset::in, prog_context::in, (func(T) = generic_language_foreign_type)::in, maybe(type_details_foreign(T))::in, cord(item_type_defn_info)::in, cord(item_type_defn_info)::out) is det. maybe_acc_foreign_type_defn_info(TypeSymName, TypeParams, TVarSet, Context, MakeGeneric, MaybeDetails, !TypeDefnsCord) :- ( MaybeDetails = no ; MaybeDetails = yes(Details), Details = type_details_foreign(LangForeignType, MaybeUserEqComp, Assertions), DetailsForeign = type_details_foreign(MakeGeneric(LangForeignType), MaybeUserEqComp, Assertions), ItemTypeDefn = item_type_defn_info(TypeSymName, TypeParams, parse_tree_foreign_type(DetailsForeign), TVarSet, Context, item_no_seq_num), cord.snoc(ItemTypeDefn, !TypeDefnsCord) ). :- pred gather_foreign_enum_value_pair(constructor_repn::in, assoc_list(sym_name, string)::in, assoc_list(sym_name, string)::out) is det. gather_foreign_enum_value_pair(CtorRepn, !RevValues) :- CtorRepn = ctor_repn(_, _, SymName, Tag, _, Arity, _), expect(unify(Arity, 0), $pred, "Arity != 0"), ( if Tag = foreign_tag(_ForeignLang, ForeignTag) then !:RevValues = [SymName - ForeignTag | !.RevValues] else unexpected($pred, "expected foreign tag") ). %---------------------------------------------------------------------------% :- pred intermod_gather_insts(module_info::in, list(item_inst_defn_info)::out) is det. intermod_gather_insts(ModuleInfo, InstDefns) :- module_info_get_name(ModuleInfo, ModuleName), module_info_get_inst_table(ModuleInfo, Insts), inst_table_get_user_insts(Insts, UserInstMap), map.foldl(intermod_gather_inst(ModuleName), UserInstMap, cord.init, InstDefnsCord), InstDefns = cord.list(InstDefnsCord). :- pred intermod_gather_inst(module_name::in, inst_ctor::in, hlds_inst_defn::in, cord(item_inst_defn_info)::in, cord(item_inst_defn_info)::out) is det. intermod_gather_inst(ModuleName, InstCtor, InstDefn, !InstDefnsCord) :- InstCtor = inst_ctor(SymName, _Arity), InstDefn = hlds_inst_defn(VarSet, Args, Inst, IFTC, Context, InstStatus), ( if SymName = qualified(ModuleName, _), inst_status_to_write(InstStatus) = yes then ( IFTC = iftc_applicable_declared(ForTypeCtor), MaybeForTypeCtor = yes(ForTypeCtor) ; ( IFTC = iftc_applicable_known(_) ; IFTC = iftc_applicable_not_known ; IFTC = iftc_applicable_error ; IFTC = iftc_not_applicable ), MaybeForTypeCtor = no ), ItemInstDefn = item_inst_defn_info(SymName, Args, MaybeForTypeCtor, nonabstract_inst_defn(Inst), VarSet, Context, item_no_seq_num), cord.snoc(ItemInstDefn, !InstDefnsCord) else true ). %---------------------------------------------------------------------------% :- pred intermod_gather_modes(module_info::in, list(item_mode_defn_info)::out) is det. intermod_gather_modes(ModuleInfo, ModeDefns) :- module_info_get_name(ModuleInfo, ModuleName), module_info_get_mode_table(ModuleInfo, Modes), mode_table_get_mode_defns(Modes, ModeDefnMap), map.foldl(intermod_gather_mode(ModuleName), ModeDefnMap, cord.init, ModeDefnsCord), ModeDefns = cord.list(ModeDefnsCord). :- pred intermod_gather_mode(module_name::in, mode_ctor::in, hlds_mode_defn::in, cord(item_mode_defn_info)::in, cord(item_mode_defn_info)::out) is det. intermod_gather_mode(ModuleName, ModeCtor, ModeDefn, !ModeDefnsCord) :- ModeCtor = mode_ctor(SymName, _Arity), ModeDefn = hlds_mode_defn(VarSet, Args, hlds_mode_body(Mode), Context, ModeStatus), ( if SymName = qualified(ModuleName, _), mode_status_to_write(ModeStatus) = yes then MaybeAbstractModeDefn = nonabstract_mode_defn(eqv_mode(Mode)), ItemModeDefn = item_mode_defn_info(SymName, Args, MaybeAbstractModeDefn, VarSet, Context, item_no_seq_num), cord.snoc(ItemModeDefn, !ModeDefnsCord) else true ). %---------------------------------------------------------------------------% :- pred intermod_gather_classes(module_info::in, list(item_typeclass_info)::out) is det. intermod_gather_classes(ModuleInfo, TypeClasses) :- module_info_get_name(ModuleInfo, ModuleName), module_info_get_class_table(ModuleInfo, ClassDefnMap), map.foldl(intermod_gather_class(ModuleName), ClassDefnMap, cord.init, TypeClassesCord), TypeClasses = cord.list(TypeClassesCord). :- pred intermod_gather_class(module_name::in, class_id::in, hlds_class_defn::in, cord(item_typeclass_info)::in, cord(item_typeclass_info)::out) is det. intermod_gather_class(ModuleName, ClassId, ClassDefn, !TypeClassesCord) :- ClassDefn = hlds_class_defn(TypeClassStatus, TVarSet, _Kinds, TVars, Constraints, HLDSFunDeps, _Ancestors, InstanceBody, _MaybeMethodInfos, Context, _HasBadDefn), ClassId = class_id(QualifiedClassName, _), ( if QualifiedClassName = qualified(ModuleName, _), typeclass_status_to_write(TypeClassStatus) = yes then FunDeps = list.map(unmake_hlds_class_fundep(TVars), HLDSFunDeps), ItemTypeClass = item_typeclass_info(QualifiedClassName, TVars, Constraints, FunDeps, InstanceBody, TVarSet, Context, item_no_seq_num), cord.snoc(ItemTypeClass, !TypeClassesCord) else true ). :- func unmake_hlds_class_fundep(list(tvar), hlds_class_fundep) = prog_fundep. unmake_hlds_class_fundep(TVars, HLDSFunDep) = ParseTreeFunDep :- HLDSFunDep = fundep(DomainArgPosns, RangeArgPosns), DomainTVars = unmake_hlds_class_fundep_arg_posns(TVars, DomainArgPosns), RangeTVars = unmake_hlds_class_fundep_arg_posns(TVars, RangeArgPosns), ParseTreeFunDep = fundep(DomainTVars, RangeTVars). :- func unmake_hlds_class_fundep_arg_posns(list(tvar), set(hlds_class_argpos)) = list(tvar). unmake_hlds_class_fundep_arg_posns(TVars, ArgPosns) = ArgTVars :- ArgTVarsSet = set.map(list.det_index1(TVars), ArgPosns), set.to_sorted_list(ArgTVarsSet, ArgTVars). %---------------------------------------------------------------------------% :- pred intermod_gather_instances(assoc_list(class_id, hlds_instance_defn)::in, list(item_instance_info)::out) is det. intermod_gather_instances(InstanceDefns, Instances) :- list.sort(InstanceDefns, SortedInstanceDefns), list.foldl(intermod_gather_instance, SortedInstanceDefns, cord.init, InstancesCord), Instances = cord.list(InstancesCord). :- pred intermod_gather_instance(pair(class_id, hlds_instance_defn)::in, cord(item_instance_info)::in, cord(item_instance_info)::out) is det. intermod_gather_instance(ClassId - InstanceDefn, !InstancesCord) :- InstanceDefn = hlds_instance_defn(ModuleName, _, TVarSet, OriginalTypes, Types, Constraints, _, _, Body, _, Context), ClassId = class_id(ClassName, _), ItemInstance = item_instance_info(ClassName, Types, OriginalTypes, Constraints, Body, TVarSet, ModuleName, Context, item_no_seq_num), cord.snoc(ItemInstance, !InstancesCord). %---------------------------------------------------------------------------% % We need to write all the declarations for local predicates so % the procedure labels for the C code are calculated correctly. % :- pred intermod_write_pred_decls(merc_out_info::in, io.text_output_stream::in, module_info::in, list(order_pred_info)::in, cord(item_pred_decl_info)::in, cord(item_pred_decl_info)::out, cord(item_mode_decl_info)::in, cord(item_mode_decl_info)::out, cord(pragma_info_pred_marker)::in, cord(pragma_info_pred_marker)::out, cord(pragma_info_type_spec)::in, cord(pragma_info_type_spec)::out, io::di, io::uo) is det. intermod_write_pred_decls(_, _, _, [], !PredDeclsCord, !ModeDeclsCord, !PredMarkerPragmasCord, !TypeSpecPragmasCord, !IO). intermod_write_pred_decls(MercInfo, Stream, ModuleInfo, [OrderPredInfo | OrderPredInfos], !PredDeclsCord, !ModeDeclsCord, !PredMarkerPragmasCord, !TypeSpecPragmasCord, !IO) :- intermod_write_pred_decl(MercInfo, Stream, ModuleInfo, OrderPredInfo, !PredDeclsCord, !ModeDeclsCord, !PredMarkerPragmasCord, !TypeSpecPragmasCord, !IO), intermod_write_pred_decls(MercInfo, Stream, ModuleInfo, OrderPredInfos, !PredDeclsCord, !ModeDeclsCord, !PredMarkerPragmasCord, !TypeSpecPragmasCord, !IO). :- pred intermod_write_pred_decl(merc_out_info::in, io.text_output_stream::in, module_info::in, order_pred_info::in, cord(item_pred_decl_info)::in, cord(item_pred_decl_info)::out, cord(item_mode_decl_info)::in, cord(item_mode_decl_info)::out, cord(pragma_info_pred_marker)::in, cord(pragma_info_pred_marker)::out, cord(pragma_info_type_spec)::in, cord(pragma_info_type_spec)::out, io::di, io::uo) is det. intermod_write_pred_decl(MercInfo, Stream, ModuleInfo, OrderPredInfo, !PredDeclsCord, !ModeDeclsCord, !PredMarkerPragmasCord, !TypeSpecPragmasCord, !IO) :- OrderPredInfo = order_pred_info(PredName, _PredArity, PredOrFunc, PredId, PredInfo), ModuleName = pred_info_module(PredInfo), pred_info_get_arg_types(PredInfo, TVarSet, ExistQVars, ArgTypes), pred_info_get_purity(PredInfo, Purity), pred_info_get_class_context(PredInfo, ClassContext), pred_info_get_context(PredInfo, Context), PredSymName = qualified(ModuleName, PredName), TypesAndNoModes = list.map((func(T) = type_only(T)), ArgTypes), MaybeWithType = maybe.no, MaybeWithInst = maybe.no, MaybeDetism = maybe.no, % We are NOT declaring the mode. varset.init(InstVarSet), % Origin is a dummy, which is OK because the origin is never printed. % If that ever changes, we would have to reverse the transform done % by record_pred_origin in add_pred.m. Origin = item_origin_user, PredDecl = item_pred_decl_info(PredSymName, PredOrFunc, TypesAndNoModes, MaybeWithType, MaybeWithInst, MaybeDetism, Origin, TVarSet, InstVarSet, ExistQVars, Purity, ClassContext, Context, item_no_seq_num), % NOTE: The names of type variables in type_spec pragmas must match % *exactly* the names of the corresponding type variables in the % predicate declaration to which they apply. This is why one variable, % VarNamePrint, controls both. % % If a predicate is defined by a foreign_proc, then its declaration % *must* be printed with print_name_only, because that is the only way % that any reference to the type_info variable in the foreign code % in the body of the foreign_proc will match the declared name of the % type variable that it is for. % % We used to print the predicate declarations with print_name_only % for such predicates (predicates defined by foreign_procs) and with % print_name_and_num for all other predicates. (That included predicates % representing promises.) However, the predicates whose declarations % we are writing out have not been through any transformation that % would have either (a) changed the names of any existing type variables, % or (b) introduced any new type variables, so the mapping between % type variable numbers and names should be the same now as when the % the predicate declaration was first parsed. And at that time, two % type variable occurrences with the same name obviously referred to the % same type variable, so the numeric suffix added by print_name_and_num % was obviously not needed. VarNamePrint = print_name_only, mercury_output_item_pred_decl(output_mercury, VarNamePrint, Stream, PredDecl, !IO), pred_info_get_proc_table(PredInfo, ProcMap), % Make sure the mode declarations go out in the same order they came in, % so that the all the modes get the same proc_id in the importing modules. % SortedProcPairs will be sorted on proc_ids. (map.values is not % *documented* to return a list sorted by keys.) map.to_sorted_assoc_list(ProcMap, SortedProcPairs), intermod_gather_pred_valid_modes(PredOrFunc, PredSymName, SortedProcPairs, ModeDecls), intermod_gather_pred_marker_pragmas(PredInfo, PredMarkerPragmas), intermod_gather_pred_type_spec_pragmas(ModuleInfo, PredId, TypeSpecPragmas), list.foldl(mercury_output_item_mode_decl(MercInfo, Stream), ModeDecls, !IO), list.foldl(mercury_output_item_pred_marker(Stream), PredMarkerPragmas, !IO), Lang = output_mercury, list.foldl(mercury_output_pragma_type_spec(Stream, Lang), TypeSpecPragmas, !IO), cord.snoc(PredDecl, !PredDeclsCord), !:ModeDeclsCord = !.ModeDeclsCord ++ cord.from_list(ModeDecls), !:PredMarkerPragmasCord = !.PredMarkerPragmasCord ++ cord.from_list(PredMarkerPragmas), !:TypeSpecPragmasCord = !.TypeSpecPragmasCord ++ cord.from_list(TypeSpecPragmas). :- pred intermod_gather_pred_valid_modes(pred_or_func::in, sym_name::in, assoc_list(proc_id, proc_info)::in, list(item_mode_decl_info)::out) is det. intermod_gather_pred_valid_modes(_, _, [], []). intermod_gather_pred_valid_modes(PredOrFunc, PredSymName, [ProcIdInfo | ProcIdInfos], ModeDecls) :- intermod_gather_pred_valid_modes(PredOrFunc, PredSymName, ProcIdInfos, TailModeDecls), ProcIdInfo = _ProcId - ProcInfo, ( if proc_info_is_valid_mode(ProcInfo) then proc_info_get_maybe_declared_argmodes(ProcInfo, MaybeArgModes), proc_info_get_declared_determinism(ProcInfo, MaybeDetism), ( if MaybeArgModes = yes(ArgModesPrime), MaybeDetism = yes(DetismPrime) then ArgModes = ArgModesPrime, Detism = DetismPrime else unexpected($pred, "attempt to write undeclared mode") ), MaybeWithInst = maybe.no, varset.init(InstVarSet), HeadModeDecl = item_mode_decl_info(PredSymName, yes(PredOrFunc), ArgModes, MaybeWithInst, yes(Detism), InstVarSet, dummy_context, item_no_seq_num), ModeDecls = [HeadModeDecl | TailModeDecls] else ModeDecls = TailModeDecls ). :- pred intermod_gather_pred_marker_pragmas(pred_info::in, list(pragma_info_pred_marker)::out) is det. intermod_gather_pred_marker_pragmas(PredInfo, PredMarkerPragmas) :- ModuleName = pred_info_module(PredInfo), PredOrFunc = pred_info_is_pred_or_func(PredInfo), PredName = pred_info_name(PredInfo), PredSymName = qualified(ModuleName, PredName), PredFormArity = pred_info_orig_arity(PredInfo), user_arity_pred_form_arity(PredOrFunc, UserArity, pred_form_arity(PredFormArity)), pred_info_get_markers(PredInfo, Markers), markers_to_marker_list(Markers, MarkerList), intermod_gather_pred_marker_pragmas_loop(PredOrFunc, PredSymName, UserArity, MarkerList, [], RevPredMarkerPragmas), list.reverse(RevPredMarkerPragmas, PredMarkerPragmas). :- pred intermod_gather_pred_marker_pragmas_loop(pred_or_func::in, sym_name::in, user_arity::in, list(pred_marker)::in, list(pragma_info_pred_marker)::in, list(pragma_info_pred_marker)::out) is det. intermod_gather_pred_marker_pragmas_loop(_, _, _, [], !RevPredMarkerPragmas). intermod_gather_pred_marker_pragmas_loop(PredOrFunc, PredSymName, UserArity, [Marker | Markers], !RevPredMarkerPragmas) :- ( % We do not output these markers. ( Marker = marker_stub ; Marker = marker_builtin_stub ; Marker = marker_no_pred_decl ; Marker = marker_no_detism_warning ; Marker = marker_heuristic_inline ; Marker = marker_mmc_marked_no_inline ; Marker = marker_consider_used ; Marker = marker_calls_are_fully_qualified ; Marker = marker_mutable_access_pred ; Marker = marker_has_require_scope ; Marker = marker_has_incomplete_switch ; Marker = marker_has_format_call ; Marker = marker_has_rhs_lambda ; Marker = marker_fact_table_semantic_errors % Since the inferred declarations are output, these don't need % to be done in the importing module. ; Marker = marker_infer_type ; Marker = marker_infer_modes % Purity is output as part of the pred/func decl. ; Marker = marker_is_impure ; Marker = marker_is_semipure % There is no pragma required for generated class methods. ; Marker = marker_class_method ; Marker = marker_class_instance_method ; Marker = marker_named_class_instance_method % Termination should only be checked in the defining module. ; Marker = marker_check_termination ) ; % We do output these markers. ( Marker = marker_user_marked_inline, PragmaKind = pmpk_inline ; Marker = marker_user_marked_no_inline, PragmaKind = pmpk_noinline ; Marker = marker_promised_pure, PragmaKind = pmpk_promise_pure ; Marker = marker_promised_semipure, PragmaKind = pmpk_promise_semipure ; Marker = marker_promised_equivalent_clauses, PragmaKind = pmpk_promise_eqv_clauses ; Marker = marker_terminates, PragmaKind = pmpk_terminates ; Marker = marker_does_not_terminate, PragmaKind = pmpk_does_not_terminate ; Marker = marker_mode_check_clauses, PragmaKind = pmpk_mode_check_clauses ), PredSpec = pred_pf_name_arity(PredOrFunc, PredSymName, UserArity), PredMarkerInfo = pragma_info_pred_marker(PredSpec, PragmaKind), !:RevPredMarkerPragmas = [PredMarkerInfo | !.RevPredMarkerPragmas] ), intermod_gather_pred_marker_pragmas_loop(PredOrFunc, PredSymName, UserArity, Markers, !RevPredMarkerPragmas). :- pred intermod_gather_pred_type_spec_pragmas(module_info::in, pred_id::in, list(pragma_info_type_spec)::out) is det. intermod_gather_pred_type_spec_pragmas(ModuleInfo, PredId, TypeSpecPragmas) :- module_info_get_type_spec_info(ModuleInfo, TypeSpecInfo), PragmaMap = TypeSpecInfo ^ pragma_map, ( if multi_map.search(PragmaMap, PredId, TypeSpecPragmasPrime) then TypeSpecPragmas = TypeSpecPragmasPrime else TypeSpecPragmas = [] ). %---------------------------------------------------------------------------% :- pred intermod_write_pred_defns(hlds_out_info::in, io.text_output_stream::in, module_info::in, list(order_pred_info)::in, cord(pragma_info_pred_marker)::in, cord(pragma_info_pred_marker)::out, io::di, io::uo) is det. intermod_write_pred_defns(_, _, _, [], !PredMarkerPragmas, !IO). intermod_write_pred_defns(OutInfo, Stream, ModuleInfo, [OrderPredInfo | OrderPredInfos], !PredMarkerPragmas, !IO) :- intermod_write_pred_defn(OutInfo, Stream, ModuleInfo, OrderPredInfo, !PredMarkerPragmas, !IO), intermod_write_pred_defns(OutInfo, Stream, ModuleInfo, OrderPredInfos, !PredMarkerPragmas, !IO). :- pred intermod_write_pred_defn(hlds_out_info::in, io.text_output_stream::in, module_info::in, order_pred_info::in, cord(pragma_info_pred_marker)::in, cord(pragma_info_pred_marker)::out, io::di, io::uo) is det. intermod_write_pred_defn(OutInfo, Stream, ModuleInfo, OrderPredInfo, !PredMarkerPragmas, !IO) :- io.nl(Stream, !IO), OrderPredInfo = order_pred_info(PredName, _PredArity, PredOrFunc, PredId, PredInfo), ModuleName = pred_info_module(PredInfo), PredSymName = qualified(ModuleName, PredName), intermod_gather_pred_marker_pragmas(PredInfo, PredMarkerPragmas), list.foldl(mercury_output_item_pred_marker(Stream), PredMarkerPragmas, !IO), !:PredMarkerPragmas = !.PredMarkerPragmas ++ cord.from_list(PredMarkerPragmas), % The type specialization pragmas for exported preds should % already be in the interface file. pred_info_get_clauses_info(PredInfo, ClausesInfo), clauses_info_get_var_table(ClausesInfo, VarTable), clauses_info_get_headvar_list(ClausesInfo, HeadVars), clauses_info_get_clauses_rep(ClausesInfo, ClausesRep, _ItemNumbers), get_clause_list_maybe_repeated(ClausesRep, Clauses), pred_info_get_goal_type(PredInfo, GoalType), ( GoalType = goal_for_promise(PromiseType), ( Clauses = [Clause], write_promise(OutInfo, Stream, ModuleInfo, VarTable, PromiseType, HeadVars, Clause, !IO) ; ( Clauses = [] ; Clauses = [_, _ | _] ), unexpected($pred, "assertion not a single clause.") ) ; GoalType = goal_not_for_promise(_), pred_info_get_typevarset(PredInfo, TypeVarSet), TypeQual = tvarset_var_table(TypeVarSet, VarTable), list.foldl( intermod_write_clause(OutInfo, Stream, ModuleInfo, PredId, PredSymName, PredOrFunc, VarTable, TypeQual, HeadVars), Clauses, !IO) ). :- pred write_promise(hlds_out_info::in, io.text_output_stream::in, module_info::in, var_table::in, promise_type::in, list(prog_var)::in, clause::in, io::di, io::uo) is det. write_promise(Info, Stream, ModuleInfo, VarTable, PromiseType, HeadVars, Clause, !IO) :- % Please *either* keep this code in sync with mercury_output_item_promise % in parse_tree_out.m, *or* rewrite it to forward the work to that % predicate. HeadVarStrs = list.map(var_table_entry_name(VarTable), HeadVars), HeadVarsStr = string.join_list(", ", HeadVarStrs), % Print initial formatting differently for assertions. ( PromiseType = promise_type_true, io.format(Stream, ":- promise all [%s] (\n", [s(HeadVarsStr)], !IO) ; ( PromiseType = promise_type_exclusive ; PromiseType = promise_type_exhaustive ; PromiseType = promise_type_exclusive_exhaustive ), io.format(Stream, ":- all [%s] %s\n(\n", [s(HeadVarsStr), s(promise_to_string(PromiseType))], !IO) ), Goal = Clause ^ clause_body, do_write_goal(Info, Stream, ModuleInfo, vns_var_table(VarTable), no_tvarset_var_table, print_name_only, 1, "\n).\n", Goal, !IO). :- pred intermod_write_clause(hlds_out_info::in, io.text_output_stream::in, module_info::in, pred_id::in, sym_name::in, pred_or_func::in, var_table::in, type_qual::in, list(prog_var)::in, clause::in, io::di, io::uo) is det. intermod_write_clause(OutInfo, Stream, ModuleInfo, PredId, SymName, PredOrFunc, VarTable, TypeQual, HeadVars, Clause0, !IO) :- Clause0 = clause(ApplicableProcIds, Goal, ImplLang, _, _), ( ImplLang = impl_lang_mercury, strip_headvar_unifications(HeadVars, Clause0, ClauseHeadVars, Clause), % Variable numbers need to be used for the case where the added % arguments for a DCG pred expression are named the same % as variables in the enclosing clause. % % We don't need the actual names, and including them in the .opt file % would lead to unnecessary recompilations when the *only* changes % in a .opt file are changes in variable variables. % % We could standardize the variables in the clause before printing % it out, numbering them e.g. in the order of their appearance, % so that changes in variable *numbers* don't cause recompilations % either. However, the variable numbers *are* initially allocated % in such an order, both by the code that reads in terms and the % code that converts parse tree goals into HLDS goals, so this is % not likely to be necessary, while its cost may be non-negligible. init_var_table(EmptyVarTable), write_clause(OutInfo, Stream, output_mercury, ModuleInfo, PredId, PredOrFunc, vns_var_table(EmptyVarTable), TypeQual, print_name_and_num, write_declared_modes, 1, ClauseHeadVars, Clause, !IO) ; ImplLang = impl_lang_foreign(_), module_info_pred_info(ModuleInfo, PredId, PredInfo), pred_info_get_proc_table(PredInfo, Procs), ( if ( % Pull the foreign code out of the goal. Goal = hlds_goal(conj(plain_conj, Goals), _), list.filter( ( pred(G::in) is semidet :- G = hlds_goal(GE, _), GE = call_foreign_proc(_, _, _, _, _, _, _) ), Goals, [ForeignCodeGoal]), ForeignCodeGoal = hlds_goal(ForeignCodeGoalExpr, _), ForeignCodeGoalExpr = call_foreign_proc(Attributes, _, _, Args, _ExtraArgs, _MaybeTraceRuntimeCond, PragmaCode) ; Goal = hlds_goal(GoalExpr, _), GoalExpr = call_foreign_proc(Attributes, _, _, Args, _ExtraArgs, _MaybeTraceRuntimeCond, PragmaCode) ) then ( ApplicableProcIds = all_modes, unexpected($pred, "all_modes foreign_proc") ; ApplicableProcIds = selected_modes(ProcIds), list.foldl( intermod_write_foreign_clause(Stream, Procs, PredOrFunc, VarTable, PragmaCode, Attributes, Args, SymName), ProcIds, !IO) ; ( ApplicableProcIds = unify_in_in_modes ; ApplicableProcIds = unify_non_in_in_modes ), unexpected($pred, "unify modes foreign_proc") ) else unexpected($pred, "did not find foreign_proc") ) ). % Strip the `Headvar.n = Term' unifications from each clause, % except if the `Term' is a lambda expression. % % At least two problems occur if this is not done: % % - in some cases where nested unique modes were accepted by mode analysis, % the extra aliasing added by the extra level of headvar unifications % caused mode analysis to report an error (ground expected unique), % when analysing the clauses read in from `.opt' files. % % - only HeadVar unifications may be reordered with impure goals, % so a mode error results for the second level of headvar unifications % added when the clauses are read in again from the `.opt' file. % Clauses containing impure goals are not written to the `.opt' file % for this reason. % :- pred strip_headvar_unifications(list(prog_var)::in, clause::in, list(prog_term)::out, clause::out) is det. strip_headvar_unifications(HeadVars, Clause0, HeadTerms, Clause) :- Goal0 = Clause0 ^ clause_body, Goal0 = hlds_goal(_, GoalInfo0), goal_to_conj_list(Goal0, Goals0), map.init(HeadVarMap0), ( if strip_headvar_unifications_from_goal_list(Goals0, HeadVars, [], Goals, HeadVarMap0, HeadVarMap) then list.map( ( pred(HeadVar0::in, HeadTerm::out) is det :- ( if map.search(HeadVarMap, HeadVar0, HeadTerm0) then HeadTerm = HeadTerm0 else Context = Clause0 ^ clause_context, HeadTerm = term.variable(HeadVar0, Context) ) ), HeadVars, HeadTerms), conj_list_to_goal(Goals, GoalInfo0, Goal), Clause = Clause0 ^ clause_body := Goal else term_subst.var_list_to_term_list(HeadVars, HeadTerms), Clause = Clause0 ). :- pred strip_headvar_unifications_from_goal_list(list(hlds_goal)::in, list(prog_var)::in, list(hlds_goal)::in, list(hlds_goal)::out, map(prog_var, prog_term)::in, map(prog_var, prog_term)::out) is semidet. strip_headvar_unifications_from_goal_list([], _, RevGoals, Goals, !HeadVarMap) :- list.reverse(RevGoals, Goals). strip_headvar_unifications_from_goal_list([Goal | Goals0], HeadVars, RevGoals0, Goals, !HeadVarMap) :- ( if Goal = hlds_goal(unify(LHSVar, RHS, _, _, _), _), list.member(LHSVar, HeadVars), Context = dummy_context, ( RHS = rhs_var(RHSVar), RHSTerm = term.variable(RHSVar, Context) ; RHS = rhs_functor(ConsId, _, Args), require_complete_switch [ConsId] ( ConsId = some_int_const(IntConst), RHSTerm = int_const_to_decimal_term(IntConst, Context) ; ConsId = float_const(Float), RHSTerm = term.functor(term.float(Float), [], Context) ; ConsId = char_const(Char), RHSTerm = term.functor(term.atom(string.from_char(Char)), [], Context) ; ConsId = string_const(String), RHSTerm = term.functor(term.string(String), [], Context) ; ConsId = cons(SymName, _, _), term_subst.var_list_to_term_list(Args, ArgTerms), construct_qualified_term(SymName, ArgTerms, RHSTerm) ; ( ConsId = base_typeclass_info_const(_, _, _, _) ; ConsId = closure_cons(_, _) ; ConsId = deep_profiling_proc_layout(_) ; ConsId = ground_term_const(_, _) ; ConsId = tabling_info_const(_) ; ConsId = impl_defined_const(_) ; ConsId = table_io_entry_desc(_) ; ConsId = tuple_cons(_) ; ConsId = type_ctor_info_const(_, _, _) ; ConsId = type_info_cell_constructor(_) ; ConsId = typeclass_info_cell_constructor ; ConsId = type_info_const(_) ; ConsId = typeclass_info_const(_) ), fail ) ; RHS = rhs_lambda_goal(_, _, _, _, _, _, _, _), fail ) then % Don't strip the headvar unifications if one of the headvars % appears twice. This should probably never happen. map.insert(LHSVar, RHSTerm, !HeadVarMap), RevGoals1 = RevGoals0 else RevGoals1 = [Goal | RevGoals0] ), strip_headvar_unifications_from_goal_list(Goals0, HeadVars, RevGoals1, Goals, !HeadVarMap). :- pred intermod_write_foreign_clause(io.text_output_stream::in, proc_table::in, pred_or_func::in, var_table::in, pragma_foreign_proc_impl::in, pragma_foreign_proc_attributes::in, list(foreign_arg)::in, sym_name::in, proc_id::in, io::di, io::uo) is det. intermod_write_foreign_clause(Stream, Procs, PredOrFunc, VarTable0, PragmaImpl, Attributes, Args, SymName, ProcId, !IO) :- map.lookup(Procs, ProcId, ProcInfo), proc_info_get_maybe_declared_argmodes(ProcInfo, MaybeArgModes), ( MaybeArgModes = yes(ArgModes), get_pragma_foreign_code_vars(Args, ArgModes, PragmaVars, VarTable0, VarTable), proc_info_get_inst_varset(ProcInfo, InstVarSet), split_var_table(VarTable, ProgVarSet, _VarTypes), FPInfo = pragma_info_foreign_proc(Attributes, SymName, PredOrFunc, PragmaVars, ProgVarSet, InstVarSet, PragmaImpl), mercury_output_pragma_foreign_proc(Stream, output_mercury, FPInfo, !IO) ; MaybeArgModes = no, unexpected($pred, "no mode declaration") ). :- pred get_pragma_foreign_code_vars(list(foreign_arg)::in, list(mer_mode)::in, list(pragma_var)::out, var_table::in, var_table::out) is det. get_pragma_foreign_code_vars(Args, Modes, PragmaVars, !VarTable) :- ( Args = [Arg | ArgsTail], Modes = [Mode | ModesTail], Arg = foreign_arg(Var, MaybeNameAndMode, _, _), ( MaybeNameAndMode = no, Name = "_" ; MaybeNameAndMode = yes(foreign_arg_name_mode(Name, _Mode2)) ), PragmaVar = pragma_var(Var, Name, Mode, bp_native_if_possible), update_var_name(Var, Name, !VarTable), get_pragma_foreign_code_vars(ArgsTail, ModesTail, PragmaVarsTail, !VarTable), PragmaVars = [PragmaVar | PragmaVarsTail] ; Args = [], Modes = [], PragmaVars = [] ; Args = [], Modes = [_ | _], unexpected($pred, "list length mismatch") ; Args = [_ | _], Modes = [], unexpected($pred, "list length mismatch") ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % Should a declaration with the given status be written to the `.opt' file. % :- func type_status_to_write(type_status) = bool. :- func inst_status_to_write(inst_status) = bool. :- func mode_status_to_write(mode_status) = bool. :- func typeclass_status_to_write(typeclass_status) = bool. :- func instance_status_to_write(instance_status) = bool. :- func pred_status_to_write(pred_status) = bool. type_status_to_write(type_status(OldStatus)) = old_status_to_write(OldStatus). inst_status_to_write(inst_status(InstModeStatus)) = ToWrite :- ToWrite = instmode_status_to_write(InstModeStatus). mode_status_to_write(mode_status(InstModeStatus)) = ToWrite :- ToWrite = instmode_status_to_write(InstModeStatus). typeclass_status_to_write(typeclass_status(OldStatus)) = old_status_to_write(OldStatus). instance_status_to_write(instance_status(OldStatus)) = old_status_to_write(OldStatus). pred_status_to_write(pred_status(OldStatus)) = old_status_to_write(OldStatus). :- func instmode_status_to_write(new_instmode_status) = bool. instmode_status_to_write(InstModeStatus) = ToWrite :- ( InstModeStatus = instmode_defined_in_this_module(InstModeExport), ( InstModeExport = instmode_export_anywhere, ToWrite = no ; ( InstModeExport = instmode_export_only_submodules ; InstModeExport = instmode_export_nowhere ), ToWrite = yes ) ; InstModeStatus = instmode_defined_in_other_module(_), ToWrite = no ). :- func old_status_to_write(old_import_status) = bool. old_status_to_write(status_imported(_)) = no. old_status_to_write(status_abstract_imported) = no. old_status_to_write(status_pseudo_imported) = no. old_status_to_write(status_opt_imported) = no. old_status_to_write(status_exported) = no. old_status_to_write(status_opt_exported) = yes. old_status_to_write(status_abstract_exported) = yes. old_status_to_write(status_pseudo_exported) = no. old_status_to_write(status_exported_to_submodules) = yes. old_status_to_write(status_local) = yes. old_status_to_write(status_external(Status)) = bool.not(old_status_is_exported(Status)). %---------------------------------------------------------------------------% :- type maybe_need_foreign_import_modules ---> do_not_need_foreign_import_modules ; do_need_foreign_import_modules. % A collection of stuff to go in the .opt file. % :- type intermod_info ---> intermod_info( % The initial ModuleInfo. Readonly. im_module_info :: module_info, % The modules that the .opt file will need to use. im_use_modules :: set(module_name), % The ids of the predicates (and functions) whose type and mode % declarations we want to put into the .opt file. im_pred_decls :: set(pred_id), % The ids of the predicates (and functions) whose definitions % (i.e. clauses, foreign_procs or promises) we want to put % into the .opt file. im_pred_defns :: set(pred_id), % The instance definitions we want to put into the .opt file. im_instance_defns :: assoc_list(class_id, hlds_instance_defn), % The type definitions we want to put into the .opt file. im_type_defns :: assoc_list(type_ctor, hlds_type_defn), % Is there anything we want to put into the .opt file % that may refer to foreign language entities that may need % access to foreign_import_modules to resolve? % % If no, we don't need to include any of the % foreign_import_modules declarations in the module % in the .opt file. % % If yes, we need to include all of them in the .opt file, % since we have no info about which fim defines what. im_need_foreign_imports :: maybe_need_foreign_import_modules ). :- pred init_intermod_info(module_info::in, intermod_info::out) is det. init_intermod_info(ModuleInfo, IntermodInfo) :- set.init(Modules), set.init(PredDecls), set.init(PredDefns), InstanceDefns = [], TypeDefns = [], IntermodInfo = intermod_info(ModuleInfo, Modules, PredDecls, PredDefns, InstanceDefns, TypeDefns, do_not_need_foreign_import_modules). :- pred intermod_info_get_module_info(intermod_info::in, module_info::out) is det. :- pred intermod_info_get_use_modules(intermod_info::in, set(module_name)::out) is det. :- pred intermod_info_get_pred_decls(intermod_info::in, set(pred_id)::out) is det. :- pred intermod_info_get_pred_defns(intermod_info::in, set(pred_id)::out) is det. :- pred intermod_info_get_instances(intermod_info::in, assoc_list(class_id, hlds_instance_defn)::out) is det. :- pred intermod_info_get_types(intermod_info::in, assoc_list(type_ctor, hlds_type_defn)::out) is det. :- pred intermod_info_set_use_modules(set(module_name)::in, intermod_info::in, intermod_info::out) is det. :- pred intermod_info_set_pred_decls(set(pred_id)::in, intermod_info::in, intermod_info::out) is det. :- pred intermod_info_set_pred_defns(set(pred_id)::in, intermod_info::in, intermod_info::out) is det. :- pred intermod_info_set_instances( assoc_list(class_id, hlds_instance_defn)::in, intermod_info::in, intermod_info::out) is det. :- pred intermod_info_set_types(assoc_list(type_ctor, hlds_type_defn)::in, intermod_info::in, intermod_info::out) is det. %:- pred intermod_info_set_insts(set(inst_ctor)::in, % intermod_info::in, intermod_info::out) is det. :- pred intermod_info_set_need_foreign_import_modules(intermod_info::in, intermod_info::out) is det. intermod_info_get_module_info(IntermodInfo, X) :- X = IntermodInfo ^ im_module_info. intermod_info_get_use_modules(IntermodInfo, X) :- X = IntermodInfo ^ im_use_modules. intermod_info_get_pred_decls(IntermodInfo, X) :- X = IntermodInfo ^ im_pred_decls. intermod_info_get_pred_defns(IntermodInfo, X) :- X = IntermodInfo ^ im_pred_defns. intermod_info_get_instances(IntermodInfo, X) :- X = IntermodInfo ^ im_instance_defns. intermod_info_get_types(IntermodInfo, X) :- X = IntermodInfo ^ im_type_defns. intermod_info_set_use_modules(X, !IntermodInfo) :- !IntermodInfo ^ im_use_modules := X. intermod_info_set_pred_decls(X, !IntermodInfo) :- !IntermodInfo ^ im_pred_decls := X. intermod_info_set_pred_defns(X, !IntermodInfo) :- !IntermodInfo ^ im_pred_defns := X. intermod_info_set_instances(X, !IntermodInfo) :- !IntermodInfo ^ im_instance_defns := X. intermod_info_set_types(X, !IntermodInfo) :- !IntermodInfo ^ im_type_defns := X. intermod_info_set_need_foreign_import_modules(!IntermodInfo) :- !IntermodInfo ^ im_need_foreign_imports := do_need_foreign_import_modules. %---------------------------------------------------------------------------% maybe_opt_export_entities(!ModuleInfo) :- module_info_get_globals(!.ModuleInfo, Globals), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), trace [io(!IO)] ( get_progress_output_stream(!.ModuleInfo, ProgressStream, !IO), maybe_write_string(ProgressStream, VeryVerbose, "% Adjusting import status of predicates in the `.opt' file...", !IO) ), decide_what_to_opt_export(!.ModuleInfo, IntermodInfo), maybe_opt_export_listed_entities(IntermodInfo, !ModuleInfo), trace [io(!IO)] ( get_progress_output_stream(!.ModuleInfo, ProgressStream, !IO), maybe_write_string(ProgressStream, VeryVerbose, " done\n", !IO) ). maybe_opt_export_listed_entities(IntermodInfo, !ModuleInfo) :- % XXX This would be clearer as well as faster if we gathered up % the pred_ids of all the predicates that we found we need to opt_export % while processing type, typeclass and instance definitions, % and then opt_exported them all at once. intermod_info_get_pred_decls(IntermodInfo, PredDeclsSet), set.to_sorted_list(PredDeclsSet, PredDecls), opt_export_preds(PredDecls, !ModuleInfo), maybe_opt_export_types(!ModuleInfo), maybe_opt_export_classes(!ModuleInfo), maybe_opt_export_instances(!ModuleInfo). %---------------------% :- pred maybe_opt_export_types(module_info::in, module_info::out) is det. maybe_opt_export_types(!ModuleInfo) :- module_info_get_type_table(!.ModuleInfo, TypeTable0), map_foldl_over_type_ctor_defns(maybe_opt_export_type_defn, TypeTable0, TypeTable, !ModuleInfo), module_info_set_type_table(TypeTable, !ModuleInfo). :- pred maybe_opt_export_type_defn(type_ctor::in, hlds_type_defn::in, hlds_type_defn::out, module_info::in, module_info::out) is det. maybe_opt_export_type_defn(TypeCtor, TypeDefn0, TypeDefn, !ModuleInfo) :- module_info_get_name(!.ModuleInfo, ModuleName), ( if should_opt_export_type_defn(ModuleName, TypeCtor, TypeDefn0) then hlds_data.set_type_defn_status(type_status(status_exported), TypeDefn0, TypeDefn), adjust_status_of_special_preds(TypeCtor, !ModuleInfo) else TypeDefn = TypeDefn0 ). :- pred adjust_status_of_special_preds((type_ctor)::in, module_info::in, module_info::out) is det. adjust_status_of_special_preds(TypeCtor, ModuleInfo0, ModuleInfo) :- special_pred_list(SpecialPredList), module_info_get_special_pred_maps(ModuleInfo0, SpecPredMaps), list.filter_map( ( pred(SpecPredId::in, PredId::out) is semidet :- search_special_pred_maps(SpecPredMaps, SpecPredId, TypeCtor, PredId) ), SpecialPredList, PredIds), opt_export_preds(PredIds, ModuleInfo0, ModuleInfo). %---------------------% :- pred maybe_opt_export_classes(module_info::in, module_info::out) is det. maybe_opt_export_classes(!ModuleInfo) :- module_info_get_class_table(!.ModuleInfo, Classes0), map.to_assoc_list(Classes0, ClassAL0), list.map_foldl(maybe_opt_export_class_defn, ClassAL0, ClassAL, !ModuleInfo), map.from_sorted_assoc_list(ClassAL, Classes), module_info_set_class_table(Classes, !ModuleInfo). :- pred maybe_opt_export_class_defn(pair(class_id, hlds_class_defn)::in, pair(class_id, hlds_class_defn)::out, module_info::in, module_info::out) is det. maybe_opt_export_class_defn(ClassId - ClassDefn0, ClassId - ClassDefn, !ModuleInfo) :- ToWrite = typeclass_status_to_write(ClassDefn0 ^ classdefn_status), ( ToWrite = yes, ClassDefn = ClassDefn0 ^ classdefn_status := typeclass_status(status_exported), method_infos_to_pred_ids(ClassDefn ^ classdefn_method_infos, PredIds), opt_export_preds(PredIds, !ModuleInfo) ; ToWrite = no, ClassDefn = ClassDefn0 ). :- pred method_infos_to_pred_ids(list(method_info)::in, list(pred_id)::out) is det. method_infos_to_pred_ids(MethodInfos, PredIds) :- GetMethodPredId = ( pred(MI::in, PredId::out) is det :- MI ^ method_cur_proc = proc(PredId, _ProcId) ), list.map(GetMethodPredId, MethodInfos, PredIds0), list.remove_adjacent_dups(PredIds0, PredIds). %---------------------% :- pred maybe_opt_export_instances(module_info::in, module_info::out) is det. maybe_opt_export_instances(!ModuleInfo) :- module_info_get_instance_table(!.ModuleInfo, Instances0), map.to_assoc_list(Instances0, InstanceAL0), list.map_foldl(maybe_opt_export_class_instances, InstanceAL0, InstanceAL, !ModuleInfo), map.from_sorted_assoc_list(InstanceAL, Instances), module_info_set_instance_table(Instances, !ModuleInfo). :- pred maybe_opt_export_class_instances( pair(class_id, list(hlds_instance_defn))::in, pair(class_id, list(hlds_instance_defn))::out, module_info::in, module_info::out) is det. maybe_opt_export_class_instances(ClassId - InstanceList0, ClassId - InstanceList, !ModuleInfo) :- list.map_foldl(maybe_opt_export_instance_defn, InstanceList0, InstanceList, !ModuleInfo). :- pred maybe_opt_export_instance_defn(hlds_instance_defn::in, hlds_instance_defn::out, module_info::in, module_info::out) is det. maybe_opt_export_instance_defn(Instance0, Instance, !ModuleInfo) :- Instance0 = hlds_instance_defn(InstanceModule, InstanceStatus0, TVarSet, OriginalTypes, Types, Constraints, MaybeSubsumedContext, ConstraintProofs, Body, MaybeMethodInfos, Context), ToWrite = instance_status_to_write(InstanceStatus0), ( ToWrite = yes, InstanceStatus = instance_status(status_exported), Instance = hlds_instance_defn(InstanceModule, InstanceStatus, TVarSet, OriginalTypes, Types, Constraints, MaybeSubsumedContext, ConstraintProofs, Body, MaybeMethodInfos, Context), ( MaybeMethodInfos = yes(MethodInfos), method_infos_to_pred_ids(MethodInfos, PredIds), opt_export_preds(PredIds, !ModuleInfo) ; % This can happen if an instance has multiple declarations, % one of which is abstract. MaybeMethodInfos = no ) ; ToWrite = no, Instance = Instance0 ). %---------------------% :- pred opt_export_preds(list(pred_id)::in, module_info::in, module_info::out) is det. opt_export_preds(PredIds, !ModuleInfo) :- module_info_get_pred_id_table(!.ModuleInfo, PredIdTable0), opt_export_preds_in_pred_id_table(PredIds, PredIdTable0, PredIdTable), module_info_set_pred_id_table(PredIdTable, !ModuleInfo). :- pred opt_export_preds_in_pred_id_table(list(pred_id)::in, pred_id_table::in, pred_id_table::out) is det. opt_export_preds_in_pred_id_table([], !PredIdTable). opt_export_preds_in_pred_id_table([PredId | PredIds], !PredIdTable) :- map.lookup(!.PredIdTable, PredId, PredInfo0), pred_info_get_status(PredInfo0, PredStatus0), ToWrite = pred_status_to_write(PredStatus0), ( ToWrite = yes, ( if pred_info_get_origin(PredInfo0, Origin), Origin = origin_compiler(made_for_uci(spec_pred_unify, _)) then PredStatus = pred_status(status_pseudo_exported) else if PredStatus0 = pred_status(status_external(_)) then PredStatus = pred_status(status_external(status_opt_exported)) else PredStatus = pred_status(status_opt_exported) ), pred_info_set_status(PredStatus, PredInfo0, PredInfo), map.det_update(PredId, PredInfo, !PredIdTable) ; ToWrite = no ), opt_export_preds_in_pred_id_table(PredIds, !PredIdTable). %---------------------------------------------------------------------------% :- end_module transform_hlds.intermod. %---------------------------------------------------------------------------%