Files
mercury/library/bit_buffer.m
Zoltan Somogyi c509c49bdc Add a mechanism for generating warnings when the various clauses of a predicate
Estimated hours taken: 24
Branches: main

Add a mechanism for generating warnings when the various clauses of a predicate
or function are not contiguous.

This mechanism consists of two options:

	--warn-non-contiguous-clauses
	--warn-non-contiguous-foreign-procs

The first option generates warnings when the Mercury clauses of a predicate
or function are not contiguous, but it ignores any foreign_procs of that
predicate or function, and thus allows these to be away from the Mercury
clauses and each other. This option is enabled by default.

The second option generating warnings unless both the Mercury clauses and
all the foreign_procs of the predicate or function are all contiguous.
This option is not enabled by default, because many library modules
group foreign_procs not by predicate, but by foreign language. (All C foreign
procs for a group of predicates, then all the Java foreign procs for that group
of predicates, etc.)

compiler/hlds_clauses.m:
	Store, next to the representation of each clause list, information
	about the locations (item numbers and context) of the clauses.
	We store two versions of this information, one version for each option.

	Make the predicates that access the clause list access the location
	information as well, to ensure that any code that adds clauses also
	records their location.

	Add a predicate that tests for non-contiguity.

	Add a specific type to represent the modes that a clause applies to.
	This replaces the error-prone scheme we used to use that represented
	the notion "this clause applies to all modes" with an empty list of
	modes. This allows us to remove the code in add_pragma.m that used
	to replace these empty lists with the list of actual modes they
	represented.

	Change the prefix on the fields of clauses_info to avoid ambiguities.
	Add a prefix to the names of the function symbols of the clauses_rep
	type to avoid ambiguities.

compiler/add_clause.m:
compiler/add_pragma.m:
	When adding Mercury clauses and pragma foreign_procs to a predicate or
	function, record the location of the clause or foreign_procs. We do so
	even if the clause or foreign_proc is overridden by another. For
	example, when compiling to C, a Mercury clause overrides an Erlang
	foreign_proc, and a C foreign_proc overrides a Mercury clause.

	Fix an old bug where a foreign_proc that should override Mercury
	clauses overrode only one Mercury clause, and left the others
	in the predicate, to yield a disjunction with some Mercury disjuncts
	and a foreign_proc disjunct. This disjunction would then yield
	determinism errors if it had outputs.

	The new code that fixes the bug has a much more direct implicit
	correctness argument, and should be significantly easier to understand.
	It also avoids doing unnecessary work. (The old code could make a
	decision to ignore a clause, yet still proceed to transform it,
	just to ignore the result of the transformation.)

compiler/options.m:
	Add the new options.

doc/user_guide.texi:
	Document the new options. Fix an inconsistency between options.m and
	user_guide.texi for a nearby option.

compiler/make_hlds_passes.m:
	Pass the information that add_clause.m and add_pragma.m need.

compiler/typecheck.m:
	Detect non-contiguous clauses and call typecheck_errors to generate
	error messages.

compiler/typecheck_errors.m:
	Add functions for formatting error messages about non-contiguous
	clauses.

compiler/hlds_out.m:
	Do not print the modes to which a clause applies for the usual case,
	in which the clause applies to all modes.

compiler/clause_to_proc.m:
	Simplify some code.

	Rename a predicate to better reflect its purpose.

	Conform to the changes above.

compiler/intermod.m:
	Rename a predicate to avoid ambiguities.

	Conform to the changes above.

compiler/add_class.m:
compiler/add_pred.m:
compiler/add_special_pred.m:
compiler/assertion.m:
compiler/build_mode_constraints.m:
compiler/dead_proc_elim.m:
compiler/dependency_graph.m:
compiler/goal_path.m:
compiler/headvar_names.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_pred.m:
compiler/implementation_defined_literals.m:
compiler/mode_constraints.m:
compiler/modes.m:
compiler/ordering_mode_constraints.m:
compiler/polymorphism.m:
compiler/post_typecheck.m:
compiler/proc_gen.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/type_constraints.m:
compiler/unify_proc.m:
compiler/unused_imports.m:
	Conform to the changes above.

compiler/goal_form.m:
	The new warnings pointed out a non-contiguous clause in goal_form.m.
	Since this clause happened to be a duplicate of another clause, this
	diff deletes it. The duplicate clause was not detected because the
	predicate is semidet, and has no outputs.

