mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-24 05:43:53 +00:00
compiler/passes_aux.m:
The bug that this diff "fixes", Mantis bug #516, surfaced when
in an earlier change, I "fixed" an inconsistency in passes_aux.m,
which was that the names of many predicate said that they iterated
over *all* procedures, but actually they iterated over only the *valid*
procedures. The fix was to iterate over all the procedures.
As it happens, in the rare circumstances represented by Mantis #516,
this can lead a compiler pass in the middle end (loop_inv.m)
processing an invalid procedure, whose invalid contents unsurprisingly
caused the abort. I thought such procedures should never be kept
in the HLDS once the front end has done its job, but I was wrong.
This diff restores the "status quo ante" by once again iterating over
only the valid procedures, but fixes the inconsistency in another way:
by replacing "all" with "valid" in the names of the affected predicates.
compiler/modes.m:
The actual root cause of Mantis #516 lies *not* in passes_aux.m,
but here, since it is modes.m that leaves the invalid procedure
in the HLDS. Document both the scenario that caused Mantis #516,
(which now causes only some overhead), and another performance problem.
And document a probable correctness problem that this diff *does not* fix,
for two reasons. First, the fix would require rewriting most or all
of the mode inference mechanism; second, since we haven't bumped into it
by now, we probably never will. (At least those of us who always declare
the modes of all predicates.)
compiler/deforest.m:
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mercury_compile_middle_passes.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/par_loop_control.m:
compiler/ssdebug.m:
compiler/structure_reuse.analysis.m:
compiler/structure_sharing.analysis.m:
Conform to the name changes in passes_aux.m.
compiler/hlds_out_pred.m:
Make it possible to diagnose problems with invalid procedures,
like this one, by (a) actually *printing* invalid procedures as well as
valid ones, but (b) *marking* them as being invalid.
tests/general/mode_inference_reorder.m:
Bring up to date the programming style of the test case that exhibited
the bug. Document its nonstandard naming scheme.
135 lines
3.9 KiB
Mathematica
135 lines
3.9 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mode_inference_reorder.
|
|
:- interface.
|
|
:- import_module io.
|
|
|
|
:- pred main(io::di, io::uo) is det.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module string.
|
|
|
|
:- type expr
|
|
---> expr_number(int)
|
|
; expr_plus(expr, expr)
|
|
; expr_minus(expr, expr)
|
|
; expr_times(expr, expr)
|
|
; expr_div(expr, expr).
|
|
|
|
main(!IO) :-
|
|
main2(!IO).
|
|
|
|
% This is separate from main/2 so that we can require the compiler
|
|
% to infer its mode.
|
|
main2(!IO) :-
|
|
io.write_string("calculator> ", !IO),
|
|
io.flush_output(!IO),
|
|
io.read_line(Res, !IO),
|
|
(
|
|
Res = error(_),
|
|
io.write_string("Error reading from stdin\n", !IO)
|
|
;
|
|
Res = eof,
|
|
io.write_string("EOF\n", !IO)
|
|
;
|
|
Res = ok(Line0),
|
|
list.delete_all(Line0, ' ', Line),
|
|
( if full_expr(X, Line, []) then
|
|
Num = eval_expr(X),
|
|
io.write_int(Num, !IO),
|
|
io.write_string("\n", !IO)
|
|
else
|
|
io.write_string("Syntax error\n", !IO)
|
|
),
|
|
main(!IO) % recursively call ourself for the next line(s)
|
|
).
|
|
|
|
:- func eval_expr(expr) = int.
|
|
|
|
eval_expr(expr_number(Num)) = Num.
|
|
eval_expr(expr_plus(X, Y)) = eval_expr(X) + eval_expr(Y).
|
|
eval_expr(expr_minus(X, Y)) = eval_expr(X) - eval_expr(Y).
|
|
eval_expr(expr_times(X, Y)) = eval_expr(X) * eval_expr(Y).
|
|
eval_expr(expr_div(X, Y)) = eval_expr(X) // eval_expr(Y).
|
|
|
|
% Simple recursive-descent parser for expressions.
|
|
% Note: this code uses the two terms "factor" and "term" ass backwards.
|
|
% People usually name the nonterminal for identifiers and parenthesized
|
|
% expressions "factor", but this parser calls it "term". On the other hand,
|
|
% people usually call one or more such entities connected by multiply and/or
|
|
% divide operations "terms", but this parser calls them "factors".
|
|
|
|
% :- pred full_expr(expr::out, list(char)::in, list(char)::out) is semidet.
|
|
full_expr(X) -->
|
|
ord_expr(X),
|
|
['\n'].
|
|
|
|
% :- pred expr(expr::out, list(char)::in, list(char)::out) is semidet.
|
|
ord_expr(Expr, DCG0, DCG) :-
|
|
expr2(Factor, Expr, DCG1, DCG),
|
|
factor(Factor, DCG0, DCG1),
|
|
true.
|
|
|
|
% :- pred expr2(expr::in, expr::out, list(char)::in, list(char)::out)
|
|
% is semidet.
|
|
% :- pred expr2(expr::out(free), expr::out(free), list(char)::out(free),
|
|
% list(char)::out(free)) is semidet.
|
|
expr2(Factor1, Expr) -->
|
|
( if [('+') `with_type` char] then
|
|
factor(Factor2),
|
|
expr2(expr_plus(Factor1, Factor2), Expr)
|
|
else if [('-') `with_type` char] then
|
|
factor(Factor2),
|
|
expr2(expr_minus(Factor1, Factor2), Expr)
|
|
else
|
|
{ Expr = Factor1 }
|
|
).
|
|
|
|
% :- pred factor(expr::out, list(char)::in, list(char)::out) is semidet.
|
|
factor(Factor) -->
|
|
term(Term),
|
|
factor2(Term, Factor).
|
|
|
|
% :- pred factor2(expr::in, expr::out, list(char)::in, list(char)::out)
|
|
% is semidet.
|
|
factor2(Term1, Factor) -->
|
|
( if [('*') `with_type` char] then
|
|
term(Term2),
|
|
factor2(expr_times(Term1, Term2), Factor)
|
|
else if [('/') `with_type` char] then
|
|
term(Term2),
|
|
factor2(expr_div(Term1, Term2), Factor)
|
|
else
|
|
{ Factor = Term1 }
|
|
).
|
|
|
|
% :- pred term(expr::out, list(char)::in, list(char)::out) is semidet.
|
|
term(Term) -->
|
|
( if const(Const) then
|
|
{ string.from_char_list(Const, ConstString) },
|
|
{ string.to_int(ConstString, Num) },
|
|
{ Term = expr_number(Num) }
|
|
else
|
|
['('], ord_expr(Term), [')']
|
|
).
|
|
|
|
% :- pred const(list(char)::out, list(char)::in, list(char)::out) is semidet.
|
|
const([Digit | Rest]) -->
|
|
digit(Digit),
|
|
( if const(Const) then
|
|
{ Rest = Const }
|
|
else
|
|
{ Rest = [] }
|
|
).
|
|
|
|
% :- pred digit(char::out, list(char)::in, list(char)::out) is semidet.
|
|
digit(Char) -->
|
|
[Char],
|
|
{ char.is_digit(Char) }.
|