mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
compiler/make_hlds_passes.m:
We used to add all modules imported by an ancestor of the current module
to the set of used modules. Once upon a time, this was meant to stop
the compiler generating misleading warnings about imports being unused
when the import wasn't even done by the current module. However, since
we introduced structured representations of import- and use_module
declarations and taught unused_imports.m to use them, that has not been
an issue. However, a bad side-effect remained, which was that if
a module A imported a module B but did not use it, or it imported
module B in its interface but did not use in its interface, then
any warning we could generate about that import being unused was
suppressed by any import of module B in any of module A's ancestors.
(The "shadowing" mentioned above.)
Fix the problem by adding modules imported by ancestors of the
current module NOT to the set of used modules, but to a new field
in the module_info.
compiler/hlds_module.m:
Add this new field. As it happens, it is not needed right now,
but it may be needed later.
Update some documentation.
Note an only-tangentially-related problem.
compiler/unused_imports.m:
Fix a bug that was hiding behind the shadowing, which was that whether
the text of the warning message we generated for an unused local import-
or use_module declaration could be affected by the presence of an
import- or use_module declaration in an ancestor module.
Improve debugging infrastructure.
Make a predicate name more descriptive.
NEWS:
Announce the bugfix.
compiler/add_pragma_tabling.m:
compiler/add_solver.m:
compiler/add_type.m:
compiler/parse_string_format.m:
compiler/recompilation.usage.m:
compiler/recompilation.used_file.m:
library/io.call_system.m:
library/io.text_read.m:
library/random.sfc32.m:
library/random.sfc64.m:
library/random.system_rng.m:
library/string.parse_runtime.m:
library/string.parse_util.m:
library/string.to_string.m:
library/thread.closeable_channel.m:
mdbcomp/feedback.automatic_parallelism.m:
Delete imports that the fixed compiler now generates unused import
warnings for.
480 lines
17 KiB
Mathematica
480 lines
17 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2014-2015, 2018-2020 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: string.parse_runtime.m.
|
|
%
|
|
% This module parses format strings for the runtime system;
|
|
% the module compiler/parse_string_format.m does the same job for the compiler.
|
|
% Any changes here will probably also require a corresponding change there.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module string.parse_runtime.
|
|
:- interface.
|
|
|
|
:- import_module string.parse_util.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_format_spec
|
|
---> spec_constant_string(
|
|
string
|
|
)
|
|
; spec_char(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
char
|
|
)
|
|
; spec_string(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
string_format_maybe_prec,
|
|
string
|
|
)
|
|
; spec_signed_int(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
string_format_maybe_prec,
|
|
sized_int
|
|
)
|
|
; spec_unsigned_int(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
string_format_maybe_prec,
|
|
string_format_int_base,
|
|
sized_int
|
|
)
|
|
; spec_uint(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
string_format_maybe_prec,
|
|
string_format_int_base,
|
|
sized_uint
|
|
)
|
|
; spec_float(
|
|
string_format_flags,
|
|
string_format_maybe_width,
|
|
string_format_maybe_prec,
|
|
string_format_float_kind,
|
|
float
|
|
).
|
|
|
|
:- type sized_int
|
|
---> sized_int(int)
|
|
; sized_int8(int8)
|
|
; sized_int16(int16)
|
|
; sized_int32(int32)
|
|
; sized_int64(int64).
|
|
|
|
:- type sized_uint
|
|
---> sized_uint(uint)
|
|
; sized_uint8(uint8)
|
|
; sized_uint16(uint16)
|
|
; sized_uint32(uint32)
|
|
; sized_uint64(uint64).
|
|
|
|
% This predicate parses the entire format string. When it encounters
|
|
% something that looks like a conversion specification (i.e. it starts
|
|
% with a '%' character), but which cannot be parsed as one, it records
|
|
% an error message, and keeps going.
|
|
%
|
|
% Note that making this predicate use an accumulator for the lists
|
|
% of specs and errors seen so far would yield cleaner code,
|
|
% but would probably be slower since our caller would have to unreverse
|
|
% the list of specs we return.
|
|
%
|
|
% The lack of tail recursion here should not be a problem, since no
|
|
% format string will be long enough to make us consume too much stack.
|
|
%
|
|
:- pred parse_format_string(list(char)::in,
|
|
list(poly_type)::in, int::in,
|
|
list(string_format_spec)::out, list(string_format_error)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
parse_format_string(!.Chars, !.PolyTypes, SpecNum, Specs, Errors) :-
|
|
gather_non_percent_chars(!.Chars, NonConversionSpecChars, GatherEndedBy),
|
|
(
|
|
GatherEndedBy = found_end_of_string,
|
|
Specs0 = [],
|
|
(
|
|
!.PolyTypes = [],
|
|
Errors = []
|
|
;
|
|
!.PolyTypes = [_ | _],
|
|
Error = error_extra_polytypes(SpecNum, list.length(!.PolyTypes)),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
GatherEndedBy = found_percent(!:Chars),
|
|
parse_conversion_specification(!Chars, !PolyTypes, SpecNum,
|
|
HeadSpec, HeadErrors),
|
|
parse_format_string(!.Chars, !.PolyTypes, SpecNum + 1,
|
|
TailSpecs, TailErrors),
|
|
(
|
|
HeadErrors = [],
|
|
Specs0 = [HeadSpec | TailSpecs],
|
|
Errors = TailErrors
|
|
;
|
|
HeadErrors = [_ | _],
|
|
Specs0 = TailSpecs,
|
|
Errors = HeadErrors ++ TailErrors
|
|
)
|
|
),
|
|
(
|
|
NonConversionSpecChars = [],
|
|
Specs = Specs0
|
|
;
|
|
NonConversionSpecChars = [_ | _],
|
|
NonConversionSpecString =
|
|
string.from_char_list(NonConversionSpecChars),
|
|
StringSpec = spec_constant_string(NonConversionSpecString),
|
|
Specs = [StringSpec | Specs0]
|
|
).
|
|
|
|
% Each conversion specification starts with the character '%' (which
|
|
% our caller has already read) and ends with a conversion specifier.
|
|
% In between there may be (in this order) zero or more flags, an optional
|
|
% minimum field width, and an optional precision.
|
|
%
|
|
:- pred parse_conversion_specification(list(char)::in, list(char)::out,
|
|
list(poly_type)::in, list(poly_type)::out, int::in,
|
|
string_format_spec::out, list(string_format_error)::out) is det.
|
|
|
|
parse_conversion_specification(!Chars, !PolyTypes, SpecNum, Spec, Errors) :-
|
|
Flags0 = string_format_flags(flag_hash_clear, flag_space_clear,
|
|
flag_zero_clear, flag_minus_clear, flag_plus_clear),
|
|
gather_flag_chars(!Chars, Flags0, Flags),
|
|
get_optional_width(!Chars, !PolyTypes, SpecNum, MaybeWidth, WidthErrors),
|
|
get_optional_prec(!Chars, !PolyTypes, SpecNum, MaybePrec, PrecErrors),
|
|
get_first_spec(!Chars, !PolyTypes, Flags, MaybeWidth, MaybePrec, SpecNum,
|
|
Spec, SpecErrors),
|
|
Errors = WidthErrors ++ PrecErrors ++ SpecErrors.
|
|
|
|
% Do we have a minimum field width? If yes, get it.
|
|
%
|
|
:- pred get_optional_width(list(char)::in, list(char)::out,
|
|
list(poly_type)::in, list(poly_type)::out, int::in,
|
|
string_format_maybe_width::out, list(string_format_error)::out) is det.
|
|
|
|
get_optional_width(!Chars, !PolyTypes, SpecNum, MaybeWidth, Errors) :-
|
|
( if !.Chars = ['*' | !:Chars] then
|
|
(
|
|
!.PolyTypes = [PolyType | !:PolyTypes],
|
|
( if PolyType = i(PolyWidth) then
|
|
MaybeWidth = specified_width(PolyWidth),
|
|
Errors = []
|
|
else
|
|
MaybeWidth = no_specified_width,
|
|
Errors = [error_nonint_star_width(SpecNum,
|
|
poly_type_to_kind(PolyType))]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
MaybeWidth = no_specified_width,
|
|
Errors = [error_missing_star_width(SpecNum)]
|
|
)
|
|
else if get_nonzero_number_prefix(!Chars, Width) then
|
|
MaybeWidth = specified_width(Width),
|
|
Errors = []
|
|
else
|
|
MaybeWidth = no_specified_width,
|
|
Errors = []
|
|
).
|
|
|
|
% Do we have a precision? If yes, get it.
|
|
%
|
|
:- pred get_optional_prec(list(char)::in, list(char)::out,
|
|
list(poly_type)::in, list(poly_type)::out, int::in,
|
|
string_format_maybe_prec::out, list(string_format_error)::out) is det.
|
|
|
|
get_optional_prec(!Chars, !PolyTypes, SpecNum, MaybePrec, Errors) :-
|
|
( if !.Chars = ['.' | !:Chars] then
|
|
( if !.Chars = ['*' | !:Chars] then
|
|
(
|
|
!.PolyTypes = [PolyType | !:PolyTypes],
|
|
( if PolyType = i(PolyPrec) then
|
|
MaybePrec = specified_prec(PolyPrec),
|
|
Errors = []
|
|
else
|
|
MaybePrec = no_specified_prec,
|
|
Errors = [error_nonint_star_prec(SpecNum,
|
|
poly_type_to_kind(PolyType))]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
MaybePrec = no_specified_prec,
|
|
Errors = [error_missing_star_prec(SpecNum)]
|
|
)
|
|
else
|
|
% This treats an empty string as an EXPLICIT zero.
|
|
get_number_prefix(!Chars, Prec),
|
|
MaybePrec = specified_prec(Prec),
|
|
Errors = []
|
|
)
|
|
else
|
|
MaybePrec = no_specified_prec,
|
|
Errors = []
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% get_first_spec(!Chars, !PolyTypes, Flags, MaybeWidth, MaybePrec,
|
|
% SpecNum, Spec, Errors):
|
|
%
|
|
% Try to read one conversion specifier, whose percent sign, flags,
|
|
% width and precision have already been read, from !Chars.
|
|
%
|
|
% If successful, consume the corresponding poly_type from !PolyTypes,
|
|
% we return the specifier as Spec and return an empty error list.
|
|
%
|
|
% If there is a problem, we return a garbage Spec and a nonempty
|
|
% errors list. We also consume the poly_type that corresponds
|
|
% (or at least, looks like it corresponds) to the specifier,
|
|
% if there is one.
|
|
%
|
|
:- pred get_first_spec(list(char)::in, list(char)::out,
|
|
list(poly_type)::in, list(poly_type)::out,
|
|
string_format_flags::in, string_format_maybe_width::in,
|
|
string_format_maybe_prec::in, int::in,
|
|
string_format_spec::out, list(string_format_error)::out) is det.
|
|
|
|
get_first_spec(!Chars, !PolyTypes, _Flags, _MaybeWidth, _MaybePrec, SpecNum,
|
|
Spec, Errors) :-
|
|
!.Chars = [],
|
|
Spec = spec_constant_string(""),
|
|
Errors = [error_no_specifier(SpecNum, list.length(!.PolyTypes))].
|
|
get_first_spec(!Chars, !PolyTypes, !.Flags, MaybeWidth, MaybePrec, SpecNum,
|
|
Spec, Errors) :-
|
|
!.Chars = [SpecChar | !:Chars],
|
|
( if
|
|
parse_spec(SpecChar, !PolyTypes, !.Flags, MaybeWidth, MaybePrec,
|
|
SpecNum, SpecPrime, ErrorsPrime)
|
|
then
|
|
Spec = SpecPrime,
|
|
Errors = ErrorsPrime
|
|
else
|
|
Error = error_unknown_specifier(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
).
|
|
|
|
:- pred parse_spec(char::in, list(poly_type)::in, list(poly_type)::out,
|
|
string_format_flags::in, string_format_maybe_width::in,
|
|
string_format_maybe_prec::in, int::in,
|
|
string_format_spec::out, list(string_format_error)::out) is semidet.
|
|
|
|
parse_spec(SpecChar, !PolyTypes, !.Flags, MaybeWidth, MaybePrec,
|
|
SpecNum, Spec, Errors) :-
|
|
require_switch_arms_det [SpecChar]
|
|
(
|
|
SpecChar = '%',
|
|
Spec = spec_constant_string("%"),
|
|
Errors = []
|
|
;
|
|
( SpecChar = 'd'
|
|
; SpecChar = 'i'
|
|
),
|
|
(
|
|
!.PolyTypes = [SpecPolyType | !:PolyTypes],
|
|
(
|
|
( SpecPolyType = i(Int), SizedInt = sized_int(Int)
|
|
; SpecPolyType = i8(Int8), SizedInt = sized_int8(Int8)
|
|
; SpecPolyType = i16(Int16), SizedInt = sized_int16(Int16)
|
|
; SpecPolyType = i32(Int32), SizedInt = sized_int32(Int32)
|
|
; SpecPolyType = i64(Int64), SizedInt = sized_int64(Int64)
|
|
),
|
|
% Base is always decimal.
|
|
Spec = spec_signed_int(!.Flags, MaybeWidth, MaybePrec,
|
|
SizedInt),
|
|
Errors = []
|
|
;
|
|
( SpecPolyType = f(_)
|
|
; SpecPolyType = u(_)
|
|
; SpecPolyType = u8(_)
|
|
; SpecPolyType = u16(_)
|
|
; SpecPolyType = u32(_)
|
|
; SpecPolyType = u64(_)
|
|
; SpecPolyType = s(_)
|
|
; SpecPolyType = c(_)
|
|
),
|
|
Error = error_wrong_polytype(SpecNum, SpecChar,
|
|
poly_type_to_kind(SpecPolyType)),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
(
|
|
SpecChar = 'o',
|
|
Base = base_octal
|
|
;
|
|
SpecChar = 'u',
|
|
Base = base_decimal
|
|
;
|
|
SpecChar = 'x',
|
|
Base = base_hex_lc
|
|
;
|
|
SpecChar = 'X',
|
|
Base = base_hex_uc
|
|
;
|
|
SpecChar = 'p',
|
|
Base = base_hex_p,
|
|
% XXX This should not be necessary.
|
|
!Flags ^ flag_hash := flag_hash_set
|
|
),
|
|
(
|
|
!.PolyTypes = [SpecPolyType | !:PolyTypes],
|
|
(
|
|
( SpecPolyType = i(Int), SizedInt = sized_int(Int)
|
|
; SpecPolyType = i8(Int8), SizedInt = sized_int8(Int8)
|
|
; SpecPolyType = i16(Int16), SizedInt = sized_int16(Int16)
|
|
; SpecPolyType = i32(Int32), SizedInt = sized_int32(Int32)
|
|
; SpecPolyType = i64(Int64), SizedInt = sized_int64(Int64)
|
|
),
|
|
Spec = spec_unsigned_int(!.Flags, MaybeWidth, MaybePrec, Base,
|
|
SizedInt),
|
|
Errors = []
|
|
;
|
|
( SpecPolyType = u(UInt), SizedUInt = sized_uint(UInt)
|
|
; SpecPolyType = u8(UInt8), SizedUInt = sized_uint8(UInt8)
|
|
; SpecPolyType = u16(UInt16), SizedUInt = sized_uint16(UInt16)
|
|
; SpecPolyType = u32(UInt32), SizedUInt = sized_uint32(UInt32)
|
|
; SpecPolyType = u64(UInt64), SizedUInt = sized_uint64(UInt64)
|
|
),
|
|
Spec = spec_uint(!.Flags, MaybeWidth, MaybePrec, Base,
|
|
SizedUInt),
|
|
Errors = []
|
|
;
|
|
( SpecPolyType = f(_)
|
|
; SpecPolyType = s(_)
|
|
; SpecPolyType = c(_)
|
|
),
|
|
Error = error_wrong_polytype(SpecNum, SpecChar,
|
|
poly_type_to_kind(SpecPolyType)),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
(
|
|
SpecChar = 'e',
|
|
FloatKind = kind_e_scientific_lc
|
|
;
|
|
SpecChar = 'E',
|
|
FloatKind = kind_e_scientific_uc
|
|
;
|
|
SpecChar = 'f',
|
|
FloatKind = kind_f_plain_lc
|
|
;
|
|
SpecChar = 'F',
|
|
FloatKind = kind_f_plain_uc
|
|
;
|
|
SpecChar = 'g',
|
|
FloatKind = kind_g_flexible_lc
|
|
;
|
|
SpecChar = 'G',
|
|
FloatKind = kind_g_flexible_uc
|
|
),
|
|
(
|
|
!.PolyTypes = [SpecPolyType | !:PolyTypes],
|
|
( if SpecPolyType = f(Float) then
|
|
Spec = spec_float(!.Flags, MaybeWidth, MaybePrec,
|
|
FloatKind, Float),
|
|
Errors = []
|
|
else
|
|
Error = error_wrong_polytype(SpecNum, SpecChar,
|
|
poly_type_to_kind(SpecPolyType)),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
SpecChar = 'c',
|
|
(
|
|
!.PolyTypes = [SpecPolyType | !:PolyTypes],
|
|
( if SpecPolyType = c(Char) then
|
|
% XXX Should we generate an error if MaybePrec = yes(...)?
|
|
Spec = spec_char(!.Flags, MaybeWidth, Char),
|
|
Errors = []
|
|
else
|
|
Error = error_wrong_polytype(SpecNum, SpecChar,
|
|
poly_type_to_kind(SpecPolyType)),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
SpecChar = 's',
|
|
(
|
|
!.PolyTypes = [SpecPolyType | !:PolyTypes],
|
|
( if SpecPolyType = s(Str) then
|
|
Spec = spec_string(!.Flags, MaybeWidth, MaybePrec, Str),
|
|
Errors = []
|
|
else
|
|
Error = error_wrong_polytype(SpecNum, SpecChar,
|
|
poly_type_to_kind(SpecPolyType)),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
;
|
|
!.PolyTypes = [],
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Spec = spec_constant_string(""),
|
|
Errors = [Error]
|
|
)
|
|
).
|
|
|
|
:- func poly_type_to_kind(poly_type) = poly_kind.
|
|
|
|
poly_type_to_kind(c(_)) = poly_kind_char.
|
|
poly_type_to_kind(s(_)) = poly_kind_str.
|
|
poly_type_to_kind(i(_)) = poly_kind_int.
|
|
poly_type_to_kind(i8(_)) = poly_kind_int8.
|
|
poly_type_to_kind(i16(_)) = poly_kind_int16.
|
|
poly_type_to_kind(i32(_)) = poly_kind_int32.
|
|
poly_type_to_kind(i64(_)) = poly_kind_int64.
|
|
poly_type_to_kind(u(_)) = poly_kind_uint.
|
|
poly_type_to_kind(u8(_)) = poly_kind_uint8.
|
|
poly_type_to_kind(u16(_)) = poly_kind_uint16.
|
|
poly_type_to_kind(u32(_)) = poly_kind_uint32.
|
|
poly_type_to_kind(u64(_)) = poly_kind_uint64.
|
|
poly_type_to_kind(f(_)) = poly_kind_float.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module string.parse_runtime.
|
|
%---------------------------------------------------------------------------%
|