compiler/mlds_to_c.m:
compiler/rbmm.points_to_analysis.m:
deep_profiler/measurements.m:
library/library.m:
library/list.m:
	Fix non-contiguous clauses pointed out by the new warnings.

library/bit_buffer.m:
	Fix programming style.

tests/invalid/types2.err_exp:
	This test has non-contiguous clauses, so expect the new warning.

tests/warnings/warn_contiguous.{m,exp}:
tests/warnings/warn_non_contiguous.{m,exp}:
tests/warnings/warn_non_contiguous_foreign.{m,exp}:
tests/warnings/warn_non_contiguous_foreign_group.{m,exp}:
	New test cases that exercise the new capability.

tests/warnings/Mmakefile:
tests/warnings/Mercury.options:
	Enable and specify the options for the new tests.
2009-08-19 07:45:13 +00:00

356 lines
12 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ts=4 sw=4 et ft=mercury
%-----------------------------------------------------------------------------%
% Copyright (C) 2007, 2009 The University of Melbourne
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: bit_buffer.m.
% Main author: stayl.
% Stability: low.
%
% A bit buffer provides an interface between bit-oriented I/O requests
% and byte-oriented streams. The useful part of the interface is defined
% in bit_buffer.read and bit_buffer.write.
%
% CAVEAT: the user is referred to the documentation in the header
% of array.m regarding programming with unique objects (the compiler
% does not currently recognise them, hence we are forced to use
% non-unique modes until the situation is rectified; this places
% a small burden on the programmer to ensure the correctness of his
% code that would otherwise be assured by the compiler.)
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module bit_buffer.
:- interface.
:- import_module bitmap.
:- import_module stream.
:- include_module bit_buffer.read.
:- include_module bit_buffer.write.
% An error_stream throws an `error_stream_error' exception if any of
% its output methods are called, or returns an `error_stream_error'
% if any of its input methods are called.
%
:- type error_stream ---> error_stream.
:- type error_state ---> error_state.
:- type error_stream_error ---> error_stream_error.
:- instance stream.error(error_stream_error).
:- instance stream.stream(error_stream, error_state).
:- instance stream.input(error_stream, error_state).
:- instance stream.bulk_reader(error_stream, byte_index, bitmap,
error_state, error_stream_error).
:- instance stream.output(error_stream, error_state).
:- instance stream.writer(error_stream, bitmap.slice, error_state).
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module bool.
:- import_module exception.
:- import_module int.
:- import_module list.
:- instance stream.error(error_stream_error) where
[
error_message(_) = "method called for error_stream"
].
:- instance stream.stream(error_stream, error_state) where
[
name(_Stream, "error_stream", !State)
].
:- instance stream.input(error_stream, error_state) where
[].
:- instance stream.bulk_reader(error_stream, byte_index, bitmap,
error_state, error_stream_error) where
[
bulk_get(_, _, _, !BM, 0, error(error_stream_error), !State)
].
:- instance stream.output(error_stream, error_state) where
[
flush(_, !State) :- throw(error_stream_error)
].
:- instance stream.writer(error_stream, bitmap.slice, error_state)
where
[
put(_, _, _, _) :- throw(error_stream_error)
].
% The bitmap has room for the chunk size given as an argument
% to `new', plus a word.
%
% This means that a for a write buffer a word can always
% be written to the buffer, and the buffer will be flushed
% if the position is greater than the chunk size.
%
% For a read_buffer, bits will be read from the input stream
% and placed starting at bit number `bits_per_int'. When the
% buffer is nearly exhausted (less than a word left), the last
% word is copied to the start of the buffer and the buffer is
% refilled.
%
:- type bit_buffer(Stream, State, Error)
---> bit_buffer(
mer_bitmap :: bitmap,
mer_pos :: bit_index,
mer_size :: num_bits,
mer_use_stream :: bool,
mer_stream :: Stream,
mer_state :: State,
% For write buffers only.
% If we're not writing to a stream, keep a list of filled
% bitmaps in reverse order. These will be concatenated into
% a single bitmap by finalize_to_bitmap.
%
mer_filled_bitmaps :: list(bitmap),
% For read buffers only. The first error found when reading
% from a stream. Subsequent calls will return this error.
%
mer_read_status :: stream.res(Error)
).
:- type bit_buffer(Stream, State) == bit_buffer(Stream, State, {}).
% XXX These should be unique.
:- mode bit_buffer_ui == in.
:- mode bit_buffer_di == in.
:- mode bit_buffer_uo == out.
% Allocating memory for every read or write would be bad, so
% we manually perform destructive update in C.
%
:- pragma foreign_decl("C", "
typedef struct {
MR_BitmapPtr ML_bit_buffer_bitmap;
MR_Integer ML_bit_buffer_pos;
MR_Integer ML_bit_buffer_size;
MR_Word ML_bit_buffer_use_stream;
MR_Word ML_bit_buffer_stream;
MR_Word ML_bit_buffer_state;
MR_Word ML_bit_buffer_filled_bitmaps;
MR_Word ML_bit_buffer_read_status;
} ML_BitBuffer;
typedef ML_BitBuffer *ML_BitBufferPtr;
").
:- pragma foreign_type("C", bit_buffer(Stream, State, Error),
"ML_BitBufferPtr", [can_pass_as_mercury_type]).
:- func new_buffer(bitmap, bit_index, num_bits, bool, Stream, State) =
bit_buffer(Stream, State, Error).
new_buffer(BM, Pos, Size, UseStream, Stream, State) =
( if Size =< 0 then
throw("bit_buffer: invalid buffer size")
else
new_buffer_2(BM, Pos, Size, UseStream, Stream, State, ok)
).
:- func new_buffer_2(bitmap, num_bits, bit_index, bool,
Stream, State, stream.res(Error)) = bit_buffer(Stream, State, Error).
new_buffer_2(BM, Pos, Size, UseStream, Stream, State, ReadStatus) =
bit_buffer(BM, Pos, Size, UseStream, Stream, State, [], ReadStatus).
:- pragma foreign_proc("C",
new_buffer_2(BM::in, Pos::in, Size::in, UseStream::in,
Stream::in, State::in, ReadStatus::in) = (Buffer::out),
[will_not_call_mercury, promise_pure],
"{
Buffer = MR_GC_NEW(ML_BitBuffer);
Buffer->ML_bit_buffer_bitmap = BM;
Buffer->ML_bit_buffer_pos = Pos;
Buffer->ML_bit_buffer_size = Size;
Buffer->ML_bit_buffer_use_stream = UseStream;
Buffer->ML_bit_buffer_stream = Stream;
Buffer->ML_bit_buffer_state = State;
Buffer->ML_bit_buffer_filled_bitmaps = MR_list_empty();
Buffer->ML_bit_buffer_read_status = ReadStatus;
}").
:- func (bit_buffer(_, _, _)::bit_buffer_ui) ^ bitmap =
(bitmap::bitmap_uo) is det.
:- func (bit_buffer(_, _, _)::bit_buffer_ui) ^ pos = (int::out) is det.
:- func (bit_buffer(_, _, _)::bit_buffer_ui) ^ size = (int::out) is det.
:- func (bit_buffer(_, _, _)::bit_buffer_ui) ^ use_stream = (bool::out) is det.
:- func (bit_buffer(Stream, _, _)::bit_buffer_ui) ^ stream =
(Stream::out) is det.
:- func (bit_buffer(_, State, _)::bit_buffer_ui) ^ state = (State::uo) is det.
:- func (bit_buffer(_, _, _)::bit_buffer_ui) ^ filled_bitmaps =
(list(bitmap)::out) is det.
:- func (bit_buffer(_, _, Error)::bit_buffer_ui) ^ read_status =
(stream.res(Error)::out) is det.
Buffer ^ bitmap = Buffer ^ mer_bitmap.
Buffer ^ pos = Buffer ^ mer_pos.
Buffer ^ size = Buffer ^ mer_size.
Buffer ^ use_stream = Buffer ^ mer_use_stream.
Buffer ^ stream = Buffer ^ mer_stream.
Buffer ^ state = unsafe_promise_unique(Buffer ^ mer_state).
Buffer ^ filled_bitmaps = Buffer ^ mer_filled_bitmaps.
Buffer ^ read_status = Buffer ^ mer_read_status.
:- pragma foreign_proc("C",
bitmap(Buffer::bit_buffer_ui) = (BM::bitmap_uo),
[will_not_call_mercury, promise_pure],
"
BM = Buffer->ML_bit_buffer_bitmap;
").
:- pragma foreign_proc("C",
pos(Buffer::bit_buffer_ui) = (Pos::out),
[will_not_call_mercury, promise_pure],
"
Pos = Buffer->ML_bit_buffer_pos;
").
:- pragma foreign_proc("C",
size(Buffer::bit_buffer_ui) = (Size::out),
[will_not_call_mercury, promise_pure],
"
Size = Buffer->ML_bit_buffer_size;
").
:- pragma foreign_proc("C",
use_stream(Buffer::bit_buffer_ui) = (UseStream::out),
[will_not_call_mercury, promise_pure],
"
UseStream = Buffer->ML_bit_buffer_use_stream;
").
:- pragma foreign_proc("C",
stream(Buffer::bit_buffer_ui) = (Stream::out),
[will_not_call_mercury, promise_pure],
"
Stream = Buffer->ML_bit_buffer_stream;
").
:- pragma foreign_proc("C",
state(Buffer::bit_buffer_ui) = (State::uo),
[will_not_call_mercury, promise_pure],
"
State = Buffer->ML_bit_buffer_state;
").
:- pragma foreign_proc("C",
filled_bitmaps(Buffer::bit_buffer_ui) = (FilledBMs::out),
[will_not_call_mercury, promise_pure],
"
FilledBMs = Buffer->ML_bit_buffer_filled_bitmaps;
").
:- pragma foreign_proc("C",
read_status(Buffer::bit_buffer_ui) = (ReadStatus::out),
[will_not_call_mercury, promise_pure],
"
ReadStatus = Buffer->ML_bit_buffer_read_status;
").
:- pred set_all(bitmap::bitmap_di, bit_index::in, num_bits::in, State::di,
list(bitmap)::in, bit_buffer(Stream, State, Error)::bit_buffer_di,
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
set_all(BM, Pos, Size, State, FilledBMs, !Buffer) :-
!Buffer ^ mer_bitmap := BM,
!Buffer ^ mer_pos := Pos,
!Buffer ^ mer_state := State,
!Buffer ^ mer_filled_bitmaps := FilledBMs,
!Buffer ^ mer_size := Size.
:- pragma foreign_proc("C",
set_all(BM::bitmap_di, Pos::in, Size::in, State::di, FilledBMs::in,
Buffer0::bit_buffer_di, Buffer::bit_buffer_uo),
[will_not_call_mercury, promise_pure],
"
Buffer = Buffer0;
Buffer->ML_bit_buffer_bitmap = BM;
Buffer->ML_bit_buffer_pos = Pos;
Buffer->ML_bit_buffer_size = Size;
Buffer->ML_bit_buffer_state = State;
Buffer->ML_bit_buffer_filled_bitmaps = FilledBMs;
").
:- pred set_bitmap(bitmap::bitmap_di, bit_index::in,
bit_buffer(Stream, State, Error)::bit_buffer_di,
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
set_bitmap(BM, Pos, !Buffer) :-
!Buffer ^ mer_bitmap := BM,
!Buffer ^ mer_pos := Pos.
:- pragma foreign_proc("C",
set_bitmap(BM::bitmap_di, Pos::in,
Buffer0::bit_buffer_di, Buffer::bit_buffer_uo),
[will_not_call_mercury, promise_pure],
"
Buffer = Buffer0;
Buffer->ML_bit_buffer_bitmap = BM;
Buffer->ML_bit_buffer_pos = Pos;
").
:- pred set_state(State::di,
bit_buffer(Stream, State, Error)::bit_buffer_di,
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
set_state(State, !Buffer) :-
!:Buffer = !.Buffer ^ mer_state := State.
:- pragma foreign_proc("C",
set_state(State::di, Buffer0::bit_buffer_di, Buffer::bit_buffer_uo),
[will_not_call_mercury, promise_pure],
"
Buffer = Buffer0;
Buffer->ML_bit_buffer_state = State;
").
:- pred set_use_stream(bool::in,
bit_buffer(Stream, State, Error)::bit_buffer_di,
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
set_use_stream(UseStream, !Buffer) :-
!:Buffer = !.Buffer ^ mer_use_stream := UseStream.
:- pragma foreign_proc("C",
set_use_stream(UseStream::in,
Buffer0::bit_buffer_di, Buffer::bit_buffer_uo),
[will_not_call_mercury, promise_pure],
"
Buffer = Buffer0;
Buffer->ML_bit_buffer_use_stream = UseStream;
").
:- pred set_read_status(stream.res(Error)::in,
bit_buffer(Stream, State, Error)::bit_buffer_di,
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
set_read_status(ReadStatus, !Buffer) :-
!:Buffer = !.Buffer ^ mer_read_status := ReadStatus.
:- pragma foreign_proc("C",
set_read_status(ReadStatus::in,
Buffer0::bit_buffer_di, Buffer::bit_buffer_uo),
[will_not_call_mercury, promise_pure],
"
Buffer = Buffer0;
Buffer->ML_bit_buffer_read_status = ReadStatus;
").
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%