Message ID | CAESRpQCGfALwFN_PgLxwkf7KCmByzBevrvGY634PhBFCXX6zVg@mail.gmail.com |
---|---|
State | New |
Headers | show |
On Thu, Nov 27, 2014 at 06:25:01PM +0100, Manuel López-Ibáñez wrote: > > The ugliest part is how to handle warningcount and werrorcount. I > could handle this in the common machinery in a better way by storing > DK_WERROR in the diagnostic->kind and checking it after printing. This > way we can first decrease both counters, then increase back the one > not changed and store the kind somewhere. then if the diagnostic is > canceled nothing is done, if it is flushed, then increase the > appropriate counter (perhaps calling into the common part for any post > action due to Wfatal-errors or -fmax-errors=). > > I can also hide the output_buffer switching inside two helper > functions, but the helper function would need to use either a static > variable or a global one to save and restore the tmp_buffer. I'm not > sure that is better or worse (the current code uses a global pointer > &cur_error_buffer, so perhaps I should have used a similar approach). > Hi Manuel, First, thanks for all your work on the error/warning code in gfortran. Second, I'm dredging through my memories from a decade or so ago. Others are encouraged to jump in and correct me. The design of gfortran's warning/error code is largely unchanged from when g95 was originally imported into the tree. There have been, of course, some changes but the design was set. My understanding of how it all works(ed) is that gfortran's parsing runs a series of matchers over the input. A matcher may generate an error/warning, which is written into a buffer. But, instead of immediately reporting the issue gfortran may continue to run a series of matchers to see if the input is in fact validate. gfortran will report the last buffered error/warning message only after the last matcher fails. In running the series of matchers, there are times when a matcher may hit an error/warning condition and it is, in fact, a problem and gfortran wants to report the problem NOW. This is the origin of the *_now variants. I would need to go dredge through ChangeLogs, but I believe I may be responsible for the counting of error messages. I know that I at least set the default value for -fmax-error. The origins to the counting is that once gfortran encountered and reported an error, she would discard the problematic statement and continue parsing the input. This often leads to a series of spurious run-on error messages, which are all caused by the first error. Thus, if one does -fmax-error=1 and fixes the problem, then all the other problems disappear. Finally, gfortran's error/warning mechanism isn't immediately available at the start of execution. However, errors can occur before the mechanism is initialized. This is one reason why one finds fatal_error() spinkled throughout the code. > > * Fortran devs #2: The testsuite is testing that the warning is > eventually printed. However, I'm not sure it is testing when the > warning is buffered and then discarded, is it? If not, how can I > produce such a test? > I think that you can't check for a discarded buffered message. In fact, why bother?
Manuel López-Ibáñez wrote: > * Fortran devs: Is this approach acceptable? The main idea is to have > an output_buffer called pp_warning_buffer with the flush_p bit unset > if we are buffering. When printing buffered warnings, use this > output_buffer in the global_dc->printer instead of the (unbuffered > one) used by the *_now variants. In principle this could support > several buffered diagnostics, but Fortran only seems to buffer at most > one. I think the approach is fine. As the _now version overrides the buffer, one might even do with a single buffer by clearing it, setting flush_p temporarily to true and printing the message. It only might collide with buffered warnings (for error_now) and errors (for warning_now), but I don't see whether that's an issue. For warnings/error_now it probably isn't, for errors/warning_now, it might. Thus, having two buffers is probably better. > The ugliest part is how to handle warningcount and werrorcount. I > could handle this in the common machinery in a better way by storing > DK_WERROR in the diagnostic->kind and checking it after printing. I'm missing the "but". Does it require more modifications in common code and thus you shy away? Or is there another reason? > I can also hide the output_buffer switching inside two helper > functions, but the helper function would need to use either a static > variable or a global one to save and restore the tmp_buffer. I'm not > sure that is better or worse (the current code uses a global pointer > &cur_error_buffer, so perhaps I should have used a similar approach). Me neither. The current approach is rather localized in error.c; thus, it doesn't really matter. > * Fortran devs #2: The testsuite is testing that the warning is > eventually printed. However, I'm not sure it is testing when the > warning is buffered and then discarded, is it? If not, how can I > produce such a test? Well, for nearly every Fortran program, at least for one line multiple attempts have to be done by the parser. Thus, if you set a break point in gfc_error, you will see "syntax error" messages which never make it to the screen. As the test suite checks for excess errors, nearly every test-suite file tests this. * * * With the patch in place, plus its gfc_error cousin, and after the follow-up conversion work, only the following would remain: * gfc_warning: a single occurence of two locations in the same error string (of ~120 calls) * gfc_error: approx. 34 times two locations (of ~1630 calls) * gfc_error_now_1: 11 times two locations * In scanner.c: - gfc_warning_now_1: 6 calls in scanner.c - gfc_warning: 3 - fprintf: 2 - gfc_error: 0 Some of those can probably be simply converted, others either need to remain, or one has to setup a proper location (currently, using gfc_warning_now would ICE), or one creates a work around and constructs manually the error string (at least for the fprintf cases). * * * Regarding the use of libcpp: Currently, it is only used with explicit preprocessing ("-cpp", file extension) and it is used to output into a temporary file which is then read back. I experimented with token = cpp_get_token (cpp_in); const char *str = cpp_token_as_text (cpp_in, token); which kind of works okay. But I do not understand how one gets linebreaks and spacing correctly. For linebreaks, I can use "flags & BOL" or a callback. But I'm still trying to understand how one can get the number of spacings. "flags & PREV_WHITE" seems to record only a single one and seems to convert all of them (\t, \n and ' ') into a single type. For fixed-form Fortran, spaces are essential. On punch cards,* only the first 72 characters were read; as (some?) punch cards had additional 8 characters, those were used for comments (e.g. to enumerate the cards). Hence, there are codes out there, which assumes that everything beyond 72 characters is ignored. Thus, the number of spaces is crucial – at least with -fpreprocessed, if one wants to always goes through libcpp. Looking at the current code, it seems to use a line-break callback with SOURCE_COLUMN to reconstruct the indentation. I think saving the token src_column and the length of the token string and subtracting it from the new source location, will also work for mid-line spaces. However, that's really a kludge – and it still doesn't permit one to distiniguish between spaces and tabs. Finally, the token handling seems to get in trouble with print *, "Hello & &world!" where a single string extends over two lines. Taking everything together, I think using libcpp for reading Fortran files is a topic for GCC 6. Any suggestion how to properly handle the spacing? For the lexing itself, one probably needs a Fortran mode, which can recognize Fortran comments, continuation lines and some special properties about Fortran string. When implemented, one could then also turn of the traditional mode. Tobias * Disclaimer: I started with Fortran 95 and a rather object-oriented code. Hence, I might have gotten the fine print of the Fortran history wrong.
On 27 November 2014 at 20:28, Tobias Burnus <burnus@net-b.de> wrote: > I think the approach is fine. As the _now version overrides the buffer, one > might even do with a single buffer by clearing it, setting flush_p > temporarily to true and printing the message. It only might collide with > buffered warnings (for error_now) and errors (for warning_now), but I don't > see whether that's an issue. For warnings/error_now it probably isn't, for > errors/warning_now, it might. Thus, having two buffers is probably better. Oh, I didn't notice that the _now versions override the buffered messages. Where do you see that? It seems that gfc_warning_1 saves and restores buffer_flag but does not touch the buffers (it prints directly to the stream). However, as you say, I may still need two buffers, one for errors and other for warnings. (If this were not necessary, it would be great! Is it really?). In fact, we will need more buffers, since there is gfc_push/pop_error. This is why I didn't start with gfc_error, it is a bit more complex than gfc_warning. >> The ugliest part is how to handle warningcount and werrorcount. I >> could handle this in the common machinery in a better way by storing >> DK_WERROR in the diagnostic->kind and checking it after printing. > > > I'm missing the "but". Does it require more modifications in common code and > thus you shy away? Or is there another reason? I was going to write "Exactly" and a long explanation. But now I realize that in this particular case I can simply check DK_ERROR, since within gfc_warning that can only come from a warning converted to an error. That simplifies the function a bit: +/* Issue a warning. */ + +bool +gfc_warning_1 (int opt, const char *gmsgid, ...) +{ + va_list argp; + diagnostic_info diagnostic; + bool fatal_errors = global_dc->fatal_errors; + pretty_printer *pp = global_dc->printer; + output_buffer *tmp_buffer = pp->buffer; + + if (!pp_warning_buffer.flush_p) + { + /* To prevent -fmax-errors= triggering. */ + --werrorcount; + pp->buffer = &pp_warning_buffer; + global_dc->fatal_errors = false; + } + + va_start (argp, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, + DK_WARNING); + diagnostic.option_index = opt; + bool ret = report_diagnostic (&diagnostic); + + if (ret && !pp_warning_buffer.flush_p) + { + warningcount_buffered = 0; + werrorcount_buffered = 0; + /* Undo the above --werrorcount if not Werror, otherwise werrorcount is + correct already. */ + diagnostic.kind == DK_ERROR + ? (++werrorcount_buffered) + : (++werrorcount, --warningcount, ++warningcount_buffered); + + pp->buffer = tmp_buffer; + global_dc->fatal_errors = fatal_errors; + } + + va_end (argp); + return ret; +} > Well, for nearly every Fortran program, at least for one line multiple > attempts have to be done by the parser. Thus, if you set a break point in > gfc_error, you will see "syntax error" messages which never make it to the > screen. As the test suite checks for excess errors, nearly every test-suite > file tests this. Sure, but I would like to test specifically triggering and discarding the gfc_warning that I converted (or another single one that you suggest), if this were possible. > Some of those can probably be simply converted, others either need to > remain, or one has to setup a proper location (currently, using > gfc_warning_now would ICE), or one creates a work around and constructs > manually the error string (at least for the fprintf cases). Why does it ICE? At worst it should give a wrong location, but the computation of the offset is fairly similar to what Fortran already does. Could you give me a patch+testcase that ICEs? > Taking everything together, I think using libcpp for reading Fortran files > is a topic for GCC 6. Any suggestion how to properly handle the spacing? No idea. My knowledge of cpp is limited. I think you need to ask Dodji, Tom or Joseph (in IRC or a new thread). I was hoping it was going to be easier to lex Fortran with cpp since cpp can already preprocess it. Thanks for the comment. They are very helpful. Cheers, Manuel.
On 27 November 2014 at 22:33, Manuel López-Ibáñez <lopezibanez@gmail.com> wrote: > On 27 November 2014 at 20:28, Tobias Burnus <burnus@net-b.de> wrote: >> I think the approach is fine. As the _now version overrides the buffer, one >> might even do with a single buffer by clearing it, setting flush_p >> temporarily to true and printing the message. It only might collide with >> buffered warnings (for error_now) and errors (for warning_now), but I don't >> see whether that's an issue. For warnings/error_now it probably isn't, for >> errors/warning_now, it might. Thus, having two buffers is probably better. > > Oh, I didn't notice that the _now versions override the buffered > messages. Where do you see that? It seems that gfc_warning_1 saves and > restores buffer_flag but does not touch the buffers (it prints > directly to the stream). I meant gfc_warning_now_1. However, gfc_error_now_1 does reset error_buffer (and sets it as curr_buffer, why if without buffering it is not used?) . Hum, if this is really necessary, I'm afraid I just introduced a subtle bug, since the new gfc_error_now does not reset error_buffer, thus, potentially it may give an error that should have been discarded. Tobias, what do you think?
Manuel López-Ibáñez <lopezibanez@gmail.com> writes: > * Dodji: Do the common diagnostics part look reasonable? Yes they do. I just have one minor comment nit: [...] > Index: gcc/pretty-print.h [...] > + > + /* Nonzero means that text should be flushed when > + appropriate. Otherwise, text is buffered until either > + pp_pp_really_flush or pp_clear_output_area are called. */ I think you wanted to say pp_really_flush, not pp_pp_realy_flush. > + bool flush_p; > }; [...] Thanks!
Index: gcc/pretty-print.c =================================================================== --- gcc/pretty-print.c (revision 218090) +++ gcc/pretty-print.c (working copy) @@ -40,7 +40,8 @@ cur_chunk_array (), stream (stderr), line_length (), - digit_buffer () + digit_buffer (), + flush_p (true) { obstack_init (&formatted_obstack); obstack_init (&chunk_obstack); @@ -679,12 +680,25 @@ pp_wrapping_mode (pp) = oldmode; } -/* Flush the content of BUFFER onto the attached stream. */ +/* Flush the content of BUFFER onto the attached stream. This + function does nothing unless pp->output_buffer->flush_p. */ void pp_flush (pretty_printer *pp) { + pp_clear_state (pp); + if (!pp->buffer->flush_p) + return; pp_write_text_to_stream (pp); + fflush (pp_buffer (pp)->stream); +} + +/* Flush the content of BUFFER onto the attached stream independently + of the value of pp->output_buffer->flush_p. */ +void +pp_really_flush (pretty_printer *pp) +{ pp_clear_state (pp); + pp_write_text_to_stream (pp); fflush (pp_buffer (pp)->stream); } Index: gcc/pretty-print.h =================================================================== --- gcc/pretty-print.h (revision 218090) +++ gcc/pretty-print.h (working copy) @@ -100,6 +100,11 @@ /* This must be large enough to hold any printed integer or floating-point value. */ char digit_buffer[128]; + + /* Nonzero means that text should be flushed when + appropriate. Otherwise, text is buffered until either + pp_pp_really_flush or pp_clear_output_area are called. */ + bool flush_p; }; /* The type of pretty-printer flags passed to clients. */ @@ -314,6 +319,7 @@ extern void pp_verbatim (pretty_printer *, const char *, ...) ATTRIBUTE_GCC_PPDIAG(2,3); extern void pp_flush (pretty_printer *); +extern void pp_really_flush (pretty_printer *); extern void pp_format (pretty_printer *, text_info *); extern void pp_output_formatted_text (pretty_printer *); extern void pp_format_verbatim (pretty_printer *, text_info *); Index: gcc/fortran/gfortran.h =================================================================== --- gcc/fortran/gfortran.h (revision 218090) +++ gcc/fortran/gfortran.h (working copy) @@ -2443,7 +2443,6 @@ int dump_fortran_optimized; int warn_aliasing; - int warn_ampersand; int warn_function_elimination; int warn_implicit_interface; int warn_implicit_procedure; @@ -2691,6 +2690,7 @@ const char *gfc_print_wide_char (gfc_char_t); void gfc_warning (const char *, ...) ATTRIBUTE_GCC_GFC(1,2); +bool gfc_warning_1 (int opt, const char *, ...) ATTRIBUTE_GCC_GFC(2,3); void gfc_warning_now_1 (const char *, ...) ATTRIBUTE_GCC_GFC(1,2); bool gfc_warning_now (const char *, ...) ATTRIBUTE_GCC_GFC(1,2); bool gfc_warning_now (int opt, const char *, ...) ATTRIBUTE_GCC_GFC(2,3); @@ -2719,7 +2719,7 @@ void gfc_free_error (gfc_error_buf *); void gfc_get_errors (int *, int *); -void gfc_errors_to_warnings (int); +void gfc_errors_to_warnings (bool); /* arith.c */ void gfc_arith_init_1 (void); Index: gcc/fortran/error.c =================================================================== --- gcc/fortran/error.c (revision 218090) +++ gcc/fortran/error.c (working copy) @@ -50,7 +50,11 @@ static gfc_error_buf error_buffer, warning_buffer, *cur_error_buffer; +static output_buffer pp_warning_buffer; +static int warningcount_buffered, werrorcount_buffered; +#include <new> /* For placement-new */ + /* Go one level deeper suppressing errors. */ void @@ -122,6 +126,7 @@ gfc_buffer_error (int flag) { buffer_flag = flag; + pp_warning_buffer.flush_p = !flag; } @@ -833,6 +838,52 @@ } +/* Issue a warning. */ + +bool +gfc_warning_1 (int opt, const char *gmsgid, ...) +{ + va_list argp; + diagnostic_info diagnostic; + bool ret; + bool fatal_errors = global_dc->fatal_errors; + pretty_printer *pp = global_dc->printer; + output_buffer *tmp_buffer = pp->buffer; + int warningcount_saved = 0; + int werrorcount_saved = 0; + + if (!pp_warning_buffer.flush_p) + { + warningcount_saved = warningcount; + werrorcount_saved = werrorcount; + /* To prevent -fmax-errors= triggering. */ + werrorcount--; + pp->buffer = &pp_warning_buffer; + global_dc->fatal_errors = false; + } + + va_start (argp, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, + DK_WARNING); + diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); + + if (!pp_warning_buffer.flush_p) + { + warningcount_buffered = warningcount - warningcount_saved; + warningcount = warningcount_saved; + werrorcount++; /* Undo the above werrorcount-- */ + werrorcount_buffered = werrorcount - werrorcount_saved; + werrorcount = werrorcount_saved; + + pp->buffer = tmp_buffer; + global_dc->fatal_errors = fatal_errors; + } + + va_end (argp); + return ret; +} + /* Whether, for a feature included in a given standard set (GFC_STD_*), we should issue an error or a warning, or be quiet. */ @@ -1176,6 +1227,15 @@ gfc_clear_warning (void) { warning_buffer.flag = 0; + + pretty_printer *pp = global_dc->printer; + output_buffer *tmp_buffer = pp->buffer; + pp->buffer = &pp_warning_buffer; + pp_clear_output_area (pp); + pp->buffer = tmp_buffer; + pp_warning_buffer.flush_p = false; + warningcount_buffered = 0; + werrorcount_buffered = 0; } @@ -1192,6 +1252,20 @@ fputs (warning_buffer.message, stderr); warning_buffer.flag = 0; } + + /* This is for the new diagnostics machinery. */ + pretty_printer *pp = global_dc->printer; + output_buffer *tmp_buffer = pp->buffer; + pp->buffer = &pp_warning_buffer; + if (pp_last_position_in_text (pp) != NULL) + { + pp_really_flush (pp); + pp_warning_buffer.flush_p = true; + warningcount += warningcount_buffered; + werrorcount += werrorcount_buffered; + } + + pp->buffer = tmp_buffer; } @@ -1395,9 +1469,9 @@ /* Switch errors into warnings. */ void -gfc_errors_to_warnings (int f) +gfc_errors_to_warnings (bool f) { - warnings_not_errors = (f == 1) ? 1 : 0; + warnings_not_errors = f; } void @@ -1407,6 +1481,7 @@ diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer; diagnostic_format_decoder (global_dc) = gfc_format_decoder; global_dc->caret_char = '^'; + new (&pp_warning_buffer) output_buffer (); } void Index: gcc/fortran/lang.opt =================================================================== --- gcc/fortran/lang.opt (revision 218090) +++ gcc/fortran/lang.opt (working copy) @@ -202,9 +202,13 @@ Warn about alignment of COMMON blocks Wampersand -Fortran Warning +Fortran Warning Var(warn_ampersand) LangEnabledBy(Fortran, Wpedantic) Warn about missing ampersand in continued character constants +Wampersand +LangEnabledBy(Fortran, Wall) +; + Warray-temporaries Fortran Warning Warn about creation of array temporaries Index: gcc/fortran/scanner.c =================================================================== --- gcc/fortran/scanner.c (revision 218090) +++ gcc/fortran/scanner.c (working copy) @@ -1154,9 +1154,10 @@ if (in_string) { gfc_current_locus.nextc--; - if (gfc_option.warn_ampersand && in_string == INSTRING_WARN) - gfc_warning ("Missing '&' in continued character " - "constant at %C"); + if (warn_ampersand && in_string == INSTRING_WARN) + gfc_warning_1 (OPT_Wampersand, + "Missing %<&%> in continued character " + "constant at %C"); } /* Both !$omp and !$ -fopenmp continuation lines have & on the continuation line only optionally. */ Index: gcc/fortran/resolve.c =================================================================== --- gcc/fortran/resolve.c (revision 218090) +++ gcc/fortran/resolve.c (working copy) @@ -2431,7 +2431,7 @@ if (!pedantic && (gfc_option.allow_std & GFC_STD_GNU)) /* Turn erros into warnings with -std=gnu and -std=legacy. */ - gfc_errors_to_warnings (1); + gfc_errors_to_warnings (true); if (!gfc_compare_interfaces (sym, def_sym, sym->name, 0, 1, reason, sizeof(reason), NULL, NULL)) @@ -2444,14 +2444,14 @@ if (!pedantic || ((gfc_option.warn_std & GFC_STD_LEGACY) && !(gfc_option.warn_std & GFC_STD_GNU))) - gfc_errors_to_warnings (1); + gfc_errors_to_warnings (true); if (sym->attr.if_source != IFSRC_IFBODY) gfc_procedure_use (def_sym, actual, where); } done: - gfc_errors_to_warnings (0); + gfc_errors_to_warnings (false); if (gsym->type == GSYM_UNKNOWN) { Index: gcc/fortran/primary.c =================================================================== --- gcc/fortran/primary.c (revision 218090) +++ gcc/fortran/primary.c (working copy) @@ -951,7 +951,7 @@ match_string_constant (gfc_expr **result) { char name[GFC_MAX_SYMBOL_LEN + 1], peek; - int i, kind, length, warn_ampersand, ret; + int i, kind, length, warn_ampersand_saved, ret; locus old_locus, start_locus; gfc_symbol *sym; gfc_expr *e; @@ -1071,8 +1071,8 @@ /* We disable the warning for the following loop as the warning has already been printed in the loop above. */ - warn_ampersand = gfc_option.warn_ampersand; - gfc_option.warn_ampersand = 0; + warn_ampersand_saved = warn_ampersand; + warn_ampersand = false; p = e->value.character.string; for (i = 0; i < length; i++) @@ -1091,7 +1091,7 @@ } *p = '\0'; /* TODO: C-style string is for development/debug purposes. */ - gfc_option.warn_ampersand = warn_ampersand; + warn_ampersand = warn_ampersand_saved; next_string_char (delimiter, &ret); if (ret != -1) Index: gcc/fortran/options.c =================================================================== --- gcc/fortran/options.c (revision 218090) +++ gcc/fortran/options.c (working copy) @@ -94,7 +94,6 @@ gfc_option.dump_fortran_optimized = 0; gfc_option.warn_aliasing = 0; - gfc_option.warn_ampersand = 0; gfc_option.warn_array_temp = 0; gfc_option.warn_function_elimination = 0; gfc_option.warn_implicit_interface = 0; @@ -423,9 +422,6 @@ if (!gfc_option.flag_automatic) gfc_option.flag_max_stack_var_size = 0; - if (pedantic) - gfc_option.warn_ampersand = 1; - /* Optimization implies front end optimization, unless the user specified it directly. */ @@ -447,7 +443,6 @@ set_Wall (int setting) { gfc_option.warn_aliasing = setting; - gfc_option.warn_ampersand = setting; gfc_option.warn_line_truncation = setting; gfc_option.warn_surprising = setting; gfc_option.warn_underflow = setting; @@ -642,10 +637,6 @@ gfc_option.warn_aliasing = value; break; - case OPT_Wampersand: - gfc_option.warn_ampersand = value; - break; - case OPT_Warray_temporaries: gfc_option.warn_array_temp = value; break; @@ -1003,7 +994,7 @@ gfc_option.max_continue_fixed = 19; gfc_option.max_continue_free = 39; gfc_option.max_identifier_length = 31; - gfc_option.warn_ampersand = 1; + warn_ampersand = 1; warn_tabs = 1; break; @@ -1012,7 +1003,7 @@ | GFC_STD_F2003 | GFC_STD_F95 | GFC_STD_F2008_OBS; gfc_option.warn_std = GFC_STD_F95_OBS; gfc_option.max_identifier_length = 63; - gfc_option.warn_ampersand = 1; + warn_ampersand = 1; warn_tabs = 1; break; @@ -1021,7 +1012,7 @@ | GFC_STD_F2003 | GFC_STD_F95 | GFC_STD_F2008 | GFC_STD_F2008_OBS; gfc_option.warn_std = GFC_STD_F95_OBS | GFC_STD_F2008_OBS; gfc_option.max_identifier_length = 63; - gfc_option.warn_ampersand = 1; + warn_ampersand = 1; warn_tabs = 1; break; @@ -1031,7 +1022,7 @@ | GFC_STD_F2008_TS; gfc_option.warn_std = GFC_STD_F95_OBS | GFC_STD_F2008_OBS; gfc_option.max_identifier_length = 63; - gfc_option.warn_ampersand = 1; + warn_ampersand = 1; warn_tabs = 1; break;