mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
216 lines
7.3 KiB
Mathematica
216 lines
7.3 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Author: original version written by Pedro Mariano; reformatted for our
|
|
% standard style, cut down to minimal size and the problem diagnosed by zs.
|
|
%
|
|
% Since the two predicates lattice_good and lattice_bad have the same
|
|
% semantics, invoking solutions.solutions on them should yield the same
|
|
% results. The bug is that the two calls to solutions yield different results:
|
|
% calling solutions on lattice_good returns both expected results, while
|
|
% calling solutions on lattice_bad returns only one.
|
|
%
|
|
% The source of the problem is that the C code we generate for lattice_bad
|
|
%
|
|
% 1. creates a memory cell for G and fills in the xsize and ysize fields;
|
|
% 2. creates a choice point for the disjunction;
|
|
% 3a. fills in the boundary field with `torus' in the first arm of the
|
|
% disjunction, and
|
|
% 3b. fills in the boundary field with `closed' in the second arm of the
|
|
% disjunction; and then
|
|
% 4. the code after the disjunction returns G.
|
|
%
|
|
% When you print the result after each exit from lattice_bad, you get the
|
|
% the right answer. The problem is that the code in the second arm of the
|
|
% disjunction CLOBBERS a field in the memory cell returned by the first arm.
|
|
% EVERY ANSWER returned by lattice_bad specifies the same memory address for G.
|
|
% When the solutions predicate sorts the set of solutions, all the solutions
|
|
% it sorts look exactly alike, because they are all stored at the same address.
|
|
% At that time, that cell will contain the last solution returned.
|
|
%
|
|
% In a sense, neither the code generated for lattice_bad nor the solutions
|
|
% predicate are to blame; they are both doing what they are supposed to.
|
|
% The actual problem is that the code of solutions makes an assumption
|
|
% (that the memory cells containing the solutions are static and won't change)
|
|
% that is incorrect in the presence of code that fills in some parts of a
|
|
% memory cell *before* a disjunction (creating the cell, and thus establishing
|
|
% its address), and some other parts *inside* the disjunction.
|
|
%
|
|
% We could fix the problem in several ways.
|
|
%
|
|
% The quickest fix would be to make solutions deep copy all answers. Due to
|
|
% the potentially horrendous runtime cost, I don't think we should do this.
|
|
%
|
|
% We could generate an error message for disjunctions that have variables
|
|
% that are bound but not ground at the start of the disjunction and become
|
|
% more instantiated (maybe ground, maybe not) during the disjunction.
|
|
%
|
|
% The most complete solution would be to look for such variables, but instead
|
|
% of generating an error for them, we could modify the HLDS to eliminate the
|
|
% condition that causes the problem. We can do this by introducing a proxy
|
|
% for each such variable, a proxy that is set entirely inside the disjuncts.
|
|
%
|
|
% The idea is to replace the original HLDS
|
|
%
|
|
% bug311.lattice_bad(X, Y, G) :-
|
|
% (
|
|
% G = bug311.lattice(X, V_9, V_10),
|
|
% G = bug311.lattice(V_11, Y, V_12),
|
|
% (
|
|
% V_8 = bug311.torus,
|
|
% G = bug311.lattice(V_13, V_14, V_8)
|
|
% ;
|
|
% V_7 = bug311.closed,
|
|
% G = bug311.lattice(V_15, V_16, V_7)
|
|
% )
|
|
% ).
|
|
%
|
|
% with
|
|
%
|
|
% bug311.lattice_bad(X, Y, G) :-
|
|
% (
|
|
% G_prime1 = bug311.lattice(X, V_9, V_10),
|
|
% G_prime1 = bug311.lattice(V_11, Y, V_12),
|
|
% (
|
|
% G_prime1 = bug311.lattice(G_prime1_f1, G_prime1_f2, _),
|
|
% V_8 = bug311.torus,
|
|
% G = bug311.lattice(G_prime1_f1, G_prime1_f2, V_8)
|
|
% ;
|
|
% G_prime1 = bug311.lattice(G_prime1_f1, G_prime1_f2, _),
|
|
% V_7 = bug311.closed,
|
|
% G = bug311.lattice(G_prime1_f1, G_prime1_f2, V_7)
|
|
% )
|
|
% ).
|
|
%
|
|
% Note that in the original test case, G has TWO of its fields filled out
|
|
% by two SEPARATE disjunctions, so we would have to create TWO separate
|
|
% proxy variables for it.
|
|
%
|
|
% This change would have to be done by a new pass in the compiler, but
|
|
% this pass would have to be invoked only for predicates that (a) contain
|
|
% disjunctions, and (b) some of these disjunctions further instantiate
|
|
% already-partially-filled-in memory cells. We could have the pre-codegen
|
|
% simplify pass look for such situations, and record the set of variables
|
|
% involved (G in this case); the new pass would have to be invoked
|
|
% only if the set of these variables is not empty. Since this problem
|
|
% has existed for a long time and does not seem to have been noticed before,
|
|
% this definitely will be a rare occurrence.
|
|
%
|
|
% Note that the problem exists in both for low and high level C grades, and
|
|
% probably exists for the other MLDS grades as well.
|
|
%
|
|
% Note also that although this solution will fix the bug by making solutions
|
|
% generate the right set of solutions, the copying involved will slow down
|
|
% the generated code. The more fields filled in piecemeal, the more the cell
|
|
% will be copied, and the slower the code will be.
|
|
%
|
|
% Therefore for the original submitter, I propose replacing lattice_bad
|
|
% (which exhibits the bug) with lattice_bad_fixed (which does not).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module bug311.
|
|
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
|
|
:- pred main(io::di, io::uo) is det.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module list.
|
|
:- import_module solutions.
|
|
|
|
:- type geometry
|
|
---> wellmixed
|
|
; lattice(
|
|
xsize :: int,
|
|
ysize :: int,
|
|
boundary :: boundary
|
|
) .
|
|
|
|
:- type boundary
|
|
---> torus
|
|
; closed.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
main(!IO) :-
|
|
X = 2,
|
|
Y = 3,
|
|
solutions.solutions(lattice_good(X, Y), SG),
|
|
io.print_line(SG, !IO),
|
|
solutions.solutions(lattice_bad(X, Y), SB),
|
|
io.print_line(SB, !IO),
|
|
solutions.solutions(lattice_bad_fixed(X, Y), SBF),
|
|
io.print_line(SBF, !IO),
|
|
|
|
solutions.solutions(lattice_bad_3(X, Y), SB3),
|
|
io.print_line(SB3, !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred lattice_good(int::in, int::in, geometry::out) is multi.
|
|
|
|
lattice_good(X, Y, lattice(X, Y, torus)).
|
|
lattice_good(X, Y, lattice(X, Y, closed)).
|
|
|
|
:- pred lattice_bad(int::in, int::in, geometry::out) is multi.
|
|
|
|
lattice_bad(X, Y, G) :-
|
|
G ^ xsize = X,
|
|
G ^ ysize = Y,
|
|
(
|
|
G ^ boundary = torus
|
|
;
|
|
G ^ boundary = closed
|
|
).
|
|
|
|
:- pred lattice_bad_3(int::in, int::in, {geometry, geometry, geometry}::out)
|
|
is multi.
|
|
|
|
lattice_bad_3(X, Y, Tuple) :-
|
|
G1 ^ xsize = X,
|
|
G1 ^ ysize = Y,
|
|
G2 ^ xsize = X,
|
|
G2 ^ ysize = Y,
|
|
G3 ^ xsize = X,
|
|
G3 ^ ysize = Y,
|
|
(
|
|
G1 ^ boundary = torus,
|
|
G2 ^ boundary = torus,
|
|
G3 ^ boundary = torus
|
|
;
|
|
G1 ^ boundary = closed,
|
|
G2 ^ boundary = closed,
|
|
G3 ^ boundary = closed
|
|
),
|
|
Tuple = {G1, G2, G3}.
|
|
|
|
:- pred lattice_bad_lambda(int::in, int::in, geometry::out) is multi.
|
|
|
|
lattice_bad_lambda(X, Y, G) :-
|
|
P =
|
|
( pred(YY::in, GG::out) is multi :-
|
|
GG ^ xsize = X,
|
|
GG ^ ysize = YY,
|
|
(
|
|
GG ^ boundary = torus
|
|
;
|
|
GG ^ boundary = closed
|
|
)
|
|
),
|
|
P(Y, G).
|
|
|
|
:- pred lattice_bad_fixed(int::in, int::in, geometry::out) is multi.
|
|
|
|
lattice_bad_fixed(X, Y, G) :-
|
|
(
|
|
B = torus
|
|
;
|
|
B = closed
|
|
),
|
|
G = lattice(X, Y, B).
|