mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +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.
424 lines
14 KiB
Mathematica
424 lines
14 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 string.parse_compiler.m does the same job for the compiler.
|
|
% Any changes in one of these modules will probably also require
|
|
% a corresponding change in the other.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module string.parse_util.
|
|
:- interface.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_format_flag_hash
|
|
---> flag_hash_clear
|
|
; flag_hash_set.
|
|
|
|
:- type string_format_flag_space
|
|
---> flag_space_clear
|
|
; flag_space_set.
|
|
|
|
:- type string_format_flag_zero
|
|
---> flag_zero_clear
|
|
; flag_zero_set.
|
|
|
|
:- type string_format_flag_minus
|
|
---> flag_minus_clear
|
|
; flag_minus_set.
|
|
|
|
:- type string_format_flag_plus
|
|
---> flag_plus_clear
|
|
; flag_plus_set.
|
|
|
|
:- type string_format_flags
|
|
---> string_format_flags(
|
|
flag_hash :: string_format_flag_hash,
|
|
flag_space :: string_format_flag_space,
|
|
flag_zero :: string_format_flag_zero,
|
|
flag_minus :: string_format_flag_minus,
|
|
flag_plus :: string_format_flag_plus
|
|
).
|
|
|
|
:- type string_format_maybe_width
|
|
---> no_specified_width
|
|
; specified_width(int).
|
|
|
|
:- type string_format_maybe_prec
|
|
---> no_specified_prec
|
|
; specified_prec(int).
|
|
|
|
:- type string_format_int_base
|
|
---> base_octal
|
|
; base_decimal
|
|
; base_hex_lc
|
|
; base_hex_uc
|
|
; base_hex_p.
|
|
|
|
:- type string_format_float_kind
|
|
---> kind_e_scientific_lc
|
|
; kind_e_scientific_uc
|
|
; kind_f_plain_lc
|
|
; kind_f_plain_uc
|
|
; kind_g_flexible_lc
|
|
; kind_g_flexible_uc.
|
|
|
|
:- type poly_kind
|
|
---> poly_kind_char
|
|
; poly_kind_str
|
|
; poly_kind_int
|
|
; poly_kind_int8
|
|
; poly_kind_int16
|
|
; poly_kind_int32
|
|
; poly_kind_int64
|
|
; poly_kind_uint
|
|
; poly_kind_uint8
|
|
; poly_kind_uint16
|
|
; poly_kind_uint32
|
|
; poly_kind_uint64
|
|
; poly_kind_float.
|
|
|
|
:- type string_format_error
|
|
---> error_no_specifier(
|
|
int, % Which specifier were we expecting?
|
|
int % How many extra polytypes?
|
|
)
|
|
; error_unknown_specifier(
|
|
int, % Which specifier?
|
|
char % The unexpected specifier character.
|
|
)
|
|
; error_wrong_polytype(
|
|
int, % Which specifier?
|
|
char, % The specifier character.
|
|
poly_kind % The polytype we found.
|
|
)
|
|
; error_no_polytype(
|
|
int, % Which specifier?
|
|
char % The specifier character.
|
|
)
|
|
; error_nonint_star_width(
|
|
int, % Which specifier?
|
|
poly_kind % The non-i() polytype we found.
|
|
)
|
|
; error_missing_star_width(
|
|
int % Which specifier?
|
|
)
|
|
; error_nonint_star_prec(
|
|
int, % Which specifier?
|
|
poly_kind % The non-i() polytype we found.
|
|
)
|
|
; error_missing_star_prec(
|
|
int % Which specifier?
|
|
)
|
|
; error_extra_polytypes(
|
|
int, % Which specifier were we expecting?
|
|
int % How many extra polytypes?
|
|
).
|
|
|
|
% Convert an internal report of an error discovered in a format string
|
|
% to an error message that can be delivered to users.
|
|
%
|
|
:- func string_format_error_to_msg(string_format_error) = string.
|
|
|
|
:- type gather_ended_by
|
|
---> found_end_of_string
|
|
; found_percent(list(char)). % The list of chars after the percent.
|
|
|
|
% gather_non_percent_chars(Chars, ManifestChars, EndedBy):
|
|
%
|
|
% Parse the part of a format string which doesn't contain any conversion
|
|
% specifications. The manifest characters in the format string up to
|
|
% the next percent sign (if any) are returned in ManifestChars.
|
|
% If these were ended by a percent sign, then the characters after the
|
|
% percent sign are returning as the argument of found_percent in EndedBy.
|
|
%
|
|
:- pred gather_non_percent_chars(list(char)::in, list(char)::out,
|
|
gather_ended_by::out) is det.
|
|
|
|
% Record and skip past any flag characters at the start of the char list.
|
|
%
|
|
:- pred gather_flag_chars(list(char)::in, list(char)::out,
|
|
string_format_flags::in, string_format_flags::out) is det.
|
|
|
|
% get_number_prefix(!Chars, N):
|
|
%
|
|
% Consume any decimal digits at the start of !.Chars. Return the value
|
|
% of the digits found in N, and the left over characters in !:Chars.
|
|
% If there are no decimal digits at the start !.Chars, return 0 for N,
|
|
% and leave !Chars unchanged.
|
|
%
|
|
:- pred get_number_prefix(list(char)::in, list(char)::out,
|
|
int::out) is det.
|
|
|
|
% get_nonzero_number_prefix(!Chars, N):
|
|
%
|
|
% Consume any decimal digits at the start of !.Chars. Return the value
|
|
% of the digits found in N, and the left over characters in !:Chars.
|
|
% If there are no decimal digits at the start !.Chars, or if the first
|
|
% such digit is a zero, fail.
|
|
%
|
|
:- pred get_nonzero_number_prefix(list(char)::in, list(char)::out,
|
|
int::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
string_format_error_to_msg(Error) = Msg :-
|
|
(
|
|
Error = error_no_specifier(SpecNum, NumExtraPolyTypes),
|
|
Msg0 = nth_specifier(SpecNum) ++ " is missing",
|
|
( if NumExtraPolyTypes = 0 then
|
|
Msg = Msg0 ++ ", along with its input."
|
|
else if NumExtraPolyTypes = 1 then
|
|
Msg = Msg0 ++ "."
|
|
else
|
|
Msg = Msg0 ++ ", and there are "
|
|
++ string.int_to_string(NumExtraPolyTypes - 1)
|
|
++ " extra inputs."
|
|
)
|
|
;
|
|
Error = error_unknown_specifier(SpecNum, SpecChar),
|
|
Msg = nth_specifier(SpecNum) ++ " uses the unknown "
|
|
++ specifier_char(SpecChar) ++ "."
|
|
;
|
|
Error = error_wrong_polytype(SpecNum, SpecChar, PolyKind),
|
|
Msg = nth_specifier(SpecNum) ++ " uses the "
|
|
++ specifier_char(SpecChar)
|
|
++ ", but the corresponding input is "
|
|
++ poly_kind_desc(PolyKind) ++ ". " ++
|
|
acceptable_specifier_chars_for_poly_kind_msg(PolyKind)
|
|
;
|
|
Error = error_no_polytype(SpecNum, SpecChar),
|
|
Msg = nth_specifier(SpecNum)
|
|
++ ", which uses " ++ specifier_char(SpecChar)
|
|
++ ", is missing its input."
|
|
;
|
|
Error = error_nonint_star_width(SpecNum, PolyKind),
|
|
Msg = nth_specifier(SpecNum)
|
|
++ " says the width is a runtime input,"
|
|
++ " but the next input is " ++ poly_kind_desc(PolyKind)
|
|
++ ", not an integer."
|
|
;
|
|
Error = error_missing_star_width(SpecNum),
|
|
Msg = nth_specifier(SpecNum)
|
|
++ " says the width is a runtime input,"
|
|
++ " but there is no next input."
|
|
;
|
|
Error = error_nonint_star_prec(SpecNum, PolyKind),
|
|
Msg = nth_specifier(SpecNum)
|
|
++ " says the precision is a runtime input,"
|
|
++ " but the next input is " ++ poly_kind_desc(PolyKind)
|
|
++ ", not an integer."
|
|
;
|
|
Error = error_missing_star_prec(SpecNum),
|
|
Msg = nth_specifier(SpecNum)
|
|
++ " says the precision is a runtime input,"
|
|
++ " but there is no next input."
|
|
;
|
|
Error = error_extra_polytypes(SpecNum, NumExtraPolyTypes),
|
|
( if SpecNum = 1 then
|
|
% They aren't extra, since there is no other inputs before them.
|
|
Extra = ""
|
|
else
|
|
Extra = "extra "
|
|
),
|
|
Msg0 = "There is no " ++ nth(SpecNum) ++ " conversion specifier,",
|
|
( if NumExtraPolyTypes = 1 then
|
|
Msg = Msg0 ++ " but there is an " ++ Extra ++ "input."
|
|
else
|
|
Msg = Msg0 ++ " but there are " ++
|
|
string.int_to_string(NumExtraPolyTypes) ++
|
|
" " ++ Extra ++ "inputs."
|
|
)
|
|
).
|
|
|
|
:- func nth_specifier(int) = string.
|
|
|
|
nth_specifier(SpecNum) =
|
|
"The " ++ nth(SpecNum) ++ " conversion specifier".
|
|
|
|
:- func nth(int) = string.
|
|
|
|
nth(N) = NStr :-
|
|
( if N = 1 then
|
|
NStr = "first"
|
|
else if N = 2 then
|
|
NStr = "second"
|
|
else if N = 3 then
|
|
NStr = "third"
|
|
else if N = 4 then
|
|
NStr = "fourth"
|
|
else if N = 5 then
|
|
NStr = "fifth"
|
|
else if N = 6 then
|
|
NStr = "sixth"
|
|
else if N = 7 then
|
|
NStr = "seventh"
|
|
else if N = 8 then
|
|
NStr = "eighth"
|
|
else if N = 9 then
|
|
NStr = "ninth"
|
|
else if N = 10 then
|
|
NStr = "tenth"
|
|
else
|
|
NStr = string.int_to_string(N) ++ "th"
|
|
).
|
|
|
|
:- func specifier_char(char) = string.
|
|
|
|
specifier_char(SpecChar) =
|
|
"specifier character `" ++ string.char_to_string(SpecChar) ++ "'".
|
|
|
|
:- func poly_kind_desc(poly_kind) = string.
|
|
|
|
poly_kind_desc(poly_kind_char) = "a character".
|
|
poly_kind_desc(poly_kind_str) = "a string".
|
|
poly_kind_desc(poly_kind_int) = "an integer".
|
|
poly_kind_desc(poly_kind_int8) = "an 8-bit integer".
|
|
poly_kind_desc(poly_kind_int16) = "a 16-bit integer".
|
|
poly_kind_desc(poly_kind_int32) = "a 32-bit integer".
|
|
poly_kind_desc(poly_kind_int64) = "a 64-bit integer".
|
|
poly_kind_desc(poly_kind_uint) = "an unsigned integer".
|
|
poly_kind_desc(poly_kind_uint8) = "an 8-bit unsigned integer".
|
|
poly_kind_desc(poly_kind_uint16) = "a 16-bit unsigned integer".
|
|
poly_kind_desc(poly_kind_uint32) = "a 32-bit unsigned integer".
|
|
poly_kind_desc(poly_kind_uint64) = "a 64-bit unsigned integer".
|
|
poly_kind_desc(poly_kind_float) = "a float".
|
|
|
|
:- func acceptable_specifier_chars_for_poly_kind_msg(poly_kind) = string.
|
|
|
|
acceptable_specifier_chars_for_poly_kind_msg(Kind) = Msg :-
|
|
(
|
|
Kind = poly_kind_char,
|
|
Msg = "The only specifier applicable to characters is %c."
|
|
;
|
|
Kind = poly_kind_str,
|
|
Msg = "The only specifier applicable to strings is %s."
|
|
;
|
|
Kind = poly_kind_int,
|
|
Msg = "The specifiers applicable to ints are " ++
|
|
"%d, %i, %o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_int8,
|
|
Msg = "The specifiers applicable to int8s are " ++
|
|
"%d, %i, %o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_int16,
|
|
Msg = "The specifiers applicable to int16s are " ++
|
|
"%d, %i, %o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_int32,
|
|
Msg = "The specifiers applicable to int32s are " ++
|
|
"%d, %i, %o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_int64,
|
|
Msg = "The specifiers applicable to int64s are " ++
|
|
"%d, %i, %o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_uint,
|
|
Msg = "The specifiers applicable to uints are " ++
|
|
"%o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_uint8,
|
|
Msg = "The specifiers applicable to uint8s are " ++
|
|
"%o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_uint16,
|
|
Msg = "The specifiers applicable to uint16s are " ++
|
|
"%o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_uint32,
|
|
Msg = "The specifiers applicable to uint32s are " ++
|
|
"%o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_uint64,
|
|
Msg = "The specifiers applicable to uint64s are " ++
|
|
"%o, %x, %X, %u, and %p."
|
|
;
|
|
Kind = poly_kind_float,
|
|
Msg = "The specifiers applicable to floats are %f, %e, %E, %g and %G."
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
gather_non_percent_chars(Chars, NonConversionSpecChars, GatherEndedBy) :-
|
|
(
|
|
Chars = [HeadChar | TailChars],
|
|
( if HeadChar = '%' then
|
|
NonConversionSpecChars = [],
|
|
% We eat the percent sign.
|
|
GatherEndedBy = found_percent(TailChars)
|
|
else
|
|
gather_non_percent_chars(TailChars, TailNonConversionSpecChars,
|
|
GatherEndedBy),
|
|
NonConversionSpecChars = [HeadChar | TailNonConversionSpecChars]
|
|
)
|
|
;
|
|
Chars = [],
|
|
NonConversionSpecChars = [],
|
|
GatherEndedBy = found_end_of_string
|
|
).
|
|
|
|
gather_flag_chars(!Chars, !Flags) :-
|
|
% XXX Should we return an error if we find that the format string
|
|
% sets the same flag twice?
|
|
( if
|
|
!.Chars = [Char | !:Chars],
|
|
( Char = '#', !Flags ^ flag_hash := flag_hash_set
|
|
; Char = ' ', !Flags ^ flag_space := flag_space_set
|
|
; Char = '0', !Flags ^ flag_zero := flag_zero_set
|
|
; Char = ('-'), !Flags ^ flag_minus := flag_minus_set
|
|
; Char = ('+'), !Flags ^ flag_plus := flag_plus_set
|
|
)
|
|
then
|
|
disable_warning [suspicious_recursion] (
|
|
gather_flag_chars(!Chars, !Flags)
|
|
)
|
|
else
|
|
true
|
|
).
|
|
|
|
get_number_prefix(!Chars, N) :-
|
|
get_number_prefix_loop(!Chars, 0, N).
|
|
|
|
get_nonzero_number_prefix(!Chars, N) :-
|
|
!.Chars = [Char | !:Chars],
|
|
char.decimal_digit_to_int(Char, CharValue),
|
|
Char \= '0',
|
|
get_number_prefix_loop(!Chars, CharValue, N).
|
|
|
|
:- pred get_number_prefix_loop(list(char)::in, list(char)::out,
|
|
int::in, int::out) is det.
|
|
|
|
get_number_prefix_loop(!Chars, N0, N) :-
|
|
( if
|
|
!.Chars = [Char | !:Chars],
|
|
char.decimal_digit_to_int(Char, CharValue)
|
|
then
|
|
N1 = N0 * 10 + CharValue,
|
|
get_number_prefix_loop(!Chars, N1, N)
|
|
else
|
|
N = N0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module string.parse_util.
|
|
%---------------------------------------------------------------------------%
|