Files
mercury/tests/exceptions/tricky_try_store.m
Zoltan Somogyi 33eb3028f5 Clean up the tests in half the test directories.
tests/accumulator/*.m:
tests/analysis_*/*.m:
tests/benchmarks*/*.m:
tests/debugger*/*.{m,exp,inp}:
tests/declarative_debugger*/*.{m,exp,inp}:
tests/dppd*/*.m:
tests/exceptions*/*.m:
tests/general*/*.m:
tests/grade_subdirs*/*.m:
tests/hard_coded*/*.m:
    Make these tests use four-space indentation, and ensure that
    each module is imported on its own line. (I intend to use the latter
    to figure out which subdirectories' tests can be executed in parallel.)

    These changes usually move code to different lines. For the debugger tests,
    specify the new line numbers in .inp files and expect them in .exp files.
2015-02-14 20:14:03 +11:00

64 lines
2.3 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ts=4 sw=4 et ft=mercury
%---------------------------------------------------------------------------%
%
% There is one tricky aspect with exception.try_store. If we're not
% careful, the user could use `store.extract_ref_value', which destroys
% the store and extracts the referenced value without making a copy.
% The user could then throw the extracted value, and if the handler gets
% both the extracted value and a unique version of the store, then it
% can update the reference, which would modify the extracted value,
% breaking referential transparency.
%
% In other words, with a naive implementation of `try_store',
% the following program could print out
%
% Result = exception(univ_cons("initial"))
% Result = exception(univ_cons("updated"))
% ...
%
% To avoid this, the implementation of try_store must make a copy of the
% thrown object before returning it from try_store.
%
% This test case checks that the implementation of try_store does the
% right thing in this tricky case.
:- module tricky_try_store.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is cc_multi.
:- implementation.
:- import_module exception.
:- import_module list.
:- import_module store.
:- pred tricky(store_ref(T, S), int, store(S), store(S)).
:- mode tricky(in, out, di, uo) is det.
tricky(Key, _Output, Store0, _Store) :-
store.extract_ref_value(Store0, Key, Value),
throw(Value).
main(!IO) :-
store.init(Store0),
store.new_ref(mklist("initial"), Key, Store0, Store1),
store.arg_ref(Key, 0, SubKey, Store1, Store2),
exception.try_store(tricky(Key), Result, Store2, Store3),
print("Result = ", !IO), print(Result, !IO), nl(!IO),
store.set_ref_value(SubKey, "updated", Store3, Store),
print("Result = ", !IO), print(Result, !IO), nl(!IO),
store.extract_ref_value(Store, Key, Val),
print("Val = ", !IO), print(Val, !IO), nl(!IO).
% XXX the current compiler has a bug whereby it generates static ground terms
% even for things that are used in `di' modes. To avoid that bug,
% we use the following hack -- a `pragma no_inline' function --
% to ensure that `["initial"]' doesn't get stored as a static constant.
:- func mklist(T) = list(T).
:- mode mklist(di) = uo is det.
:- pragma no_inline(mklist/1).
mklist(X) = [X].