mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-20 08:19:28 +00:00
We can now generate error messages for types that have both foreign_enum
and reserve_tag pragmas, which we couldn't before. (In some cases,
we used to get a compiler abort.)
compiler/add_foreign_enum.m:
Don't stop after finding the first problem with a foreign_export_enum
or foreign_enum pragma; if a pragma has more than one problem,
generate reports for them all.
When reporting that the type named in a foreign_export_enum or
foreign_enum is not an enum type, have the error message say *why*.
Fill in the two new fields in exported_enum_infos (see below).
compiler/make_hlds_passes.m:
Add reserve_tag pragmas to the HLDS before adding foreign_export_enum
and foreign_enum pragmas. Since such a pragma would convert an enum
type into a non-enum type, this is needed to ensure that the code
in add_foreign_enum.m that checks whether a type is an enum type
is operating on data that is final. Without that, it may make
wrong decisions.
compiler/make_hlds_separate_items.m:
compiler/add_pragma.m:
Provide the support needed by the new code in make_hlds_passes.m.
In add_pragma.m, rename some insts to avoid name ambiguities,
and simplify an if-then-else.
compiler/hlds_module.m:
Add two fields to exported_enum_infos, to make processing them easier.
(We couldn't do this earlier, since add_foreign_enum.m didn't have
access to the final forms of the bodies of du type definitions,
which is where the values of the two new fields come from.)
compiler/export.m:
compiler/ml_type_gen.m:
Simplify some code using the new fields in exported_enum_infos.
compiler/error_util.m:
Provide a mechanism to control whether sym_names in cons_ids
are module qualified or not. This is "needed" by the new error
messages created in add_foreign_enum.m.
compiler/check_promise.m:
compiler/det_report.m:
compiler/parse_inst_mode_name.m:
compiler/typecheck_errors.m:
Conform to the change to error_util.m.
compiler/make_tags.m:
Clean up some code and some comments.
tests/invalid/exported_foreign_enum.{m,err_exp}:
A new test case to test the new capability to print more than one
error message for the same pragma.
tests/invalid/Mmakefile:
Enable the new test case.
tests/invalid/ee_invalid.err_exp:
Expect improved error messages.
287 lines
11 KiB
Mathematica
287 lines
11 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2015 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.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This module checks that exported promises refer only to exported entities.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.check_promise.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
% Purity checking should call post_typecheck_finish_promise on every
|
|
% predicate that implements a promise, with the third arg saying
|
|
% what kind of promise it is.
|
|
%
|
|
% This predicate records the promise in the relevant promise table
|
|
% (the assertion table or the promise_ex table). It then removes the
|
|
% predicate implementing the promise from the list of the pred ids
|
|
% that future compiler passes should process.
|
|
%
|
|
% If the assertion is in the interface, we check that it doesn't refer
|
|
% to any symbols which are local to that module.
|
|
%
|
|
:- pred check_and_store_promise(pred_id::in, pred_info::in,
|
|
promise_type::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.assertion.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.status.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module bool.
|
|
:- import_module require.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
check_and_store_promise(PredId, PredInfo, PromiseType,
|
|
!ModuleInfo, !Specs) :-
|
|
% Store the declaration in the appropriate table and get the goal
|
|
% for the promise.
|
|
store_promise(PredId, PredInfo, PromiseType, !ModuleInfo, Goal),
|
|
|
|
% Remove the predicate from further processing.
|
|
module_info_make_pred_id_invalid(PredId, !ModuleInfo),
|
|
|
|
% If the promise is in the interface, then ensure that it doesn't refer
|
|
% to any local symbols.
|
|
( if pred_info_is_exported(PredInfo) then
|
|
check_in_interface_promise_goal(!.ModuleInfo, PredInfo, Goal, !Specs)
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Store promise declaration, normalise goal and return new module_info
|
|
% and the goal for further processing.
|
|
%
|
|
:- pred store_promise(pred_id::in, pred_info::in, promise_type::in,
|
|
module_info::in, module_info::out, hlds_goal::out) is det.
|
|
|
|
store_promise(PredId, PredInfo, PromiseType, !ModuleInfo, Goal) :-
|
|
(
|
|
% Case for assertions.
|
|
PromiseType = promise_type_true,
|
|
module_info_get_assertion_table(!.ModuleInfo, AssertTable0),
|
|
assertion_table_add_assertion(PredId, AssertionId,
|
|
AssertTable0, AssertTable),
|
|
module_info_set_assertion_table(AssertTable, !ModuleInfo),
|
|
assertion.assert_id_goal(!.ModuleInfo, AssertionId, Goal),
|
|
assertion.record_preds_used_in(Goal, AssertionId, !ModuleInfo)
|
|
;
|
|
% Case for exclusivity.
|
|
( PromiseType = promise_type_exclusive
|
|
; PromiseType = promise_type_exclusive_exhaustive
|
|
),
|
|
get_promise_ex_goal(PredInfo, Goal),
|
|
predids_from_goal(Goal, PredIds),
|
|
module_info_get_exclusive_table(!.ModuleInfo, Table0),
|
|
list.foldl(exclusive_table_add(PredId), PredIds, Table0, Table),
|
|
module_info_set_exclusive_table(Table, !ModuleInfo)
|
|
;
|
|
% Case for exhaustiveness -- XXX not yet implemented.
|
|
PromiseType = promise_type_exhaustive,
|
|
get_promise_ex_goal(PredInfo, Goal)
|
|
).
|
|
|
|
% Get the goal from a promise_ex declaration.
|
|
%
|
|
:- pred get_promise_ex_goal(pred_info::in, hlds_goal::out) is det.
|
|
|
|
get_promise_ex_goal(PredInfo, Goal) :-
|
|
pred_info_get_clauses_info(PredInfo, ClausesInfo),
|
|
clauses_info_get_clauses_rep(ClausesInfo, ClausesRep, _ItemNumbers),
|
|
get_clause_list_maybe_repeated(ClausesRep, Clauses),
|
|
( if Clauses = [Clause] then
|
|
Goal0 = Clause ^ clause_body,
|
|
assertion.normalise_goal(Goal0, Goal)
|
|
else
|
|
unexpected($module, $pred, "not a single clause")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Ensure that an assertion which is defined in an interface doesn't
|
|
% refer to any constructors, functions and predicates defined in the
|
|
% implementation of that module.
|
|
%
|
|
:- pred check_in_interface_promise_goal(module_info::in, pred_info::in,
|
|
hlds_goal::in, list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Goal, !Specs) :-
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
(
|
|
GoalExpr = plain_call(PredId, _, _, _, _,SymName),
|
|
module_info_pred_info(ModuleInfo, PredId, CallPredInfo),
|
|
pred_info_get_status(CallPredInfo, PredStatus),
|
|
DefnInImplSection = pred_status_defined_in_impl_section(PredStatus),
|
|
(
|
|
DefnInImplSection = yes,
|
|
Context = goal_info_get_context(GoalInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(CallPredInfo),
|
|
Arity = pred_info_orig_arity(CallPredInfo),
|
|
IdPieces =
|
|
[simple_call(simple_call_id(PredOrFunc, SymName, Arity))],
|
|
report_assertion_interface_error(ModuleInfo, Context, IdPieces,
|
|
!Specs)
|
|
;
|
|
DefnInImplSection = no
|
|
)
|
|
;
|
|
GoalExpr = generic_call(_, _, _, _, _)
|
|
;
|
|
GoalExpr = unify(Var, RHS, _, _, _),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
check_in_interface_promise_unify_rhs(ModuleInfo, PredInfo, Var, RHS,
|
|
Context, !Specs)
|
|
;
|
|
GoalExpr = call_foreign_proc(_, PredId, _, _, _, _, _),
|
|
module_info_pred_info(ModuleInfo, PredId, PragmaPredInfo),
|
|
pred_info_get_status(PragmaPredInfo, PredStatus),
|
|
DefnInImplSection = pred_status_defined_in_impl_section(PredStatus),
|
|
(
|
|
DefnInImplSection = yes,
|
|
Context = goal_info_get_context(GoalInfo),
|
|
PredOrFunc = pred_info_is_pred_or_func(PragmaPredInfo),
|
|
Name = pred_info_name(PragmaPredInfo),
|
|
SymName = unqualified(Name),
|
|
Arity = pred_info_orig_arity(PragmaPredInfo),
|
|
IdPieces =
|
|
[simple_call(simple_call_id(PredOrFunc, SymName, Arity))],
|
|
report_assertion_interface_error(ModuleInfo, Context, IdPieces,
|
|
!Specs)
|
|
;
|
|
DefnInImplSection = no
|
|
)
|
|
;
|
|
GoalExpr = conj(_, Goals),
|
|
check_in_interface_promise_goals(ModuleInfo, PredInfo, Goals, !Specs)
|
|
;
|
|
GoalExpr = switch(_, _, _),
|
|
unexpected($module, $pred, "assertion contains switch")
|
|
;
|
|
GoalExpr = disj(Goals),
|
|
check_in_interface_promise_goals(ModuleInfo, PredInfo, Goals, !Specs)
|
|
;
|
|
GoalExpr = negation(SubGoal),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, SubGoal, !Specs)
|
|
;
|
|
GoalExpr = scope(_, SubGoal),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, SubGoal, !Specs)
|
|
;
|
|
GoalExpr = if_then_else(_, Cond, Then, Else),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Cond, !Specs),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Then, !Specs),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Else, !Specs)
|
|
;
|
|
GoalExpr = shorthand(ShortHand),
|
|
(
|
|
ShortHand = atomic_goal(_, _, _, _, MainGoal, OrElseGoals, _),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, MainGoal,
|
|
!Specs),
|
|
check_in_interface_promise_goals(ModuleInfo, PredInfo, OrElseGoals,
|
|
!Specs)
|
|
;
|
|
ShortHand = try_goal(_, _, SubGoal),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, SubGoal,
|
|
!Specs)
|
|
;
|
|
ShortHand = bi_implication(LHS, RHS),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, LHS, !Specs),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, RHS, !Specs)
|
|
)
|
|
).
|
|
|
|
:- pred check_in_interface_promise_unify_rhs(module_info::in, pred_info::in,
|
|
prog_var::in, unify_rhs::in, prog_context::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_in_interface_promise_unify_rhs(ModuleInfo, PredInfo, Var, RHS, Context,
|
|
!Specs) :-
|
|
(
|
|
RHS = rhs_var(_)
|
|
;
|
|
RHS = rhs_functor(ConsId, _, _),
|
|
pred_info_get_clauses_info(PredInfo, ClausesInfo),
|
|
clauses_info_get_vartypes(ClausesInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
module_info_get_type_table(ModuleInfo, TypeTable),
|
|
lookup_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
|
|
get_type_defn_status(TypeDefn, TypeStatus),
|
|
DefinedInImpl = type_status_defined_in_impl_section(TypeStatus),
|
|
(
|
|
DefinedInImpl = yes,
|
|
IdPieces = [words("constructor"),
|
|
qual_cons_id_and_maybe_arity(ConsId)],
|
|
report_assertion_interface_error(ModuleInfo, Context, IdPieces,
|
|
!Specs)
|
|
;
|
|
DefinedInImpl = no
|
|
)
|
|
;
|
|
RHS = rhs_lambda_goal(_, _, _, _, _, _, _, _, Goal),
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Goal, !Specs)
|
|
).
|
|
|
|
:- pred check_in_interface_promise_goals(module_info::in, pred_info::in,
|
|
list(hlds_goal)::in, list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_in_interface_promise_goals(_ModuleInfo, _PredInfo, [], !Specs).
|
|
check_in_interface_promise_goals(ModuleInfo, PredInfo, [Goal0 | Goal0s],
|
|
!Specs) :-
|
|
check_in_interface_promise_goal(ModuleInfo, PredInfo, Goal0, !Specs),
|
|
check_in_interface_promise_goals(ModuleInfo, PredInfo, Goal0s, !Specs).
|
|
|
|
%---------------------%
|
|
|
|
:- pred report_assertion_interface_error(module_info::in, prog_context::in,
|
|
list(format_component)::in, list(error_spec)::in, list(error_spec)::out)
|
|
is det.
|
|
|
|
report_assertion_interface_error(ModuleInfo, Context, IdPieces, !Specs) :-
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MainPieces =
|
|
[words("In interface for module"), qual_sym_name(ModuleName),
|
|
suffix(":"), nl,
|
|
words("error: exported promise refers to")] ++ IdPieces ++
|
|
[words("which is defined in the implementation section of module"),
|
|
qual_sym_name(ModuleName), suffix("."), nl],
|
|
VerbosePieces =
|
|
[words("Either move the promise into the implementation section"),
|
|
words("or move the definition into the interface."), nl],
|
|
Msgs = [always(MainPieces), verbose_only(verbose_always, VerbosePieces)],
|
|
Spec = error_spec(severity_error, phase_type_check,
|
|
[simple_msg(Context, Msgs)]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.check_promise.
|
|
%---------------------------------------------------------------------------%
|