[PATCH] c++: Implement -Wrange-loop-construct [PR94695]
Marek Polacek
polacek@redhat.com
Mon Sep 28 17:34:29 GMT 2020
On Fri, Sep 25, 2020 at 04:31:16PM -0600, Martin Sebor wrote:
> On 9/24/20 6:05 PM, Marek Polacek via Gcc-patches wrote:
> > This new warning can be used to prevent expensive copies inside range-based
> > for-loops, for instance:
> >
> > struct S { char arr[128]; };
> > void fn () {
> > S arr[5];
> > for (const auto x : arr) { }
> > }
> >
> > where auto deduces to S and then we copy the big S in every iteration.
> > Using "const auto &x" would not incur such a copy. With this patch the
> > compiler will warn:
> >
> > q.C:4:19: warning: loop variable 'x' creates a copy from type 'const S' [-Wrange-loop-construct]
> > 4 | for (const auto x : arr) { }
> > | ^
> > q.C:4:19: note: use reference type 'const S&' to prevent copying
> > 4 | for (const auto x : arr) { }
> > | ^
> > | &
> >
> > As per Clang, this warning is suppressed for trivially copyable types
> > whose size does not exceed 64B. The tricky part of the patch was how
> > to figure out if using a reference would have prevented a copy. I've
> > used perform_implicit_conversion to perform the imaginary conversion.
> > Then if the conversion doesn't have any side-effects, I assume it does
> > not call any functions or create any TARGET_EXPRs, and is just a simple
> > assignment like this one:
> >
> > const T &x = (const T &) <__for_begin>;
> >
> > But it can also be a CALL_EXPR:
> >
> > x = (const T &) Iterator<T&>::operator* (&__for_begin)
> >
> > which is still fine -- we just use the return value and don't create
> > any copies.
> >
> > This warning is enabled by -Wall. Further warnings of similar nature
> > should follow soon.
>
> I've always thought a warning like this would be useful when passing
> large objects to functions by value. Is adding one for these cases
> what you mean by future warnings?
No, but perhaps we should add it. I don't know if we could still enable it by
-Wall. We'd have to handle guaranteed copy elision and also the case when we
pass classes by invisible reference. Unsure how much of the implementation
these warnings could share.
Do we have a request for the warning wrt passing chunky objects by value?
As a user, I'd probably want to have the option of figuring out where I'm
copying large types, since it can be a performance issue.
> For the range loop, I wonder if more could be done to elide the copy
> and avoid the warning when it isn't really necessary. For instance,
> for trivially copyable types like in your example, since x is const,
> modifying it would be undefined, and so when we can prove that
> the original object itself isn't modified (e.g., because it's
> declared const, or because it can't be accessed in the loop),
> there should be no need to make a copy on each iteration. Using
> a reference to the original object should be sufficient. Does C++
> rule out such an optimization?
Well, changing const auto x in
struct S { char arr[128]; S(); };
void
fn ()
{
S a[5];
for (const auto x : a)
decltype(x) k;
}
to const auto &x would break this code.
> About the name of the option: my first thought was that it was
> about the construct known as the range loop, but after reading
> your description I wonder if it might actually primarily be about
> constructing expensive copies and the range loop is incidental.
It was a bit confusing to me too at first. It's about constructing expensive
copies in range-based for-loops. I don't think it's incidental that
it warns in loops only.
I'm not attached to the name but it's what Clang uses so we'll have to
follow.
> (It's impossible to tell from the Clang manual because its way
> of documenting warning options is to show examples of their text.)
Yes. I really like that we provide code snippets showing what a warning
is supposed to warn on in our manual. Let's keep it that way.
> Then again, I see it's related to -Wrange-loop-analysis so that
> suggests it is mainly about range loops, and that there may be
> a whole series of warnings and options related to it. Can you
> please shed some light on that? (E.g., what are some of
> the "further warnings of similar nature" about?) I think it
> might also be helpful to expand the documentation a bit to help
> answer common questions (I came across the following post while
> looking it up: https://stackoverflow.com/questions/50066139).
I think right now it's like this (note the names and wording changed recently,
this is the latest version):
-> -Wfor-loop-analysis
|
-Wloop-analysis -| -> -Wrange-loop-bind-reference
| |
-> -Wrange-loop-analysis -|
|
-> -Wrange-loop-construct
* -Wloop-analysis and -Wrange-loop-analysis are umbrella flags.
* -Wfor-loop-analysis warns about
void f ()
{
for (int i = 0; i < 10;) {}
}
variable 'i' used in loop condition not modified in loop body [-Wfor-loop-analysis]
and
void f ()
{
for (int i = 0; i < 10; i++)
{
i++;
}
}
warning: variable 'i' is incremented both in the loop header and in the loop body [-Wfor-loop-analysis]
No plans to implement this warning now, but it'd probably be useful. Needs
to be implemented in both the C and C++ FE. Enabled by -Wall.
* -Wrange-loop-bind-reference warns about
void f ()
{
Container<int> C;
for (const int &x : C) {}
}
warning: loop variable 'x' binds to a temporary value produced by a range of type 'Container<int>' [-Wrange-loop-bind-reference]
Not enabled by -Wall (anymore). Not sure if we should add this one.
* -Wrange-loop-construct warns about
struct S { char a[128]; };
void f ()
{
S arr[5];
for (const S x : arr) {}
}
warning: loop variable 'x' creates a copy from type 'const S' [-Wrange-loop-construct]
and also about
void f ()
{
Container<int&> foo;
for (const double &x : foo) {}
}
warning: loop variable 'x' of type 'const double &' binds to a temporary constructed from type 'int &' [-Wrange-loop-construct]
note: use non-reference type 'double' to make construction explicit or type 'const int &' to prevent copying
Both enabled by -Wall. My patch implements the first part. I think I should
also tackle the second part while at it.
Which one of these do you find most appealing? ;)
Marek
More information about the Gcc-patches
mailing list