Files
mercury/library/string.parse_util.m
Zoltan Somogyi cb1da20600 Stop ancestor imports "shadowing" unused local imports.
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.
2022-03-30 13:06:37 +11:00

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.
%---------------------------------------------------------------------------%