Re: [WIP RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]
On Wed, 2023-07-26 at 11:27 +0200, Benjamin Priour wrote: > On Sat, Jul 22, 2023 at 12:04 AM David Malcolm > wrote: > > > On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote: > > > Hi, > > > > > > Upon David's request I've joined the in progress patch to the > > > below > > > email. > > > I hope it makes more sense now. > > > > > > Best, > > > Benjamin. > > > > Thanks for posting the work-in-progress patch; it makes the idea > > clearer. > > > > Some thoughts about this: > > > > - I like the idea of defaulting to *not* showing events within > > system > > headers, which the patch achieves > > - I don't like the combination of never/system with maxdepth, in > > that > > it seems complicated and I don't think a user is likely to > > experiment > > with different depths. > > - Hence I think it would work better as a simple boolean, perhaps > > "-fanalyzer-show-events-in-system-headers" > > or somesuch? It seems like the sort of thing that we want to > > provide > > a sensible default for, but have the option of turning off for > > debugging the analyzer itself, but I don't expect an end-user to > > touch > > that option. > > > > A boolean sounds good, I will trust your experience with the end-user > here, > especially since and "never" had some overlap, it could > have > been confusing. > > > > FWIW the patch seems to have been mangled somewhat via email, so I > > don't have a sense of what the actual output from patched analyzer > > looks like. What should we output to the user with -fanalyzer and > > no > > other options for the case in PR 110543? Currently, for > > https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which > > probably only this last one is useful: > > > > (12) dereference of NULL 'a.std::__shared_ptr_access > __gnu_cxx::_S_atomic, false, false>::operator->()' > > > > What does the output look like with your patch? > > > > The plan with this patch was to get events : > (1) entry to 'main' > (2) calling 'std::__shared_ptr_access false>::operator->' from 'main' > (12) dereference of NULL 'a.std::__shared_ptr_access __gnu_cxx::_S_atomic, false, false>::operator->()' > (11) returning to 'main' from 'std::__shared_ptr_access __gnu_cxx::_S_atomic, false, false>::operator->' > > This way, we get the entry and exit point to the system headers ( (2) > and > (11) ), and the actual injurious event ( (12) ). > We could however go as you suggest, with an even more succint path > and only > keep (1) and (12). I think we could still have events (11) and (12): what if for call and return events we consider the location of both the call site and the called function: we suppress if both are in a system header, but don't suppress if only one is a system header. That way by default we'd show the call into a system header and show the return from the system header, but we'd suppress all the implementation details within the system header. Does that seem like it could work? Thanks Dave > > Thanks, > Benjamin > > > > Thanks > > Dave > > > > > > > > > > > -- Forwarded message - > > > From: Benjamin Priour > > > Date: Tue, Jul 18, 2023 at 3:30 PM > > > Subject: [RFC] analyzer: Add optional trim of the analyzer > > > diagnostics > > > going too deep [PR110543] > > > To: , David Malcolm > > > > > > > > > > > > Hi, > > > > > > I'd like to request comments on a patch I am writing for > > > PR110543. > > > The goal of this patch is to reduce the noise of the analyzer > > > emitted > > > diagnostics when dealing with > > > system headers, or simply diagnostic paths that are too long. The > > > new > > > option only affects the display > > > of the diagnostics, but doesn't hinder the actual analysis. > > > > > > I've defaulted the new option to "system", thus preventing the > > > diagnostic > > > paths from showing system headers. > > > "never" corresponds to the pre-patch behavior, whereas you can > > > also > > > specify > > > an unsigned value > > > that prevents paths to go deeper than frames. > > > > > > fanalyzer-trim-diagnostics= > > > > Common Joined RejectNegative ToLower > > > > Var(flag_analyzer_trim_diagnostics) > > > > Init("system") > > > > -fanalyzer-trim-diagnostics=[never|system|] Trim > > > > diagnostics > > > > path that are too long before emission. > > > > > > > > > > Does it sounds reasonable and user-friendly ? > > > > > > Regstrapping was a success against trunk, although one of the > > > newly > > > added > > > test case fails for c++14. > > > Note that the test case below was done with "never", thus behaves > > > exactly > > > as the pre-patch analyzer > > > on x86_64-linux-gnu. > > > > > > /* { dg-additional-options "-fdiagnostics-plain-output > > > > -fdiagnostics-path-format=inline-events -fanalyzer-trim- > > > > diagnostics=never" > > > > } */ > > > > /* { dg-skip-if "" { c++98_only } } */ > > > > > > > > #include > > > > struct A {int x; int y;}; > > > > > > > > int main () { > > > > std::shared_ptr a; > >
Re: [WIP RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]
On Sat, Jul 22, 2023 at 12:04 AM David Malcolm wrote: > On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote: > > Hi, > > > > Upon David's request I've joined the in progress patch to the below > > email. > > I hope it makes more sense now. > > > > Best, > > Benjamin. > > Thanks for posting the work-in-progress patch; it makes the idea > clearer. > > Some thoughts about this: > > - I like the idea of defaulting to *not* showing events within system > headers, which the patch achieves > - I don't like the combination of never/system with maxdepth, in that > it seems complicated and I don't think a user is likely to experiment > with different depths. > - Hence I think it would work better as a simple boolean, perhaps > "-fanalyzer-show-events-in-system-headers" > or somesuch? It seems like the sort of thing that we want to provide > a sensible default for, but have the option of turning off for > debugging the analyzer itself, but I don't expect an end-user to touch > that option. > A boolean sounds good, I will trust your experience with the end-user here, especially since and "never" had some overlap, it could have been confusing. > FWIW the patch seems to have been mangled somewhat via email, so I > don't have a sense of what the actual output from patched analyzer > looks like. What should we output to the user with -fanalyzer and no > other options for the case in PR 110543? Currently, for > https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which > probably only this last one is useful: > > (12) dereference of NULL 'a.std::__shared_ptr_access __gnu_cxx::_S_atomic, false, false>::operator->()' > > What does the output look like with your patch? > The plan with this patch was to get events : (1) entry to 'main' (2) calling 'std::__shared_ptr_access::operator->' from 'main' (12) dereference of NULL 'a.std::__shared_ptr_access::operator->()' (11) returning to 'main' from 'std::__shared_ptr_access::operator->' This way, we get the entry and exit point to the system headers ( (2) and (11) ), and the actual injurious event ( (12) ). We could however go as you suggest, with an even more succint path and only keep (1) and (12). Thanks, Benjamin > Thanks > Dave > > > > > > -- Forwarded message - > > From: Benjamin Priour > > Date: Tue, Jul 18, 2023 at 3:30 PM > > Subject: [RFC] analyzer: Add optional trim of the analyzer > > diagnostics > > going too deep [PR110543] > > To: , David Malcolm > > > > > > Hi, > > > > I'd like to request comments on a patch I am writing for PR110543. > > The goal of this patch is to reduce the noise of the analyzer emitted > > diagnostics when dealing with > > system headers, or simply diagnostic paths that are too long. The new > > option only affects the display > > of the diagnostics, but doesn't hinder the actual analysis. > > > > I've defaulted the new option to "system", thus preventing the > > diagnostic > > paths from showing system headers. > > "never" corresponds to the pre-patch behavior, whereas you can also > > specify > > an unsigned value > > that prevents paths to go deeper than frames. > > > > fanalyzer-trim-diagnostics= > > > Common Joined RejectNegative ToLower > > > Var(flag_analyzer_trim_diagnostics) > > > Init("system") > > > -fanalyzer-trim-diagnostics=[never|system|] Trim > > > diagnostics > > > path that are too long before emission. > > > > > > > Does it sounds reasonable and user-friendly ? > > > > Regstrapping was a success against trunk, although one of the newly > > added > > test case fails for c++14. > > Note that the test case below was done with "never", thus behaves > > exactly > > as the pre-patch analyzer > > on x86_64-linux-gnu. > > > > /* { dg-additional-options "-fdiagnostics-plain-output > > > -fdiagnostics-path-format=inline-events -fanalyzer-trim- > > > diagnostics=never" > > > } */ > > > /* { dg-skip-if "" { c++98_only } } */ > > > > > > #include > > > struct A {int x; int y;}; > > > > > > int main () { > > > std::shared_ptr a; > > > a->x = 4; /* { dg-line deref_a } */ > > > /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a > > > } */ > > > > > > return 0; > > > } > > > > > > /* { dg-begin-multiline-output "" } > > > 'int main()': events 1-2 > > > | > > > | > > > +--> 'std::__shared_ptr_access<_Tp, _Lp, , > > > > > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, , > > > >::operator->() const [with _Tp = A; > > > __gnu_cxx::_Lock_policy > > > _Lp = __gnu_cxx::_S_atomic; bool = false; bool > > > = > > > false]': events 3-4 > > >| > > >| > > >+--> 'std::__shared_ptr_access<_Tp, _Lp, , > > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > > > , >::_M_get() const [with _Tp = A; > > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool > > > = > > > false; bool = false]': events 5-6 > > > | > > > | > > > +--> 'std::__shared_ptr<_Tp, _Lp>::el
Re: [WIP RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]
On Fri, 21 Jul 2023 at 21:05, Benjamin Priour via Gcc-patches wrote: > > Hi, > > Upon David's request I've joined the in progress patch to the below email. > I hope it makes more sense now. > > Best, > Benjamin. > > -- Forwarded message - > From: Benjamin Priour > Date: Tue, Jul 18, 2023 at 3:30 PM > Subject: [RFC] analyzer: Add optional trim of the analyzer diagnostics > going too deep [PR110543] > To: , David Malcolm > > > Hi, > > I'd like to request comments on a patch I am writing for PR110543. > The goal of this patch is to reduce the noise of the analyzer emitted > diagnostics when dealing with > system headers, or simply diagnostic paths that are too long. The new > option only affects the display > of the diagnostics, but doesn't hinder the actual analysis. > > I've defaulted the new option to "system", thus preventing the diagnostic > paths from showing system headers. > "never" corresponds to the pre-patch behavior, whereas you can also specify > an unsigned value > that prevents paths to go deeper than frames. > > fanalyzer-trim-diagnostics= > > Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics) > > Init("system") > > -fanalyzer-trim-diagnostics=[never|system|] Trim diagnostics > > path that are too long before emission. > > > > Does it sounds reasonable and user-friendly ? > > Regstrapping was a success against trunk, although one of the newly added > test case fails for c++14. > Note that the test case below was done with "never", thus behaves exactly > as the pre-patch analyzer > on x86_64-linux-gnu. > > /* { dg-additional-options "-fdiagnostics-plain-output > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never" > > } */ > > /* { dg-skip-if "" { c++98_only } } */ > > > > #include > > struct A {int x; int y;}; > > > > int main () { > > std::shared_ptr a; > > a->x = 4; /* { dg-line deref_a } */ > > /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */ > > > > return 0; > > } > > > > /* { dg-begin-multiline-output "" } > > 'int main()': events 1-2 > > | > > | > > +--> 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, , > > >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy > > _Lp = __gnu_cxx::_S_atomic; bool = false; bool = > > false]': events 3-4 > >| > >| > >+--> 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > > , >::_M_get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool = > > false; bool = false]': events 5-6 > > | > > | > > +--> 'std::__shared_ptr<_Tp, _Lp>::element_type* > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8 > > | > > | > > <--+ > > | > > 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > > , >::_M_get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool = > > false; bool = false]': event 9 > > | > > | > ><--+ > >| > > 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, , > > >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy > > _Lp = __gnu_cxx::_S_atomic; bool = false; bool = > > false]': event 10 > >| > >| > > <--+ > > | > > 'int main()': events 11-12 > > | > > | > >{ dg-end-multiline-output "" } */ > > > > > The first events "'int main()': events 1-2" vary in c++14 (get events 1-3). > > > > > // c++14 with fully detailed output > > ‘int main()’: events 1-3 > > | > > |8 | int main () { > > | | ^~~~ > > | | | > > | | (1) entry to ‘main’ > > |9 | std::shared_ptr a; > > | | ~ > > | | | > > | | (2) > > ‘a.std::shared_ptr::.std::__shared_ptr > __gnu_cxx::_S_atomic>::_M_ptr’ is NULL > > | 10 | a->x = 4; /* { dg-line deref_a } */ > > | |~~ > > | || > > | |(3) calling ‘std::__shared_ptr_access > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’ > > > > whereas c++17 and posterior give > > > // c++17 with fully detailed output > > > // ./xg++ -fanalyzer > > ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C > > -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17 > > > ‘int main()’: events 1-2 > > | > > |8 | int main () { > > | | ^~~~ > > | | | > > | | (1) entry to ‘main’ > > |9 | std::shared_ptr a; > >
Re: [WIP RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]
On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote: > Hi, > > Upon David's request I've joined the in progress patch to the below > email. > I hope it makes more sense now. > > Best, > Benjamin. Thanks for posting the work-in-progress patch; it makes the idea clearer. Some thoughts about this: - I like the idea of defaulting to *not* showing events within system headers, which the patch achieves - I don't like the combination of never/system with maxdepth, in that it seems complicated and I don't think a user is likely to experiment with different depths. - Hence I think it would work better as a simple boolean, perhaps "-fanalyzer-show-events-in-system-headers" or somesuch? It seems like the sort of thing that we want to provide a sensible default for, but have the option of turning off for debugging the analyzer itself, but I don't expect an end-user to touch that option. FWIW the patch seems to have been mangled somewhat via email, so I don't have a sense of what the actual output from patched analyzer looks like. What should we output to the user with -fanalyzer and no other options for the case in PR 110543? Currently, for https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which probably only this last one is useful: (12) dereference of NULL 'a.std::__shared_ptr_access::operator->()' What does the output look like with your patch? Thanks Dave > > -- Forwarded message - > From: Benjamin Priour > Date: Tue, Jul 18, 2023 at 3:30 PM > Subject: [RFC] analyzer: Add optional trim of the analyzer > diagnostics > going too deep [PR110543] > To: , David Malcolm > > > Hi, > > I'd like to request comments on a patch I am writing for PR110543. > The goal of this patch is to reduce the noise of the analyzer emitted > diagnostics when dealing with > system headers, or simply diagnostic paths that are too long. The new > option only affects the display > of the diagnostics, but doesn't hinder the actual analysis. > > I've defaulted the new option to "system", thus preventing the > diagnostic > paths from showing system headers. > "never" corresponds to the pre-patch behavior, whereas you can also > specify > an unsigned value > that prevents paths to go deeper than frames. > > fanalyzer-trim-diagnostics= > > Common Joined RejectNegative ToLower > > Var(flag_analyzer_trim_diagnostics) > > Init("system") > > -fanalyzer-trim-diagnostics=[never|system|] Trim > > diagnostics > > path that are too long before emission. > > > > Does it sounds reasonable and user-friendly ? > > Regstrapping was a success against trunk, although one of the newly > added > test case fails for c++14. > Note that the test case below was done with "never", thus behaves > exactly > as the pre-patch analyzer > on x86_64-linux-gnu. > > /* { dg-additional-options "-fdiagnostics-plain-output > > -fdiagnostics-path-format=inline-events -fanalyzer-trim- > > diagnostics=never" > > } */ > > /* { dg-skip-if "" { c++98_only } } */ > > > > #include > > struct A {int x; int y;}; > > > > int main () { > > std::shared_ptr a; > > a->x = 4; /* { dg-line deref_a } */ > > /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a > > } */ > > > > return 0; > > } > > > > /* { dg-begin-multiline-output "" } > > 'int main()': events 1-2 > > | > > | > > +--> 'std::__shared_ptr_access<_Tp, _Lp, , > > > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, , > > >::operator->() const [with _Tp = A; > > __gnu_cxx::_Lock_policy > > _Lp = __gnu_cxx::_S_atomic; bool = false; bool > > = > > false]': events 3-4 > > | > > | > > +--> 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > > , >::_M_get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool > > = > > false; bool = false]': events 5-6 > > | > > | > > +--> 'std::__shared_ptr<_Tp, _Lp>::element_type* > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8 > > | > > | > > <--+ > > | > > 'std::__shared_ptr_access<_Tp, _Lp, , > > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > > , >::_M_get() const [with _Tp = A; > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool > > = > > false; bool = false]': event 9 > > | > > | > > <--+ > > | > > 'std::__shared_ptr_access<_Tp, _Lp, , > > > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, , > > >::operator->() const [with _Tp = A; > > __gnu_cxx::_Lock_policy > > _Lp = __gnu_cxx::_S_atomic; bool = false; bool > > = > > false]': event 10 > > | > > | > > <--+ > > | > > 'int main()': events 11-12 > > | > > |
[WIP RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]
Hi, Upon David's request I've joined the in progress patch to the below email. I hope it makes more sense now. Best, Benjamin. -- Forwarded message - From: Benjamin Priour Date: Tue, Jul 18, 2023 at 3:30 PM Subject: [RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543] To: , David Malcolm Hi, I'd like to request comments on a patch I am writing for PR110543. The goal of this patch is to reduce the noise of the analyzer emitted diagnostics when dealing with system headers, or simply diagnostic paths that are too long. The new option only affects the display of the diagnostics, but doesn't hinder the actual analysis. I've defaulted the new option to "system", thus preventing the diagnostic paths from showing system headers. "never" corresponds to the pre-patch behavior, whereas you can also specify an unsigned value that prevents paths to go deeper than frames. fanalyzer-trim-diagnostics= > Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics) > Init("system") > -fanalyzer-trim-diagnostics=[never|system|] Trim diagnostics > path that are too long before emission. > Does it sounds reasonable and user-friendly ? Regstrapping was a success against trunk, although one of the newly added test case fails for c++14. Note that the test case below was done with "never", thus behaves exactly as the pre-patch analyzer on x86_64-linux-gnu. /* { dg-additional-options "-fdiagnostics-plain-output > -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never" > } */ > /* { dg-skip-if "" { c++98_only } } */ > > #include > struct A {int x; int y;}; > > int main () { > std::shared_ptr a; > a->x = 4; /* { dg-line deref_a } */ > /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */ > > return 0; > } > > /* { dg-begin-multiline-output "" } > 'int main()': events 1-2 > | > | > +--> 'std::__shared_ptr_access<_Tp, _Lp, , > >::element_type* std::__shared_ptr_access<_Tp, _Lp, , > >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy > _Lp = __gnu_cxx::_S_atomic; bool = false; bool = > false]': events 3-4 >| >| >+--> 'std::__shared_ptr_access<_Tp, _Lp, , > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > , >::_M_get() const [with _Tp = A; > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool = > false; bool = false]': events 5-6 > | > | > +--> 'std::__shared_ptr<_Tp, _Lp>::element_type* > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A; > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8 > | > | > <--+ > | > 'std::__shared_ptr_access<_Tp, _Lp, , > >::element_type* std::__shared_ptr_access<_Tp, _Lp, > , >::_M_get() const [with _Tp = A; > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool = > false; bool = false]': event 9 > | > | ><--+ >| > 'std::__shared_ptr_access<_Tp, _Lp, , > >::element_type* std::__shared_ptr_access<_Tp, _Lp, , > >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy > _Lp = __gnu_cxx::_S_atomic; bool = false; bool = > false]': event 10 >| >| > <--+ > | > 'int main()': events 11-12 > | > | >{ dg-end-multiline-output "" } */ > The first events "'int main()': events 1-2" vary in c++14 (get events 1-3). > > // c++14 with fully detailed output > ‘int main()’: events 1-3 > | > |8 | int main () { > | | ^~~~ > | | | > | | (1) entry to ‘main’ > |9 | std::shared_ptr a; > | | ~ > | | | > | | (2) > ‘a.std::shared_ptr::.std::__shared_ptr __gnu_cxx::_S_atomic>::_M_ptr’ is NULL > | 10 | a->x = 4; /* { dg-line deref_a } */ > | |~~ > | || > | |(3) calling ‘std::__shared_ptr_access __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’ > whereas c++17 and posterior give > // c++17 with fully detailed output > // ./xg++ -fanalyzer > ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C > -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17 > ‘int main()’: events 1-2 > | > |8 | int main () { > | | ^~~~ > | | | > | | (1) entry to ‘main’ > |9 | std::shared_ptr a; > | 10 | a->x = 4; /* { dg-line deref_a } */ > | |~~ > | || > | |(2) calling ‘std::__shared_ptr_access __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’ > Is there a way to make dg-multiline-output check for a regex ? Or would checking the multiline-output only for c++17 and c++20 be acceptable ? This