https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55881

--- Comment #7 from Manuel López-Ibáñez <manu at gcc dot gnu.org> ---
Some comments from a discussion with Martin and David:

%G and %K  sometimes do not work with pragma
diagnostics. The reason is that the pragma diagnostics check is done
here:

https://github.com/gcc-mirror/gcc/blob/a0e9bfbb865dcaf307a4a06a29a7e1e7be24ee15/gcc/diagnostic.c#L944

which is processed earlier than the %G and %K formats.

If we have something like:

 warning_at(loc, "%K", exp)

there is one case when it should work (otherwise something is really
broken): when loc == EXPR_LOCATION(exp) and loc points to a location
that is affected by the #pragma.

And two cases when it will not work:

Case #1. loc != EXPR_LOCATION(exp) and loc is pointing to a location not
affected by the #pragma but EXPR_LOCATION(exp) is pointing to a
location affected by the #pragma.

Case #2. loc == EXPR_LOCATION(exp) and loc is pointing to a location not
affected by the #pragma but some location in the inlining stack is
affected by the #pragma.

I would say that every case #1 is a bug. The fix is to always make
sure that loc == TREE_LOCATION(exp). I see in builtins.c code like:

      location_t loc = tree_nonartificial_location (exp);
      loc = expansion_point_location_if_in_system_header (loc);
        warning_at (loc, opt,
                "%K%qD specified bound %E "
                "exceeds maximum object size %E",
                exp, func,
                range[0], maxobjsize);

This is likely to break, because "loc", that is,
expansion_point_location_if_in_system_header (tree_nonartificial_location
(exp)), is used for evaluating the
#pragma but %K (that is, EXPR_LOCATION(exp)) is used for printing the source
location, and those two may not be equal in some cases. Fixing this is trivial
but fragile. Just make sure that "loc" in warning_at(loc) is always
"EXPR_LOCATION(exp)", but this is fragile. The real fix is to replace %K and %G
with an explicit location.

For replacing %K and %G (and also for fixing case #2), we need to process the
inlining stack before calling update_effective_level_from_pragmas(). And we
probably need to decide what level takes precedence in cases such as:
https://godbolt.org/z/YXA_vG

In constructor 'BadString3<N>::BadString3(const char*, size_t) [with
long unsigned int N = 3]',
inlined from 'void bad3_warn_size_m1_var(const char*)' at <source>:30:23,
inlined from 'void call_bad3_warn_size_m1_var()' at <source>:37:25:
<source>:21:13: warning: 'char* strncpy(char*, const char*, size_t)'
output truncated before terminating nul copying 3 bytes from a string
of the same length [-Wstringop-truncation]

If I place the pragma ignored around line 21. The diagnostic is
silenced as it should be. But if I place it around line 30, it is not.
I think it makes sense that the outermost #pragmas override inner-most
#pragmas, since the inner-most affects more places than the
outer-most. But there may be cases I haven't thought about.

In any case, I see two ways to fix case #2 (and remove completely %G and %K):

Solution 1: Have new variants warning_K and error_K (better names
needed or overloads?) of warning_at and error_at  that can take a
tree, do what percent_K_format does, in particular, set
*pp_ti_abstract_origin (text) (or even better, set
diagnostic_abstract_origin (diagnostic)), and then walk the
abstract_origin chain to extract the locations as done here
(https://github.com/gcc-mirror/gcc/blob/cba058c7d596cf482254e7a2d7492eec29553a9c/gcc/langhooks.c#L407)
and save it somewhere in the diagnostic_info or context. Then, call
into diagnostic.c (diagnostic_report_diagnostic) and make
update_effective_level_from_pragmas() handle multiple locations.

Solution 2: Provide a function (or constructor) that somehow encodes
the abstract_origin chain in the rich locations and that must be
called explicitly before calling warning/error_at using this
rich_location, then handle the abstract_origin chain within
diagnostic.c as in solution 1. Something like:

rich_location ao_loc(EXPR_LOCATION (t), TREE_BLOCK (t));
warning_at(ao_loc,"");

Both solutions will remove completely the need for %K and
percent_K_format. Solution 1 could also be an intermediate step until
there is a way to encode the abstract_origin chain in a rich_location.

Reply via email to