Discussion:
[cfe-commits] Clang change to detect and warn about unused private members
Daniel Jasper
2012-01-11 09:11:39 UTC
Permalink
Hi,

the attached change is designed to detect and warn about private unused
members of C++ classes. It checks whether a class is fully defined in the
current translation unit, i.e. all methods are either defined or pure
virtual and all friends are defined. Otherwise, the private member could be
used by a function defined in another translation unit.

An initializer does not count as "use", if:
- the member is a primitive type
- the member is a pointer
- the initializer does not take any arguments

Kind regards,
Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120111/e308c9b0/attachment.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: unused-private-members.patch
Type: text/x-patch
Size: 8021 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120111/e308c9b0/attachment.bin
Matthieu Monrocq
2012-01-11 20:02:18 UTC
Permalink
Le 11 janvier 2012 10:11, Daniel Jasper <djasper at google.com> a ?crit :

> Hi,
>
> the attached change is designed to detect and warn about private unused
> members of C++ classes. It checks whether a class is fully defined in the
> current translation unit, i.e. all methods are either defined or pure
> virtual and all friends are defined. Otherwise, the private member could be
> used by a function defined in another translation unit.
>
> An initializer does not count as "use", if:
> - the member is a primitive type
> - the member is a pointer
> - the initializer does not take any arguments
>
> Kind regards,
> Daniel
>
>
Hello Daniel,

it sounds like a fairly useful warning :)

I only have two comments regarding the patch itself:

> is it necessary to actually have UnusedPrivateFields and
(Not)FullyDefinedCXXRecords as members of Sema ? They could be passed (by
reference) to IsRecordFullyDefinedInTranslationUnit which could then be
marked `const` (unless I missed something)

> there is no test case

Nice patch.

-- Matthieu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120111/df8c841b/attachment.html
Chandler Carruth
2012-01-11 21:17:00 UTC
Permalink
n Wed, Jan 11, 2012 at 12:02 PM, Matthieu Monrocq <
matthieu.monrocq at gmail.com> wrote:

>
>
> Le 11 janvier 2012 10:11, Daniel Jasper <djasper at google.com> a ?crit :
>
> Hi,
>>
>> the attached change is designed to detect and warn about private unused
>> members of C++ classes. It checks whether a class is fully defined in the
>> current translation unit, i.e. all methods are either defined or pure
>> virtual and all friends are defined. Otherwise, the private member could be
>> used by a function defined in another translation unit.
>>
>> An initializer does not count as "use", if:
>> - the member is a primitive type
>> - the member is a pointer
>> - the initializer does not take any arguments
>>
>> Kind regards,
>> Daniel
>>
>>
> Hello Daniel,
>
> it sounds like a fairly useful warning :)
>
> I only have two comments regarding the patch itself:
>
> > is it necessary to actually have UnusedPrivateFields and
> (Not)FullyDefinedCXXRecords as members of Sema ? They could be passed (by
> reference) to IsRecordFullyDefinedInTranslationUnit which could then be
> marked `const` (unless I missed something)
>

I agree. Actually, I'd go further.

IsRecordFullyDefinedInTranslationUnit should be a static helper function in
the file, not a member of Sema. Then the two sets and a Sema& can be passed
to it.

You can find lots of similar static helper functions that accept a Sema& as
their first argument and use them as examples.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120111/31fe3d74/attachment.html
Daniel Jasper
2012-01-12 18:19:17 UTC
Permalink
Hi,

comments inline.

On Wed, Jan 11, 2012 at 9:02 PM, Matthieu Monrocq <
matthieu.monrocq at gmail.com> wrote:

>
>
> Le 11 janvier 2012 10:11, Daniel Jasper <djasper at google.com> a ?crit :
>
> Hi,
>>
>> the attached change is designed to detect and warn about private unused
>> members of C++ classes. It checks whether a class is fully defined in the
>> current translation unit, i.e. all methods are either defined or pure
>> virtual and all friends are defined. Otherwise, the private member could be
>> used by a function defined in another translation unit.
>>
>> An initializer does not count as "use", if:
>> - the member is a primitive type
>> - the member is a pointer
>> - the initializer does not take any arguments
>>
>> Kind regards,
>> Daniel
>>
>>
> Hello Daniel,
>
> it sounds like a fairly useful warning :)
>
> I only have two comments regarding the patch itself:
>
> > is it necessary to actually have UnusedPrivateFields and
> (Not)FullyDefinedCXXRecords as members of Sema ? They could be passed (by
> reference) to IsRecordFullyDefinedInTranslationUnit which could then be
> marked `const` (unless I missed something)
>

I will change that, probably according to Chandler's suggestion (make it
static helper function).


> > there is no test case
>

Sorry, I forgot to include that in the patch (there actually is a test). I
will include it in the next version.

Cheers,
Daniel


>
> Nice patch.
>
> -- Matthieu
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120112/7e0bbc1e/attachment.html
Nico Weber
2012-01-11 21:49:15 UTC
Permalink
Hi Daniel,

On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com> wrote:
> Hi,
>
> the attached change is designed to detect and warn about private unused
> members of C++ classes. It checks whether a class is fully defined in the
> current translation unit, i.e. all methods are either?defined?or?pure
> virtual?and all friends are defined. Otherwise, the private member could be
> used by a function defined in another translation unit.

that's a cool warning! Here are a few cases where it flags false
positives (it finds tons of true positives too, but those are boring
:-) ).

It flags |stackArray| in ICU's cmemory.h. This is declared for storage
and accessed through pointer aliasing, probably not much that can be
done about this:
http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285

In flags StaticResource::instance_ in v8's utils.h:
http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
This is supposed to be accessed through the class below, Access, which
is a friend of utils.h. Do you check if any friends use private
variables?

In chromium itself, it flags ShadowingAtExitManager member variables.
This is basically a RAII class which only exists to call a protected
superclass constructor, and which only exists if UNIT_TESTS is
defined: http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
So maybe the "constructor without arguments" heuristic could be
tweaked to exclude constructors that call superclass constructors with
arguments?
(Here's another RAII class whose constructor takes 0 arguments:
http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
)

(This is not an exhaustive list, just the things I found after a quick look.)

Nico
Nico Weber
2012-01-11 23:02:38 UTC
Permalink
On Wed, Jan 11, 2012 at 1:49 PM, Nico Weber <thakis at chromium.org> wrote:
> Hi Daniel,
>
> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com> wrote:
>> Hi,
>>
>> the attached change is designed to detect and warn about private unused
>> members of C++ classes. It checks whether a class is fully defined in the
>> current translation unit, i.e. all methods are either?defined?or?pure
>> virtual?and all friends are defined. Otherwise, the private member could be
>> used by a function defined in another translation unit.
>
> that's a cool warning! Here are a few cases where it flags false
> positives (it finds tons of true positives too, but those are boring
> :-) ).
>
> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
> and accessed through pointer aliasing, probably not much that can be
> done about this:
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285

(Sorry, I highlighted the wrong line. It complains about line 460, but
as zygoloid and chandler commented off-list, that class has undefined
behavior anyway.)

>
> In flags StaticResource::instance_ in v8's utils.h:
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
> This is supposed to be accessed through the class below, Access, which
> is a friend of utils.h. Do you check if any friends use private
> variables?
>
> In chromium itself, it flags ShadowingAtExitManager member variables.
> This is basically a RAII class which only exists to call a protected
> superclass constructor, and which only exists if UNIT_TESTS is
> defined: http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
> So maybe the "constructor without arguments" heuristic could be
> tweaked to exclude constructors that call superclass constructors with
> arguments?
> (Here's another RAII class whose constructor takes 0 arguments:
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
> )
>
> (This is not an exhaustive list, just the things I found after a quick look.)
>
> Nico
Daniel Jasper
2012-01-12 18:27:41 UTC
Permalink
Forgot to CC the list..

---------- Forwarded message ----------
From: Daniel Jasper <djasper at google.com>
Date: Thu, Jan 12, 2012 at 7:26 PM
Subject: Re: [cfe-commits] Clang change to detect and warn about unused
private members
To: Nico Weber <thakis at chromium.org>


Hi Nico,

comments inline.

On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org> wrote:

> Hi Daniel,
>
> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com> wrote:
> > Hi,
> >
> > the attached change is designed to detect and warn about private unused
> > members of C++ classes. It checks whether a class is fully defined in the
> > current translation unit, i.e. all methods are either defined or pure
> > virtual and all friends are defined. Otherwise, the private member could
> be
> > used by a function defined in another translation unit.
>
> that's a cool warning! Here are a few cases where it flags false
> positives (it finds tons of true positives too, but those are boring
> :-) ).
>
> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
> and accessed through pointer aliasing, probably not much that can be
> done about this:
>
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285


As you said in the other email, it is the wrong code line and I think, it
is correct to warn about the other |stackArray|.


>
> In flags StaticResource::instance_ in v8's utils.h:
>
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
> This is supposed to be accessed through the class below, Access, which
> is a friend of utils.h. Do you check if any friends use private
> variables?
>

Yes, I check friends. This is a bug, I know why it happens (it is because
of the dependent templates) and I will fix it.


> In chromium itself, it flags ShadowingAtExitManager member variables.
> This is basically a RAII class which only exists to call a protected
> superclass constructor, and which only exists if UNIT_TESTS is
> defined:
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
> So maybe the "constructor without arguments" heuristic could be
> tweaked to exclude constructors that call superclass constructors with
> arguments?
> (Here's another RAII class whose constructor takes 0 arguments:
>
> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
> )
>

I have changed it to be more conservative (I will send a new patch once I
have fixed the bug mentioned above). It now only accepts members without
initializer or with an argument-less initializer, if the field's type has a
trivial default constructor and a trivial destructor. Unfortunately, we now
also miss a lot of true positives, but I guess the false positives are
worse, especially because there is no easy way to explicitly suppress the
warning for these cases within the standard syntax.

Cheers,
Daniel


>
> (This is not an exhaustive list, just the things I found after a quick
> look.)
>
> Nico
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120112/f1face5e/attachment.html
David Blaikie
2012-01-12 19:15:49 UTC
Permalink
On Thu, Jan 12, 2012 at 10:27 AM, Daniel Jasper <djasper at google.com> wrote:

> Forgot to CC the list..
>
> ---------- Forwarded message ----------
> From: Daniel Jasper <djasper at google.com>
> Date: Thu, Jan 12, 2012 at 7:26 PM
> Subject: Re: [cfe-commits] Clang change to detect and warn about unused
> private members
> To: Nico Weber <thakis at chromium.org>
>
>
> Hi Nico,
>
> comments inline.
>
> On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org> wrote:
>
>> Hi Daniel,
>>
>> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com>
>> wrote:
>> > Hi,
>> >
>> > the attached change is designed to detect and warn about private unused
>> > members of C++ classes. It checks whether a class is fully defined in
>> the
>> > current translation unit, i.e. all methods are either defined or pure
>> > virtual and all friends are defined. Otherwise, the private member
>> could be
>> > used by a function defined in another translation unit.
>>
>> that's a cool warning! Here are a few cases where it flags false
>> positives (it finds tons of true positives too, but those are boring
>> :-) ).
>>
>> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
>> and accessed through pointer aliasing, probably not much that can be
>> done about this:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285
>
>
> As you said in the other email, it is the wrong code line and I think, it
> is correct to warn about the other |stackArray|.
>
>
>>
>> In flags StaticResource::instance_ in v8's utils.h:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
>> This is supposed to be accessed through the class below, Access, which
>> is a friend of utils.h. Do you check if any friends use private
>> variables?
>>
>
> Yes, I check friends. This is a bug, I know why it happens (it is because
> of the dependent templates) and I will fix it.
>
>
>> In chromium itself, it flags ShadowingAtExitManager member variables.
>> This is basically a RAII class which only exists to call a protected
>> superclass constructor, and which only exists if UNIT_TESTS is
>> defined:
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
>> So maybe the "constructor without arguments" heuristic could be
>> tweaked to exclude constructors that call superclass constructors with
>> arguments?
>> (Here's another RAII class whose constructor takes 0 arguments:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
>> )
>>
>
> I have changed it to be more conservative (I will send a new patch once I
> have fixed the bug mentioned above). It now only accepts members without
> initializer or with an argument-less initializer, if the field's type has a
> trivial default constructor and a trivial destructor. Unfortunately, we now
> also miss a lot of true positives, but I guess the false positives are
> worse, especially because there is no easy way to explicitly suppress the
> warning for these cases within the standard syntax.
>

I haven't looked at the test cases yet - but one way you could support a
noisier warning with a syntax for suppression would be to suppress the
warning if the member is explicitly initialized in at least one ctor? (or
inline with the new C++ inline initialization syntax) even to a default
ctor so this:

class foo { std::string bar; };

Would trigger the warning, but this wouldn't:

class foo { std::string bar; foo() : bar() { } };

But that makes the ctor non-default, which is a pity (& could have other
ramifications)... though only because there was no ctor in the first place
which is relatively rare, perhaps.

Just a thought,
- David


>
> Cheers,
> Daniel
>
>
>>
>> (This is not an exhaustive list, just the things I found after a quick
>> look.)
>>
>> Nico
>>
>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120112/8a951f83/attachment.html
Daniel Jasper
2012-01-18 22:28:58 UTC
Permalink
Hi,

here is a new version of the proposed change. Changes:

1) The warning is more conservative. Members of a class-type can now only
count as unused if a trivial default constructor and a trivial destructor
are used (as these don't have side-effects). A smarter analysis for
side-effect-free constructors and destructors can follow. This prevents
false positives on RAII-style classes.
2) The warning gives up if a class has a template friend (specializations
of this friend could use any unused member).
3) Only instantiations of template classes are analyzed, not the template
classes themselves (this was a major source of false positives).
4) Testcase is include in the patch this time.

Please let me know, what you think!

Cheers,
Daniel


On Thu, Jan 12, 2012 at 7:26 PM, Daniel Jasper <djasper at google.com> wrote:

> Hi Nico,
>
> comments inline.
>
> On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org> wrote:
>
>> Hi Daniel,
>>
>> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com>
>> wrote:
>> > Hi,
>> >
>> > the attached change is designed to detect and warn about private unused
>> > members of C++ classes. It checks whether a class is fully defined in
>> the
>> > current translation unit, i.e. all methods are either defined or pure
>> > virtual and all friends are defined. Otherwise, the private member
>> could be
>> > used by a function defined in another translation unit.
>>
>> that's a cool warning! Here are a few cases where it flags false
>> positives (it finds tons of true positives too, but those are boring
>> :-) ).
>>
>> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
>> and accessed through pointer aliasing, probably not much that can be
>> done about this:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285
>
>
> As you said in the other email, it is the wrong code line and I think, it
> is correct to warn about the other |stackArray|.
>
>
>>
>> In flags StaticResource::instance_ in v8's utils.h:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
>> This is supposed to be accessed through the class below, Access, which
>> is a friend of utils.h. Do you check if any friends use private
>> variables?
>>
>
> Yes, I check friends. This is a bug, I know why it happens (it is because
> of the dependent templates) and I will fix it.
>
>
>> In chromium itself, it flags ShadowingAtExitManager member variables.
>> This is basically a RAII class which only exists to call a protected
>> superclass constructor, and which only exists if UNIT_TESTS is
>> defined:
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
>> So maybe the "constructor without arguments" heuristic could be
>> tweaked to exclude constructors that call superclass constructors with
>> arguments?
>> (Here's another RAII class whose constructor takes 0 arguments:
>>
>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
>> )
>>
>
> I have changed it to be more conservative (I will send a new patch once I
> have fixed the bug mentioned above). It now only accepts members without
> initializer or with an argument-less initializer, if the field's type has a
> trivial default constructor and a trivial destructor. Unfortunately, we now
> also miss a lot of true positives, but I guess the false positives are
> worse, especially because there is no easy way to explicitly suppress the
> warning for these cases within the standard syntax.
>
> Cheers,
> Daniel
>
>
>>
>> (This is not an exhaustive list, just the things I found after a quick
>> look.)
>>
>> Nico
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120118/743988c2/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 13506 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120118/743988c2/attachment-0001.obj
Daniel Jasper
2012-01-27 02:24:52 UTC
Permalink
Hi,

has anyone had a chance to look at this?

Kind regards,
Daniel

On Wed, Jan 18, 2012 at 10:28 PM, Daniel Jasper <djasper at google.com> wrote:

> Hi,
>
> here is a new version of the proposed change. Changes:
>
> 1) The warning is more conservative. Members of a class-type can now only
> count as unused if a trivial default constructor and a trivial destructor
> are used (as these don't have side-effects). A smarter analysis for
> side-effect-free constructors and destructors can follow. This prevents
> false positives on RAII-style classes.
> 2) The warning gives up if a class has a template friend (specializations
> of this friend could use any unused member).
> 3) Only instantiations of template classes are analyzed, not the template
> classes themselves (this was a major source of false positives).
> 4) Testcase is include in the patch this time.
>
> Please let me know, what you think!
>
> Cheers,
> Daniel
>
>
> On Thu, Jan 12, 2012 at 7:26 PM, Daniel Jasper <djasper at google.com> wrote:
>
>> Hi Nico,
>>
>> comments inline.
>>
>> On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org> wrote:
>>
>>> Hi Daniel,
>>>
>>> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com>
>>> wrote:
>>> > Hi,
>>> >
>>> > the attached change is designed to detect and warn about private unused
>>> > members of C++ classes. It checks whether a class is fully defined in
>>> the
>>> > current translation unit, i.e. all methods are either defined or pure
>>> > virtual and all friends are defined. Otherwise, the private member
>>> could be
>>> > used by a function defined in another translation unit.
>>>
>>> that's a cool warning! Here are a few cases where it flags false
>>> positives (it finds tons of true positives too, but those are boring
>>> :-) ).
>>>
>>> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
>>> and accessed through pointer aliasing, probably not much that can be
>>> done about this:
>>>
>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285
>>
>>
>> As you said in the other email, it is the wrong code line and I think, it
>> is correct to warn about the other |stackArray|.
>>
>>
>>>
>>> In flags StaticResource::instance_ in v8's utils.h:
>>>
>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
>>> This is supposed to be accessed through the class below, Access, which
>>> is a friend of utils.h. Do you check if any friends use private
>>> variables?
>>>
>>
>> Yes, I check friends. This is a bug, I know why it happens (it is because
>> of the dependent templates) and I will fix it.
>>
>>
>>> In chromium itself, it flags ShadowingAtExitManager member variables.
>>> This is basically a RAII class which only exists to call a protected
>>> superclass constructor, and which only exists if UNIT_TESTS is
>>> defined:
>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
>>> So maybe the "constructor without arguments" heuristic could be
>>> tweaked to exclude constructors that call superclass constructors with
>>> arguments?
>>> (Here's another RAII class whose constructor takes 0 arguments:
>>>
>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
>>> )
>>>
>>
>> I have changed it to be more conservative (I will send a new patch once I
>> have fixed the bug mentioned above). It now only accepts members without
>> initializer or with an argument-less initializer, if the field's type has a
>> trivial default constructor and a trivial destructor. Unfortunately, we now
>> also miss a lot of true positives, but I guess the false positives are
>> worse, especially because there is no easy way to explicitly suppress the
>> warning for these cases within the standard syntax.
>>
>> Cheers,
>> Daniel
>>
>>
>>>
>>> (This is not an exhaustive list, just the things I found after a quick
>>> look.)
>>>
>>> Nico
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120127/a96d56df/attachment.html
Chandler Carruth
2012-02-01 18:27:10 UTC
Permalink
Can you re-send the patch? that way patchwork can track it.

On Thu, Jan 26, 2012 at 6:24 PM, Daniel Jasper <djasper at google.com> wrote:

> Hi,
>
> has anyone had a chance to look at this?
>
> Kind regards,
> Daniel
>
>
> On Wed, Jan 18, 2012 at 10:28 PM, Daniel Jasper <djasper at google.com>wrote:
>
>> Hi,
>>
>> here is a new version of the proposed change. Changes:
>>
>> 1) The warning is more conservative. Members of a class-type can now only
>> count as unused if a trivial default constructor and a trivial destructor
>> are used (as these don't have side-effects). A smarter analysis for
>> side-effect-free constructors and destructors can follow. This prevents
>> false positives on RAII-style classes.
>> 2) The warning gives up if a class has a template friend (specializations
>> of this friend could use any unused member).
>> 3) Only instantiations of template classes are analyzed, not the template
>> classes themselves (this was a major source of false positives).
>> 4) Testcase is include in the patch this time.
>>
>> Please let me know, what you think!
>>
>> Cheers,
>> Daniel
>>
>>
>> On Thu, Jan 12, 2012 at 7:26 PM, Daniel Jasper <djasper at google.com>wrote:
>>
>>> Hi Nico,
>>>
>>> comments inline.
>>>
>>> On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org>wrote:
>>>
>>>> Hi Daniel,
>>>>
>>>> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com>
>>>> wrote:
>>>> > Hi,
>>>> >
>>>> > the attached change is designed to detect and warn about private
>>>> unused
>>>> > members of C++ classes. It checks whether a class is fully defined in
>>>> the
>>>> > current translation unit, i.e. all methods are either defined or pure
>>>> > virtual and all friends are defined. Otherwise, the private member
>>>> could be
>>>> > used by a function defined in another translation unit.
>>>>
>>>> that's a cool warning! Here are a few cases where it flags false
>>>> positives (it finds tons of true positives too, but those are boring
>>>> :-) ).
>>>>
>>>> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
>>>> and accessed through pointer aliasing, probably not much that can be
>>>> done about this:
>>>>
>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285
>>>
>>>
>>> As you said in the other email, it is the wrong code line and I think,
>>> it is correct to warn about the other |stackArray|.
>>>
>>>
>>>>
>>>> In flags StaticResource::instance_ in v8's utils.h:
>>>>
>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
>>>> This is supposed to be accessed through the class below, Access, which
>>>> is a friend of utils.h. Do you check if any friends use private
>>>> variables?
>>>>
>>>
>>> Yes, I check friends. This is a bug, I know why it happens (it is
>>> because of the dependent templates) and I will fix it.
>>>
>>>
>>>> In chromium itself, it flags ShadowingAtExitManager member variables.
>>>> This is basically a RAII class which only exists to call a protected
>>>> superclass constructor, and which only exists if UNIT_TESTS is
>>>> defined:
>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
>>>> So maybe the "constructor without arguments" heuristic could be
>>>> tweaked to exclude constructors that call superclass constructors with
>>>> arguments?
>>>> (Here's another RAII class whose constructor takes 0 arguments:
>>>>
>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
>>>> )
>>>>
>>>
>>> I have changed it to be more conservative (I will send a new patch once
>>> I have fixed the bug mentioned above). It now only accepts members without
>>> initializer or with an argument-less initializer, if the field's type has a
>>> trivial default constructor and a trivial destructor. Unfortunately, we now
>>> also miss a lot of true positives, but I guess the false positives are
>>> worse, especially because there is no easy way to explicitly suppress the
>>> warning for these cases within the standard syntax.
>>>
>>> Cheers,
>>> Daniel
>>>
>>>
>>>>
>>>> (This is not an exhaustive list, just the things I found after a quick
>>>> look.)
>>>>
>>>> Nico
>>>>
>>>
>>>
>>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120201/a5594317/attachment-0001.html
Daniel Jasper
2012-02-01 18:31:17 UTC
Permalink
Sure..

On Wed, Feb 1, 2012 at 10:27 AM, Chandler Carruth <chandlerc at google.com>wrote:

> Can you re-send the patch? that way patchwork can track it.
>
> On Thu, Jan 26, 2012 at 6:24 PM, Daniel Jasper <djasper at google.com> wrote:
>
>> Hi,
>>
>> has anyone had a chance to look at this?
>>
>> Kind regards,
>> Daniel
>>
>>
>> On Wed, Jan 18, 2012 at 10:28 PM, Daniel Jasper <djasper at google.com>wrote:
>>
>>> Hi,
>>>
>>> here is a new version of the proposed change. Changes:
>>>
>>> 1) The warning is more conservative. Members of a class-type can now
>>> only count as unused if a trivial default constructor and a trivial
>>> destructor are used (as these don't have side-effects). A smarter analysis
>>> for side-effect-free constructors and destructors can follow. This prevents
>>> false positives on RAII-style classes.
>>> 2) The warning gives up if a class has a template friend
>>> (specializations of this friend could use any unused member).
>>> 3) Only instantiations of template classes are analyzed, not the
>>> template classes themselves (this was a major source of false positives).
>>> 4) Testcase is include in the patch this time.
>>>
>>> Please let me know, what you think!
>>>
>>> Cheers,
>>> Daniel
>>>
>>>
>>> On Thu, Jan 12, 2012 at 7:26 PM, Daniel Jasper <djasper at google.com>wrote:
>>>
>>>> Hi Nico,
>>>>
>>>> comments inline.
>>>>
>>>> On Wed, Jan 11, 2012 at 10:49 PM, Nico Weber <thakis at chromium.org>wrote:
>>>>
>>>>> Hi Daniel,
>>>>>
>>>>> On Wed, Jan 11, 2012 at 1:11 AM, Daniel Jasper <djasper at google.com>
>>>>> wrote:
>>>>> > Hi,
>>>>> >
>>>>> > the attached change is designed to detect and warn about private
>>>>> unused
>>>>> > members of C++ classes. It checks whether a class is fully defined
>>>>> in the
>>>>> > current translation unit, i.e. all methods are either defined or pure
>>>>> > virtual and all friends are defined. Otherwise, the private member
>>>>> could be
>>>>> > used by a function defined in another translation unit.
>>>>>
>>>>> that's a cool warning! Here are a few cases where it flags false
>>>>> positives (it finds tons of true positives too, but those are boring
>>>>> :-) ).
>>>>>
>>>>> It flags |stackArray| in ICU's cmemory.h. This is declared for storage
>>>>> and accessed through pointer aliasing, probably not much that can be
>>>>> done about this:
>>>>>
>>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/third_party/icu/source/common/cmemory.h&exact_package=chromium&q=file:cmemory.h&l=285
>>>>
>>>>
>>>> As you said in the other email, it is the wrong code line and I think,
>>>> it is correct to warn about the other |stackArray|.
>>>>
>>>>
>>>>>
>>>>> In flags StaticResource::instance_ in v8's utils.h:
>>>>>
>>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/v8/src/utils.h&exact_package=chromium&q=file:v8/src/utils.h&l=300
>>>>> This is supposed to be accessed through the class below, Access, which
>>>>> is a friend of utils.h. Do you check if any friends use private
>>>>> variables?
>>>>>
>>>>
>>>> Yes, I check friends. This is a bug, I know why it happens (it is
>>>> because of the dependent templates) and I will fix it.
>>>>
>>>>
>>>>> In chromium itself, it flags ShadowingAtExitManager member variables.
>>>>> This is basically a RAII class which only exists to call a protected
>>>>> superclass constructor, and which only exists if UNIT_TESTS is
>>>>> defined:
>>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/at_exit.h&l=71
>>>>> So maybe the "constructor without arguments" heuristic could be
>>>>> tweaked to exclude constructors that call superclass constructors with
>>>>> arguments?
>>>>> (Here's another RAII class whose constructor takes 0 arguments:
>>>>>
>>>>> http://codesearch.google.com/codesearch#OAMlx_jo-ck/src/base/logging_unittest.cc&exact_package=chromium&q=logstatesaver&l=26
>>>>> )
>>>>>
>>>>
>>>> I have changed it to be more conservative (I will send a new patch once
>>>> I have fixed the bug mentioned above). It now only accepts members without
>>>> initializer or with an argument-less initializer, if the field's type has a
>>>> trivial default constructor and a trivial destructor. Unfortunately, we now
>>>> also miss a lot of true positives, but I guess the false positives are
>>>> worse, especially because there is no easy way to explicitly suppress the
>>>> warning for these cases within the standard syntax.
>>>>
>>>> Cheers,
>>>> Daniel
>>>>
>>>>
>>>>>
>>>>> (This is not an exhaustive list, just the things I found after a quick
>>>>> look.)
>>>>>
>>>>> Nico
>>>>>
>>>>
>>>>
>>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120201/ae0b7f99/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 13506 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120201/ae0b7f99/attachment-0001.obj
Chandler Carruth
2012-02-01 18:52:25 UTC
Permalink
Thanks, comments inline... This is sadly a pretty cursory read, sorry,
working on other stuff. I think, especially given the "used" concept at
play here, Richard or Eli looking at this would be very good. =] Thanks
again for working on it.

--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -134,6 +134,9 @@ def warn_unneeded_member_function : Warning<
"member function %0 is not needed and will not be emitted">,
InGroup<UnneededMemberFunction>, DefaultIgnore;

+def warn_unused_private_field: Warning<"private field %0 is not used">,
+ InGroup<UnusedPrivateField>, DefaultIgnore;
+

I wouldn't separate this in its own group w/ vertical whitespace, but
rather jam it in with the other unused warning messages.

--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -1617,8 +1617,25 @@ Sema::ActOnCXXMemberDeclarator(Scope *S,
AccessSpecifier AS, Declarator &D,

assert((Name || isInstField) && "No identifier for non-field ?");

- if (isInstField)
- FieldCollector->Add(cast<FieldDecl>(Member));
+ if (isInstField) {
+ FieldDecl *FD = cast<FieldDecl>(Member);
+ FieldCollector->Add(FD);
+ if (FD->getAccess() == AS_private) {
+ bool hasNoSideEffects = true;
+ if (!FD->getType().isNull()) {
+ if (const CXXRecordDecl *RD =
+ FD->getType().getTypePtrOrNull()->getAsCXXRecordDecl()) {

You shouldn't need getTypePtrOrNull() here. QualType provides an ->
overload that delegates to Type.

+ if
(!FD->getParent()->getTypeForDecl()->isInstantiationDependentType() &&
+ hasNoSideEffects)

I'm a bit surprised at checking InstantiationDependentType here... Not just
Dependent... Also some comments as to what this is supposed to handle would
be good...

@@ -2063,6 +2080,15 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
assert((DirectMember || IndirectMember) &&
"Member must be a FieldDecl or IndirectFieldDecl");

+ // FIXME: Allow more initializers not to count as usage, e.g. where all
+ // arguments are primitive types.
+ ValueDecl *VD = dyn_cast<ValueDecl>(Member);

If you know the cast will always succeed, just use "cast" rather than
"dyn_cast".

+ if (!VD->getType()->isBuiltinType() &&
+ !VD->getType()->isAnyPointerType() &&

Why are these here? We already have constraints on the type of things above
when we check the Field Decl? Maybe I just don't understand what these are
doing.

--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1791,6 +1791,19 @@ Sema::InstantiateClass(SourceLocation
PointOfInstantiation,
if (OldField->getInClassInitializer())
FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
Field));
+ if (Field->getAccess() == AS_private) {
+ bool hasNoSideEffects = true;
+ if (!Field->getType().isNull()) {
+ if (const CXXRecordDecl *RD =
+ Field->getType().getTypePtrOrNull()->getAsCXXRecordDecl())
{
+ hasNoSideEffects = RD->isCompleteDefinition() &&
+ RD->hasTrivialDefaultConstructor() &&
+ RD->hasTrivialDestructor();
+ }
+ }
+ if (hasNoSideEffects)
+ UnusedPrivateFields.insert(Field);
+ }

This is the second copy of this pattern. We should either have template
instantiation call the not-template Sema routines to do this, or we should
factor the predicate into a helper somewhere, likely FieldDecl.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120201/dda3ba39/attachment.html
Daniel Jasper
2012-02-06 10:04:38 UTC
Permalink
Comments inline, new version attached.

On Wed, Feb 1, 2012 at 10:52 AM, Chandler Carruth <chandlerc at google.com>wrote:

> Thanks, comments inline... This is sadly a pretty cursory read, sorry,
> working on other stuff. I think, especially given the "used" concept at
> play here, Richard or Eli looking at this would be very good. =] Thanks
> again for working on it.
>
> --- a/include/clang/Basic/DiagnosticSemaKinds.td
> +++ b/include/clang/Basic/DiagnosticSemaKinds.td
> @@ -134,6 +134,9 @@ def warn_unneeded_member_function : Warning<
> "member function %0 is not needed and will not be emitted">,
> InGroup<UnneededMemberFunction>, DefaultIgnore;
>
> +def warn_unused_private_field: Warning<"private field %0 is not used">,
> + InGroup<UnusedPrivateField>, DefaultIgnore;
> +
>
> I wouldn't separate this in its own group w/ vertical whitespace, but
> rather jam it in with the other unused warning messages.
>

Done.


> --- a/lib/Sema/SemaDeclCXX.cpp
> +++ b/lib/Sema/SemaDeclCXX.cpp
> @@ -1617,8 +1617,25 @@ Sema::ActOnCXXMemberDeclarator(Scope *S,
> AccessSpecifier AS, Declarator &D,
>
> assert((Name || isInstField) && "No identifier for non-field ?");
>
> - if (isInstField)
> - FieldCollector->Add(cast<FieldDecl>(Member));
> + if (isInstField) {
> + FieldDecl *FD = cast<FieldDecl>(Member);
> + FieldCollector->Add(FD);
> + if (FD->getAccess() == AS_private) {
> + bool hasNoSideEffects = true;
> + if (!FD->getType().isNull()) {
> + if (const CXXRecordDecl *RD =
> + FD->getType().getTypePtrOrNull()->getAsCXXRecordDecl()) {
>
> You shouldn't need getTypePtrOrNull() here. QualType provides an ->
> overload that delegates to Type.
>

Done.


>
> + if
> (!FD->getParent()->getTypeForDecl()->isInstantiationDependentType() &&
> + hasNoSideEffects)
>
> I'm a bit surprised at checking InstantiationDependentType here... Not
> just Dependent... Also some comments as to what this is supposed to handle
> would be good...
>

Done.


> @@ -2063,6 +2080,15 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
> assert((DirectMember || IndirectMember) &&
> "Member must be a FieldDecl or IndirectFieldDecl");
>
> + // FIXME: Allow more initializers not to count as usage, e.g. where all
> + // arguments are primitive types.
> + ValueDecl *VD = dyn_cast<ValueDecl>(Member);
>
> If you know the cast will always succeed, just use "cast" rather than
> "dyn_cast".
>

Done.


> + if (!VD->getType()->isBuiltinType() &&
> + !VD->getType()->isAnyPointerType() &&
>
> Why are these here? We already have constraints on the type of things
> above when we check the Field Decl? Maybe I just don't understand what
> these are doing.
>

I added a comment. Basically, this currently marks fields of a
non-primitive type as used, if a non-trivial constructor is called by an
initializer.

--- a/lib/Sema/SemaTemplateInstantiate.cpp
> +++ b/lib/Sema/SemaTemplateInstantiate.cpp
> @@ -1791,6 +1791,19 @@ Sema::InstantiateClass(SourceLocation
> PointOfInstantiation,
> if (OldField->getInClassInitializer())
> FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
> Field));
> + if (Field->getAccess() == AS_private) {
> + bool hasNoSideEffects = true;
> + if (!Field->getType().isNull()) {
> + if (const CXXRecordDecl *RD =
> +
> Field->getType().getTypePtrOrNull()->getAsCXXRecordDecl()) {
> + hasNoSideEffects = RD->isCompleteDefinition() &&
> + RD->hasTrivialDefaultConstructor() &&
> + RD->hasTrivialDestructor();
> + }
> + }
> + if (hasNoSideEffects)
> + UnusedPrivateFields.insert(Field);
> + }
>
> This is the second copy of this pattern. We should either have template
> instantiation call the not-template Sema routines to do this, or we should
> factor the predicate into a helper somewhere, likely FieldDecl.
>

Done. I implemented a method hasSideEffects that computes whether the
declaration of the field has side effects. It currently only recognizes the
trivial default constructor and the trivial destructor as side-effect-free.
This can be extended in the future. Should the method be moved to
DeclaratorDecl or ValueDecl (it doesn't use anything FieldDecl-specific)?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120206/5ab1c413/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch06022012
Type: application/octet-stream
Size: 14132 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120206/5ab1c413/attachment-0001.obj
Daniel Jasper
2012-03-16 08:50:56 UTC
Permalink
Did/will anyone have time to take a look at this?

On Mon, Feb 6, 2012 at 11:04 AM, Daniel Jasper <djasper at google.com> wrote:

> Comments inline, new version attached.
>
> On Wed, Feb 1, 2012 at 10:52 AM, Chandler Carruth <chandlerc at google.com>wrote:
>
>> Thanks, comments inline... This is sadly a pretty cursory read, sorry,
>> working on other stuff. I think, especially given the "used" concept at
>> play here, Richard or Eli looking at this would be very good. =] Thanks
>> again for working on it.
>>
>> --- a/include/clang/Basic/DiagnosticSemaKinds.td
>> +++ b/include/clang/Basic/DiagnosticSemaKinds.td
>> @@ -134,6 +134,9 @@ def warn_unneeded_member_function : Warning<
>> "member function %0 is not needed and will not be emitted">,
>> InGroup<UnneededMemberFunction>, DefaultIgnore;
>>
>> +def warn_unused_private_field: Warning<"private field %0 is not used">,
>> + InGroup<UnusedPrivateField>, DefaultIgnore;
>> +
>>
>> I wouldn't separate this in its own group w/ vertical whitespace, but
>> rather jam it in with the other unused warning messages.
>>
>
> Done.
>
>
>> --- a/lib/Sema/SemaDeclCXX.cpp
>> +++ b/lib/Sema/SemaDeclCXX.cpp
>> @@ -1617,8 +1617,25 @@ Sema::ActOnCXXMemberDeclarator(Scope *S,
>> AccessSpecifier AS, Declarator &D,
>>
>> assert((Name || isInstField) && "No identifier for non-field ?");
>>
>> - if (isInstField)
>> - FieldCollector->Add(cast<FieldDecl>(Member));
>> + if (isInstField) {
>> + FieldDecl *FD = cast<FieldDecl>(Member);
>> + FieldCollector->Add(FD);
>> + if (FD->getAccess() == AS_private) {
>> + bool hasNoSideEffects = true;
>> + if (!FD->getType().isNull()) {
>> + if (const CXXRecordDecl *RD =
>> + FD->getType().getTypePtrOrNull()->getAsCXXRecordDecl()) {
>>
>> You shouldn't need getTypePtrOrNull() here. QualType provides an ->
>> overload that delegates to Type.
>>
>
> Done.
>
>
>>
>> + if
>> (!FD->getParent()->getTypeForDecl()->isInstantiationDependentType() &&
>> + hasNoSideEffects)
>>
>> I'm a bit surprised at checking InstantiationDependentType here... Not
>> just Dependent... Also some comments as to what this is supposed to handle
>> would be good...
>>
>
> Done.
>
>
>> @@ -2063,6 +2080,15 @@ Sema::BuildMemberInitializer(ValueDecl *Member,
>> assert((DirectMember || IndirectMember) &&
>> "Member must be a FieldDecl or IndirectFieldDecl");
>>
>> + // FIXME: Allow more initializers not to count as usage, e.g. where all
>> + // arguments are primitive types.
>> + ValueDecl *VD = dyn_cast<ValueDecl>(Member);
>>
>> If you know the cast will always succeed, just use "cast" rather than
>> "dyn_cast".
>>
>
> Done.
>
>
>> + if (!VD->getType()->isBuiltinType() &&
>> + !VD->getType()->isAnyPointerType() &&
>>
>> Why are these here? We already have constraints on the type of things
>> above when we check the Field Decl? Maybe I just don't understand what
>> these are doing.
>>
>
> I added a comment. Basically, this currently marks fields of a
> non-primitive type as used, if a non-trivial constructor is called by an
> initializer.
>
> --- a/lib/Sema/SemaTemplateInstantiate.cpp
>> +++ b/lib/Sema/SemaTemplateInstantiate.cpp
>> @@ -1791,6 +1791,19 @@ Sema::InstantiateClass(SourceLocation
>> PointOfInstantiation,
>> if (OldField->getInClassInitializer())
>> FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
>> Field));
>> + if (Field->getAccess() == AS_private) {
>> + bool hasNoSideEffects = true;
>> + if (!Field->getType().isNull()) {
>> + if (const CXXRecordDecl *RD =
>> +
>> Field->getType().getTypePtrOrNull()->getAsCXXRecordDecl()) {
>> + hasNoSideEffects = RD->isCompleteDefinition() &&
>> + RD->hasTrivialDefaultConstructor() &&
>> + RD->hasTrivialDestructor();
>> + }
>> + }
>> + if (hasNoSideEffects)
>> + UnusedPrivateFields.insert(Field);
>> + }
>>
>> This is the second copy of this pattern. We should either have template
>> instantiation call the not-template Sema routines to do this, or we should
>> factor the predicate into a helper somewhere, likely FieldDecl.
>>
>
> Done. I implemented a method hasSideEffects that computes whether the
> declaration of the field has side effects. It currently only recognizes the
> trivial default constructor and the trivial destructor as side-effect-free.
> This can be extended in the future. Should the method be moved to
> DeclaratorDecl or ValueDecl (it doesn't use anything FieldDecl-specific)?
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120316/84078084/attachment.html
Douglas Gregor
2012-03-17 07:30:57 UTC
Permalink
On Feb 6, 2012, at 2:04 AM, Daniel Jasper wrote:

> Comments inline, new version attached.
> [snip]

A few comments?


+ typedef llvm::SmallPtrSet<const NamedDecl*, 16> NamedDeclSetType;
+
+ /// \brief Set containing all declared private fields that are not used.
+ NamedDeclSetType UnusedPrivateFields;

How big does this set tend to get with, say, a typical .cpp file in LLVM or Clang? Big enough that we should be concerned about a performance impact?

+ if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+ if (const Type *T = TSI->getType().getTypePtrOrNull()) {

With a non-NULL TSI, TSI->getType() will always return a non-NULL QualType. You can just collapse this into the next line to get

if (CXXRecordDecl *FriendDecl = TSI->getType()->getAsCXXRecordDecl()) {

+ if (const FunctionDecl *FD =
+ dyn_cast_or_null<FunctionDecl>((*I)->getFriendDecl()))
+ Complete = FD->isDefined();

I don't think the "_or_null" is needed here, because a FriendDecl either befriends a type or a decl.

I note that these three loops:

+ for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
+ E = RD->friend_end();
+ I != E && Complete; ++I) {

+ for (CXXRecordDecl::method_iterator M = RD->method_begin(),
+ E = RD->method_end();
+ M != E && Complete; ++M) {

+ for (record_iterator I(RD->decls_begin()), E(RD->decls_begin());
+ I != E && Complete; ++I) {

All make a complete pass over all of the declarations within each CXXRecordDecl, which is rather inefficient. It would be better to make one pass over decls_begin/decls_end and deal with all of the potential cases.

+bool FieldDecl::hasSideEffects() const {
+ if (!getType().isNull()) {
+ if (const CXXRecordDecl *RD = getType()->getAsCXXRecordDecl()) {
+ return !RD->isCompleteDefinition() ||
+ !RD->hasTrivialDefaultConstructor() ||
+ !RD->hasTrivialDestructor();
+ }
+ }
+ return false;
+}

I don't think that hasSideEffects() belongs on FieldDecl. It can be some kind of Sema utility routine, perhaps, that makes it obvious that we mean "no side effects due to its initialization." Note that there's a C++11 case missing here, for fields with an initializer

struct X {
int i = 7;
};



+ llvm::SmallPtrSet<const CXXRecordDecl*, 16> NotFullyDefined;
+ llvm::SmallPtrSet<const CXXRecordDecl*, 16> FullyDefined;
+ for (NamedDeclSetType::iterator I = UnusedPrivateFields.begin(),
+ E = UnusedPrivateFields.end(); I != E; ++I) {

This will give a non-deterministic ordering of warnings. You probably want to use a SetVector here to keep things in order.

+ const NamedDecl *D = *I;
+ const CXXRecordDecl *RD = cast<const CXXRecordDecl>(D->getDeclContext());
+ if (RD && IsRecordFullyDefined(RD, NotFullyDefined,
+ FullyDefined)) {
+ Diag(D->getLocation(), diag::warn_unused_private_field)
+ << D->getNameAsString();

There's no need for D->getNameAsString(). You can just use D->getDeclName() and the diagnostic system will format it.

--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -1643,8 +1643,17 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,

assert((Name || isInstField) && "No identifier for non-field ?");

- if (isInstField)
- FieldCollector->Add(cast<FieldDecl>(Member));
+ if (isInstField) {
+ FieldDecl *FD = cast<FieldDecl>(Member);
+ FieldCollector->Add(FD);
+ // Remember all private FieldDecls that have no side effects and are not
+ // part of a dependent type declaration.
+ if (FD->getAccess() == AS_private &&
+ !FD->getParent()->getTypeForDecl()->isDependentType() &&
+ !FD->hasSideEffects())
+ UnusedPrivateFields.insert(FD);
+ }
+

Shouldn't you also check that FD actually has a name?

+ // Mark FieldDecl as being used if it is a non-primitive type and the
+ // initializer does not call the default constructor (which is trivial
+ // for all entries in UnusedPrivateFields).
+ // FIXME: Make this smarter once more side effect-free types can be
+ // determined.
+ ValueDecl *VD = cast<ValueDecl>(Member);
+ if (!VD->getType()->isBuiltinType() &&
+ !VD->getType()->isAnyPointerType() &&
+ Args.begin() != Args.end()) {
+ UnusedPrivateFields.erase(VD);
+ }
+

I find the check for pointer + built-in types odd. Isn't there a more obvious, existing type predicate that checks what you want?

diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index dde7826..f30a521 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1809,6 +1809,9 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
if (OldField->getInClassInitializer())
FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
Field));
+ // Remember all private FieldDecls that have no side effects.
+ if (Field->getAccess() == AS_private && !Field->hasSideEffects())
+ UnusedPrivateFields.insert(Field);

Templates are an interesting case, because it's actually quite unlikely that we'll ever see the definition of all of the member functions of a class template, since no translation unit is likely to instantiate all of them. Perhaps we shouldn't bother even trying to make this warning work for templates?

- Doug
Manuel Klimek
2012-03-17 09:05:37 UTC
Permalink
On Sat, Mar 17, 2012 at 8:30 AM, Douglas Gregor <dgregor at apple.com> wrote:
> Templates are an interesting case, because it's actually quite unlikely that we'll ever see the definition of all of the member functions of a class template, since no translation unit is likely to instantiate all of them. Perhaps we shouldn't bother even trying to make this warning work for templates?

That is an interesting expectation. And if people only used templates
for base libraries, I would agree. In the large code base I happen to
work in there are many templates for which I would expect all
functions to be instantiated in a translation unit. Templates used for
performance reasons, templates used for "strategy pattern" styles etc.

I agree with the idea of not trying to make this warning work for them
unless we find a way to handle specializations, though.

Cheers,
/Manuel
Daniel Jasper
2012-05-09 06:37:19 UTC
Permalink
Finally, a new version of this patch.

+ typedef llvm::SmallPtrSet<const NamedDecl*, 16> NamedDeclSetType;
> +
> + /// \brief Set containing all declared private fields that are not used.
> + NamedDeclSetType UnusedPrivateFields;
>
> How big does this set tend to get with, say, a typical .cpp file in LLVM
> or Clang? Big enough that we should be concerned about a performance impact?
>

While compiling files from Sema, I got the following statistics: The
maximum size of this structure was 560, with 1600 total inserts and 14000
attempted deletes. This should not be a performance impact, but I don't
really know with respect to LLVM/Clang.


> + if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
> + if (const Type *T = TSI->getType().getTypePtrOrNull()) {
>
> With a non-NULL TSI, TSI->getType() will always return a non-NULL
> QualType. You can just collapse this into the next line to get
>
> if (CXXRecordDecl *FriendDecl =
> TSI->getType()->getAsCXXRecordDecl()) {
>

Done.


> + if (const FunctionDecl *FD =
> + dyn_cast_or_null<FunctionDecl>((*I)->getFriendDecl()))
> + Complete = FD->isDefined();
>
> I don't think the "_or_null" is needed here, because a FriendDecl either
> befriends a type or a decl.
>

Done.


> I note that these three loops:
>
> + for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
> + E = RD->friend_end();
> + I != E && Complete; ++I) {
>
> + for (CXXRecordDecl::method_iterator M = RD->method_begin(),
> + E = RD->method_end();
> + M != E && Complete; ++M) {
>
> + for (record_iterator I(RD->decls_begin()), E(RD->decls_begin());
> + I != E && Complete; ++I) {
>
> All make a complete pass over all of the declarations within each
> CXXRecordDecl, which is rather inefficient. It would be better to make one
> pass over decls_begin/decls_end and deal with all of the potential cases.
>

Done.

+bool FieldDecl::hasSideEffects() const {
> + if (!getType().isNull()) {
> + if (const CXXRecordDecl *RD = getType()->getAsCXXRecordDecl()) {
> + return !RD->isCompleteDefinition() ||
> + !RD->hasTrivialDefaultConstructor() ||
> + !RD->hasTrivialDestructor();
> + }
> + }
> + return false;
> +}
>
> I don't think that hasSideEffects() belongs on FieldDecl. It can be some
> kind of Sema utility routine, perhaps, that makes it obvious that we mean
> "no side effects due to its initialization."


I have now made it a local function in SemaDeclCXX.cpp and named it more
appropriately. It is not reused for templates anymore (see below).


> Note that there's a C++11 case missing here, for fields with an initializer
>
> struct X {
> int i = 7;
> };
>
>
Added test and implementation for this.


> + llvm::SmallPtrSet<const CXXRecordDecl*, 16> NotFullyDefined;
> + llvm::SmallPtrSet<const CXXRecordDecl*, 16> FullyDefined;
> + for (NamedDeclSetType::iterator I = UnusedPrivateFields.begin(),
> + E = UnusedPrivateFields.end(); I != E;
> ++I) {
>
> This will give a non-deterministic ordering of warnings. You probably want
> to use a SetVector here to keep things in order.
>

Done.


> + const NamedDecl *D = *I;
> + const CXXRecordDecl *RD = cast<const
> CXXRecordDecl>(D->getDeclContext());
> + if (RD && IsRecordFullyDefined(RD, NotFullyDefined,
> + FullyDefined)) {
> + Diag(D->getLocation(), diag::warn_unused_private_field)
> + << D->getNameAsString();
>
> There's no need for D->getNameAsString(). You can just use
> D->getDeclName() and the diagnostic system will format it.
>

Done.


> --- a/lib/Sema/SemaDeclCXX.cpp
> +++ b/lib/Sema/SemaDeclCXX.cpp
> @@ -1643,8 +1643,17 @@ Sema::ActOnCXXMemberDeclarator(Scope *S,
> AccessSpecifier AS, Declarator &D,
>
> assert((Name || isInstField) && "No identifier for non-field ?");
>
> - if (isInstField)
> - FieldCollector->Add(cast<FieldDecl>(Member));
> + if (isInstField) {
> + FieldDecl *FD = cast<FieldDecl>(Member);
> + FieldCollector->Add(FD);
> + // Remember all private FieldDecls that have no side effects and are
> not
> + // part of a dependent type declaration.
> + if (FD->getAccess() == AS_private &&
> + !FD->getParent()->getTypeForDecl()->isDependentType() &&
> + !FD->hasSideEffects())
> + UnusedPrivateFields.insert(FD);
> + }
> +
>
> Shouldn't you also check that FD actually has a name?
>

Yes. Done.


> + // Mark FieldDecl as being used if it is a non-primitive type and the
> + // initializer does not call the default constructor (which is trivial
> + // for all entries in UnusedPrivateFields).
> + // FIXME: Make this smarter once more side effect-free types can be
> + // determined.
> + ValueDecl *VD = cast<ValueDecl>(Member);
> + if (!VD->getType()->isBuiltinType() &&
> + !VD->getType()->isAnyPointerType() &&
> + Args.begin() != Args.end()) {
> + UnusedPrivateFields.erase(VD);
> + }
> +
>
> I find the check for pointer + built-in types odd. Isn't there a more
> obvious, existing type predicate that checks what you want?
>

Yes. I actually want to check that it is not a record-type. Done. Also, I
now check that the initializer itself does not have side-effects.


> diff --git a/lib/Sema/SemaTemplateInstantiate.cpp
> b/lib/Sema/SemaTemplateInstantiate.cpp
> index dde7826..f30a521 100644
> --- a/lib/Sema/SemaTemplateInstantiate.cpp
> +++ b/lib/Sema/SemaTemplateInstantiate.cpp
> @@ -1809,6 +1809,9 @@ Sema::InstantiateClass(SourceLocation
> PointOfInstantiation,
> if (OldField->getInClassInitializer())
> FieldsWithMemberInitializers.push_back(std::make_pair(OldField,
> Field));
> + // Remember all private FieldDecls that have no side effects.
> + if (Field->getAccess() == AS_private && !Field->hasSideEffects())
> + UnusedPrivateFields.insert(Field);
>
> Templates are an interesting case, because it's actually quite unlikely
> that we'll ever see the definition of all of the member functions of a
> class template, since no translation unit is likely to instantiate all of
> them. Perhaps we shouldn't bother even trying to make this warning work for
> templates?
>

Probably not as this cannot be solved locally for a TU. Another TU might
declare an explicit specialization using the otherwise unused private
member. Removed.

Kind regards,
Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120509/e6d9cc56/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 13061 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120509/e6d9cc56/attachment-0001.obj
Jordy Rose
2012-05-09 22:38:57 UTC
Permalink
I didn't put a huge amount of effort into reviewing this, but here are a couple things I noticed...

- How about putting a "Used" bit in FieldDecl, rather than keeping a separate SetVector? It's not like iterating over all fields is that much of a cost later.

- IsRecordFullyDefined should probably use SmallPtrSetImpl, to avoid specifying a size in the function prototype. (Not that it /really/ matters, but...)

- You have an additional "NumArgs > 0" when marking a field used, and the for-loop should probably be in an else-case.

- Don't use isa<> followed by dyn_cast<>. Just put the dyn_cast<> in the condition.

Jordy
Daniel Jasper
2012-05-09 23:24:52 UTC
Permalink
>
> I didn't put a huge amount of effort into reviewing this, but here are a
> couple things I noticed...
>
> - How about putting a "Used" bit in FieldDecl, rather than keeping a
> separate SetVector? It's not like iterating over all fields is that much of
> a cost later.
>

I like the idea. Anyone against it?

- IsRecordFullyDefined should probably use SmallPtrSetImpl, to avoid
> specifying a size in the function prototype. (Not that it /really/ matters,
> but...)
>

But all the methods in SmallPtrSetImpl are protected!?

- You have an additional "NumArgs > 0" when marking a field used, and the
> for-loop should probably be in an else-case.
>

Changed.


> - Don't use isa<> followed by dyn_cast<>. Just put the dyn_cast<> in the
> condition.


Done.


>
> Jordy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120509/59f1f248/attachment.html
Jordan Rose
2012-05-10 00:41:35 UTC
Permalink
On May 9, 2012, at 19:24, Daniel Jasper wrote:

>> - IsRecordFullyDefined should probably use SmallPtrSetImpl, to avoid specifying a size in the function prototype. (Not that it /really/ matters, but...)
>>
> But all the methods in SmallPtrSetImpl are protected!?

Oops, sorry. I didn't actually check the docs; I just assumed it was like SmallVector and SmallVectorImpl. I suppose a typedef, then, is the next best thing, to avoid having to specify the count in two different places.
Daniel Jasper
2012-05-10 22:12:02 UTC
Permalink
Ok, I now reuse Sema::RecordDeclSetTy.

As for putting a bit into FieldDecl as opposed to the UnusedPrivateField
SetVector: It seems like FieldDecl is carefully crafted for size. Just
adding another bit for this use case doesn't seem right.

Please find the new version attached.

On Wed, May 9, 2012 at 5:41 PM, Jordan Rose <jediknil at belkadan.com> wrote:

>
> On May 9, 2012, at 19:24, Daniel Jasper wrote:
>
> >> - IsRecordFullyDefined should probably use SmallPtrSetImpl, to avoid
> specifying a size in the function prototype. (Not that it /really/ matters,
> but...)
> >>
> > But all the methods in SmallPtrSetImpl are protected!?
>
> Oops, sorry. I didn't actually check the docs; I just assumed it was like
> SmallVector and SmallVectorImpl. I suppose a typedef, then, is the next
> best thing, to avoid having to specify the count in two different places.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120510/ebd1e4bb/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 12795 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120510/ebd1e4bb/attachment-0001.obj
Jordan Rose
2012-05-10 22:37:42 UTC
Permalink
On May 10, 2012, at 18:12, Daniel Jasper wrote:

> Ok, I now reuse Sema::RecordDeclSetTy.
>
> As for putting a bit into FieldDecl as opposed to the UnusedPrivateField SetVector: It seems like FieldDecl is carefully crafted for size. Just adding another bit for this use case doesn't seem right.
>
> Please find the new version attached.

CachedFieldIndex is just the index of the field in the record; there's no way that'll reach 2^31 anyway. (For comparison, C++11 [implimits] suggests a minimum of 4096 members.)

Also, it looks like Decl already has an isUsed() predicate. Are we already marking FieldDecls this way?
Daniel Jasper
2012-05-22 22:17:48 UTC
Permalink
On Fri, May 11, 2012 at 12:37 AM, Jordan Rose <jediknil at belkadan.com> wrote:

>
> On May 10, 2012, at 18:12, Daniel Jasper wrote:
>
> > Ok, I now reuse Sema::RecordDeclSetTy.
> >
> > As for putting a bit into FieldDecl as opposed to the UnusedPrivateField
> SetVector: It seems like FieldDecl is carefully crafted for size. Just
> adding another bit for this use case doesn't seem right.
> >
> > Please find the new version attached.
>
> CachedFieldIndex is just the index of the field in the record; there's no
> way that'll reach 2^31 anyway. (For comparison, C++11 [implimits] suggests
> a minimum of 4096 members.)
>
> Also, it looks like Decl already has an isUsed() predicate. Are we already
> marking FieldDecls this way?


I have been looking into this some more. Marking the fields as used works
fine, but I didn't find an easy way to iterate over all of the FieldDecls
in a translation unit at the end. Thus, I suggest to stick with the
previous version of my patch. Should also neither be a big performance nor
memory impact. What do you think?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120523/458d2e19/attachment.html
Jordy Rose
2012-05-27 00:31:26 UTC
Permalink
I guess I'm okay with the extra memory usage. A few last comments:

+bool InitializationHasSideEffects(const FieldDecl& FD) {

Please make this file-scoped (static).

+ // Mark FieldDecl as being used if it is a non-primitive type and the
+ // initializer does not call the default constructor (which is trivial
+ // for all entries in UnusedPrivateFields).
+ // FIXME: Make this smarter once more side effect-free types can be
+ // determined.
+ if (NumArgs > 0) {
+ ValueDecl *VD = cast<ValueDecl>(Member);
+ if (VD->getType()->isRecordType()) {
+ UnusedPrivateFields.remove(VD);
+ } else {
+ for (unsigned i = 0; i < NumArgs; ++i) {
+ if (Args[i]->HasSideEffects(Context)) {
+ UnusedPrivateFields.remove(VD);
+ break;
+ }
+ }
+ }
+ }

'Member' is already typed as a ValueDecl. Also, even if one of the constructor arguments to a primitive type has side effects, shouldn't the field still count as unused if it's never referenced again?

+ } else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) {
+ // Check if nested classes are complete.
+ if (R->hasDefinition() &&
+ R->getCanonicalDecl() != RD->getCanonicalDecl())
+ Complete = IsRecordFullyDefined(R, NotFullyDefined, FullyDefined);
+ }

This clause in IsRecordFullyDefined seems a little fishy to me. If a nested class does NOT have a definition, the enclosing class is still complete? If it has a definition but the canonical decl is the current class -- okay, I get that, if the class has a reference to itself.


+ const CXXRecordDecl *RD = cast<const CXXRecordDecl>(D->getDeclContext());
+ if (RD && IsRecordFullyDefined(RD, NotFullyDefined, FullyDefined)) {

This cast should be dyn_cast, and the 'const' is unnecessary.

Finally, even though there's not a big hit traversing the fields at the end, we probably still shouldn't pay for this if the diagnostic is off. Can you check for that before inserting into UnusedPrivateFields, or even doing all the tests?

(Oh, and how does this do with private static members? I think you'd be missing a case there. If you want to just exclude them for now, though, that's okay.)

Sorry for the review delay. Do you have commit access?

Jordy


On May 22, 2012, at 18:17, Daniel Jasper wrote:

>
>
> On Fri, May 11, 2012 at 12:37 AM, Jordan Rose <jediknil at belkadan.com> wrote:
>
> On May 10, 2012, at 18:12, Daniel Jasper wrote:
>
> > Ok, I now reuse Sema::RecordDeclSetTy.
> >
> > As for putting a bit into FieldDecl as opposed to the UnusedPrivateField SetVector: It seems like FieldDecl is carefully crafted for size. Just adding another bit for this use case doesn't seem right.
> >
> > Please find the new version attached.
>
> CachedFieldIndex is just the index of the field in the record; there's no way that'll reach 2^31 anyway. (For comparison, C++11 [implimits] suggests a minimum of 4096 members.)
>
> Also, it looks like Decl already has an isUsed() predicate. Are we already marking FieldDecls this way?
>
> I have been looking into this some more. Marking the fields as used works fine, but I didn't find an easy way to iterate over all of the FieldDecls in a translation unit at the end. Thus, I suggest to stick with the previous version of my patch. Should also neither be a big performance nor memory impact. What do you think?
Daniel Jasper
2012-05-30 05:43:03 UTC
Permalink
On Sun, May 27, 2012 at 2:31 AM, Jordy Rose <jediknil at belkadan.com> wrote:

> I guess I'm okay with the extra memory usage. A few last comments:
>
> +bool InitializationHasSideEffects(const FieldDecl& FD) {
>
> Please make this file-scoped (static).


Done.


> + // Mark FieldDecl as being used if it is a non-primitive type and the
> + // initializer does not call the default constructor (which is trivial
> + // for all entries in UnusedPrivateFields).
> + // FIXME: Make this smarter once more side effect-free types can be
> + // determined.
> + if (NumArgs > 0) {
> + ValueDecl *VD = cast<ValueDecl>(Member);
> + if (VD->getType()->isRecordType()) {
> + UnusedPrivateFields.remove(VD);
> + } else {
> + for (unsigned i = 0; i < NumArgs; ++i) {
> + if (Args[i]->HasSideEffects(Context)) {
> + UnusedPrivateFields.remove(VD);
> + break;
> + }
> + }
> + }
> + }
>
> 'Member' is already typed as a ValueDecl. Also, even if one of the
> constructor arguments to a primitive type has side effects, shouldn't the
> field still count as unused if it's never referenced again?
>

Removed VD.

I personally agree, but I have also heard that some people do initialize
fields with methods that have side effects and use the value only with
certain #ifdefs (e.g. for debugging). In these cases, the warning would be
annoying. Thus, in order to reduce false positives as far as possible in
this first version, I declared this as a "use". I am happy to change it,
though.

+ } else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) {
> + // Check if nested classes are complete.
> + if (R->hasDefinition() &&
> + R->getCanonicalDecl() != RD->getCanonicalDecl())
> + Complete = IsRecordFullyDefined(R, NotFullyDefined,
> FullyDefined);
> + }
>
> This clause in IsRecordFullyDefined seems a little fishy to me. If a
> nested class does NOT have a definition, the enclosing class is still
> complete? If it has a definition but the canonical decl is the current
> class -- okay, I get that, if the class has a reference to itself.


Yes, you are right. I actually wanted to not check the injected class name
but I messed it up (and didn't find isInjectedClassname). Also, it did not
correctly handle cases where the inner class was defined outside of the
outer class. Fixed and tests added.


> + const CXXRecordDecl *RD = cast<const
> CXXRecordDecl>(D->getDeclContext());
> + if (RD && IsRecordFullyDefined(RD, NotFullyDefined, FullyDefined)) {
>
> This cast should be dyn_cast, and the 'const' is unnecessary.
>

Done.


> Finally, even though there's not a big hit traversing the fields at the
> end, we probably still shouldn't pay for this if the diagnostic is off. Can
> you check for that before inserting into UnusedPrivateFields, or even doing
> all the tests?
>

I am now checking whether the diagnostic is enabled before inserting into
the set and before doing the checks at the end of the translation unit.
Checking before removing from the (then empty) set probably does not give
any benefit. I am not sure whether the check on the initializer arguments
is expensive (especially the call to HasSideEffects). What do you think?


> (Oh, and how does this do with private static members? I think you'd be
> missing a case there. If you want to just exclude them for now, though,
> that's okay.)
>

I added a static private member to the test. The warning does not fire at
the moment and I think that is ok for the first version.


> Sorry for the review delay. Do you have commit access?


Yes, as of today :-). Please find a new version of the patch attached.

Daniel


>
> Jordy
>
>
> On May 22, 2012, at 18:17, Daniel Jasper wrote:
>
> >
> >
> > On Fri, May 11, 2012 at 12:37 AM, Jordan Rose <jediknil at belkadan.com>
> wrote:
> >
> > On May 10, 2012, at 18:12, Daniel Jasper wrote:
> >
> > > Ok, I now reuse Sema::RecordDeclSetTy.
> > >
> > > As for putting a bit into FieldDecl as opposed to the
> UnusedPrivateField SetVector: It seems like FieldDecl is carefully crafted
> for size. Just adding another bit for this use case doesn't seem right.
> > >
> > > Please find the new version attached.
> >
> > CachedFieldIndex is just the index of the field in the record; there's
> no way that'll reach 2^31 anyway. (For comparison, C++11 [implimits]
> suggests a minimum of 4096 members.)
> >
> > Also, it looks like Decl already has an isUsed() predicate. Are we
> already marking FieldDecls this way?
> >
> > I have been looking into this some more. Marking the fields as used
> works fine, but I didn't find an easy way to iterate over all of the
> FieldDecls in a translation unit at the end. Thus, I suggest to stick with
> the previous version of my patch. Should also neither be a big performance
> nor memory impact. What do you think?
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120530/05dfa2b3/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clangpatch
Type: application/octet-stream
Size: 13485 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120530/05dfa2b3/attachment-0001.obj
Jordan Rose
2012-06-01 23:54:24 UTC
Permalink
First off, what do people think about turning this on by default, or at least making it part of -Wunused? Daniel's being conservative by leaving it off, but you know what people say about off-by-default warnings...

---

As for the patch?sorry, one last problem.

+ // Mark as fully defined for now to stop recursion in case of mutual
+ // friends.
+ FullyDefined.insert(RD);

I thought about this some more, and it seems like this will cause incorrect warnings:

class B;
class A {
int x;
public:
friend class B;
doSomethingWithX();
}

class B {
int y;
public:
friend class A;
}

When looking through A's decls (because of A::x), we'll come across "friend class B". We'll look through B's decls and find "friend class A". A has already been marked complete, so we mark B complete. Then we go back to A and find "doSomethingWithX()", and A is now (correctly and finally) marked incomplete. When we get to B::y, though, we're in trouble, because B has already been marked as complete via A!

The usual thing to do here is add an "in progress" set, but I'm still not sure exactly how to use that for warnings. I think the best thing to do (and possibly the most correct thing to do) is to simply give up when we see mutual friends declarations -- that is, tentatively mark classes as not fully defined instead of fully defined. That gives us a conservative set of warnings, even if we miss a few valid cases.


> Finally, even though there's not a big hit traversing the fields at the end, we probably still shouldn't pay for this if the diagnostic is off. Can you check for that before inserting into UnusedPrivateFields, or even doing all the tests?
>
> I am now checking whether the diagnostic is enabled before inserting into the set and before doing the checks at the end of the translation unit. Checking before removing from the (then empty) set probably does not give any benefit. I am not sure whether the check on the initializer arguments is expensive (especially the call to HasSideEffects). What do you think?

Let's leave it as is. Looking up a diagnostic isn't /that/ cheap, and most initializers are something simple like a VarDecl, a constant, or a FunctionCall.

I think once you've fixed the friend problem you can go ahead and commit. Thanks for working on this!
Jordan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120601/144e73ee/attachment.html
Daniel Jasper
2012-06-04 08:43:24 UTC
Permalink
>
> First off, what do people think about turning this on by default, or at
> least making it part of -Wunused? Daniel's being conservative by leaving it
> off, but you know what people say about off-by-default warnings...
>

It would be my intention to turn it on by default (not sure whether by
default, as part of Wunused or just as default for LLVM compilations), but
I though we should clean up LLVM's codebase before doing so. But I am not
sure what the right approach is here.

---
>
> As for the patch?sorry, one last problem.
>
> + // Mark as fully defined for now to stop recursion in case of mutual
> + // friends.
> + FullyDefined.insert(RD);
>
> I thought about this some more, and it seems like this will cause
> incorrect warnings:
>
> class B;
> class A {
> int x;
> public:
> friend class B;
> doSomethingWithX();
> }
>
> class B {
> int y;
> public:
> friend class A;
> }
>
> When looking through A's decls (because of A::x), we'll come across
> "friend class B". We'll look through B's decls and find "friend class A". A
> has already been marked complete, so we mark B complete. Then we go back to
> A and find "doSomethingWithX()", and A is now (correctly and finally)
> marked incomplete. When we get to B::y, though, we're in trouble, because B
> has already been marked as complete via A!
>
> The usual thing to do here is add an "in progress" set, but I'm still not
> sure exactly how to use that for warnings. I think the best thing to do
> (and possibly the most correct thing to do) is to simply give up when we
> see mutual friends declarations -- that is, tentatively mark classes as not
> fully defined instead of fully defined. That gives us a conservative set of
> warnings, even if we miss a few valid cases.
>

Thanks for coming up with this!! I propose a different solution which
utilizes the fact that friend relationships are not transitive. Thus, if a
class has a friend, it only needs to check whether all methods and nested
classes of the friend are defined but does not need to consider the
friend's friends. I have changed the implementation and added corresponding
test cases (see attached new patch).

> Finally, even though there's not a big hit traversing the fields at the
>> end, we probably still shouldn't pay for this if the diagnostic is off. Can
>> you check for that before inserting into UnusedPrivateFields, or even doing
>> all the tests?
>>
>
> I am now checking whether the diagnostic is enabled before inserting into
> the set and before doing the checks at the end of the translation unit.
> Checking before removing from the (then empty) set probably does not give
> any benefit. I am not sure whether the check on the initializer arguments
> is expensive (especially the call to HasSideEffects). What do you think?
>
>
> Let's leave it as is. Looking up a diagnostic isn't /that/ cheap, and most
> initializers are something simple like a VarDecl, a constant, or a
> FunctionCall.
>

Ok.

I think once you've fixed the friend problem you can go ahead and commit.
> Thanks for working on this!
> Jordan
>

Could you take one last look as the implementation of IsRecordFullyDefined
has changed substantially?

Thank you!
Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120604/6e8d2c27/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: unused-private-field.patch
Type: application/octet-stream
Size: 15108 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120604/6e8d2c27/attachment-0001.obj
Jordan Rose
2012-06-04 17:22:40 UTC
Permalink
On Jun 4, 2012, at 1:43 , Daniel Jasper <djasper at google.com> wrote:
> Thanks for coming up with this!! I propose a different solution which utilizes the fact that friend relationships are not transitive. Thus, if a class has a friend, it only needs to check whether all methods and nested classes of the friend are defined but does not need to consider the friend's friends.

Oh, of course! Good catch.

> Could you take one last look as the implementation of IsRecordFullyDefined has changed substantially?

Thank you for being careful. :-) Only small comments this time, I think:

- Please attach the & to the variable name for references, per LLVM style conventions (same as pointers).

- You've got an isa followed by dyn_cast for CXXMethodDecl; please change to just dyn_cast (like CXXRecordDecl).

- DenseMap has an operator[], which is a little easier to read than insert+make_pair.

- This is very nitpicky, but the style guide says to put all comments on their own line, and to end them with periods (full stops). So "// This is a template friend, give up" should be reformatted as such.

Also, it took me a while to convince myself that CXXRecordDecls coming out of getFriendDecl() are also problematic, but what convinced me is that if they don't have a TypeSourceInfo, we can't possibly see their definition. It does seem like it is possible to have CXXRecordDecls come out of getFriendDecl(), though, and that might be worth commenting.

I'm tempted to say that passing around a pair of maps to two different methods is worth a private class, but I guess it's fine the way it is.

I think that's it. Good work!

Jordan
Daniel Jasper
2012-06-04 18:14:43 UTC
Permalink
On Mon, Jun 4, 2012 at 7:22 PM, Jordan Rose <jordan_rose at apple.com> wrote:

>
> On Jun 4, 2012, at 1:43 , Daniel Jasper <djasper at google.com> wrote:
> > Thanks for coming up with this!! I propose a different solution which
> utilizes the fact that friend relationships are not transitive. Thus, if a
> class has a friend, it only needs to check whether all methods and nested
> classes of the friend are defined but does not need to consider the
> friend's friends.
>
> Oh, of course! Good catch.
>
> > Could you take one last look as the implementation of
> IsRecordFullyDefined has changed substantially?
>
> Thank you for being careful. :-) Only small comments this time, I think:
>
> - Please attach the & to the variable name for references, per LLVM style
> conventions (same as pointers).
>
> - You've got an isa followed by dyn_cast for CXXMethodDecl; please change
> to just dyn_cast (like CXXRecordDecl).
>
> - DenseMap has an operator[], which is a little easier to read than
> insert+make_pair.
>
> - This is very nitpicky, but the style guide says to put all comments on
> their own line, and to end them with periods (full stops). So "// This is a
> template friend, give up" should be reformatted as such.
>
> Also, it took me a while to convince myself that CXXRecordDecls coming out
> of getFriendDecl() are also problematic, but what convinced me is that if
> they don't have a TypeSourceInfo, we can't possibly see their definition.
> It does seem like it is possible to have CXXRecordDecls come out of
> getFriendDecl(), though, and that might be worth commenting.
>

I have never managed to get a CXXRecordDecl out of getFriendDecl(). For
friend classes getFriendDecl() always seems to return 0. How do I create
such a case?

Also, it uses FriendUnion as internal data type (
http://clang.llvm.org/doxygen/DeclFriend_8h_source.html#l00041), which
hints at the result either being a NamedDecl for friend functions or a
TypeSourceInfo for friend classes. I have added comments and an else-branch
to the "if(..->getAsCXXRecordDecl())" so that if we don't understand the
FriendDecl, we mark the class as incomplete.


> I'm tempted to say that passing around a pair of maps to two different
> methods is worth a private class, but I guess it's fine the way it is.
>
> I think that's it. Good work!
>
> Jordan
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120604/0b88626f/attachment-0001.html
Richard Smith
2012-06-04 18:33:09 UTC
Permalink
I have a few comments in addition to Jordy's:

* Since pure virtual functions can be defined, the diagnostic as it
stands might have false positives. In general, I think that's
unavoidable and acceptable, but in the specific case of a pure virtual
destructor, I think we should require that the TU contains a
definition: such a destructor must actually be defined for the class
to be useful as a base class, and may well be the only member of the
class which has an out-of-line definition, and might reasonably
contain the only use (other than a side-effect-free initialization) of
a private member.

* The check for isTemplateDecl() in IsRecordFullyDefined is redundant,
since CXXRecordDecls are never TemplateDecls.

* It looks like in-class initializers and (in-constructor) member
initializers are handled slightly differently (particularly for
non-class types where the initializer has side-effects). It would seem
more consistent with the language semantics if in-class initializers
were treated exactly like member initializers, and only considered if
they're actually used by a constructor. [... it also strikes me that a
warning for an unused in-class initializer could be useful :) ...]

* I would like to see some test cases for unions, and for anonymous
struct/union members, in particular for cases like this:

class S {
// ... no use of Aligner ...
private:
union {
void *Aligner;
unsigned char Data[8];
};
};

(See clang::DependentFunctionTempateSpecializationInfo for a
real-world example of this.) It looks like we won't diagnose that case
(because Aligner is technically public, despite being effectively a
private member of S). But I suspect we will diagnose this (and
probably shouldn't):

union S {
// ... no use of Aligner ...
private:
void *Aligner;
unsigned char Data[8];
};

Perhaps we should conservatively disable the diagnostic for union members?

Thanks!
Richard

On Mon, Jun 4, 2012 at 10:22 AM, Jordan Rose <jordan_rose at apple.com> wrote:
>
> On Jun 4, 2012, at 1:43 , Daniel Jasper <djasper at google.com> wrote:
>> Thanks for coming up with this!! I propose a different solution which utilizes the fact that friend relationships are not transitive. Thus, if a class has a friend, it only needs to check whether all methods and nested classes of the friend are defined but does not need to consider the friend's friends.
>
> Oh, of course! Good catch.
>
>> Could you take one last look as the implementation of IsRecordFullyDefined has changed substantially?
>
> Thank you for being careful. :-) Only small comments this time, I think:
>
> - Please attach the & to the variable name for references, per LLVM style conventions (same as pointers).
>
> - You've got an isa followed by dyn_cast for CXXMethodDecl; please change to just dyn_cast (like CXXRecordDecl).
>
> - DenseMap has an operator[], which is a little easier to read than insert+make_pair.
>
> - This is very nitpicky, but the style guide says to put all comments on their own line, and to end them with periods (full stops). So "// This is a template friend, give up" should be reformatted as such.
>
> Also, it took me a while to convince myself that CXXRecordDecls coming out of getFriendDecl() are also problematic, but what convinced me is that if they don't have a TypeSourceInfo, we can't possibly see their definition. It does seem like it is possible to have CXXRecordDecls come out of getFriendDecl(), though, and that might be worth commenting.
>
> I'm tempted to say that passing around a pair of maps to two different methods is worth a private class, but I guess it's fine the way it is.
>
> I think that's it. Good work!
>
> Jordan
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
Daniel Jasper
2012-06-04 19:36:31 UTC
Permalink
On Mon, Jun 4, 2012 at 8:33 PM, Richard Smith <richard at metafoo.co.uk> wrote:

> I have a few comments in addition to Jordy's:
>
> * Since pure virtual functions can be defined, the diagnostic as it
> stands might have false positives. In general, I think that's
> unavoidable and acceptable, but in the specific case of a pure virtual
> destructor, I think we should require that the TU contains a
> definition: such a destructor must actually be defined for the class
> to be useful as a base class, and may well be the only member of the
> class which has an out-of-line definition, and might reasonably
> contain the only use (other than a side-effect-free initialization) of
> a private member.
>

Do you mean that we should require that as part of this warning (i.e. mark
those classes as incomplete) or do you mean we should create a separate
warning for that? Also, do you think such a destructor might actually have
side-effects based on the otherwise unused fields. I somewhat doubt that as
we are only looking at pointers or primitive fields.. We might have
something like an owning pointer, but there the destructor will never be
the only "use".


> * The check for isTemplateDecl() in IsRecordFullyDefined is redundant,
> since CXXRecordDecls are never TemplateDecls.


Removed.


> * It looks like in-class initializers and (in-constructor) member
> initializers are handled slightly differently (particularly for
> non-class types where the initializer has side-effects). It would seem
> more consistent with the language semantics if in-class initializers
> were treated exactly like member initializers, and only considered if
> they're actually used by a constructor. [... it also strikes me that a
> warning for an unused in-class initializer could be useful :) ...]
>

Just to clarify: An in-class initializer is only used, if at least one
constructor does not have a corresponding member initializer?

* I would like to see some test cases for unions, and for anonymous
> struct/union members, in particular for cases like this:
>
> class S {
> // ... no use of Aligner ...
> private:
> union {
> void *Aligner;
> unsigned char Data[8];
> };
> };
>

I'd like to get the first version in. Thus I'd like to add this test case
together with a FIXME. The correct solution for this (and especially for
anonymous structs) would be to look at the corresponding DeclContext, right?


> (See clang::DependentFunctionTempateSpecializationInfo for a
> real-world example of this.) It looks like we won't diagnose that case
> (because Aligner is technically public, despite being effectively a
> private member of S). But I suspect we will diagnose this (and
> probably shouldn't):
>
> union S {
> // ... no use of Aligner ...
> private:
> void *Aligner;
> unsigned char Data[8];
> };
>
> Perhaps we should conservatively disable the diagnostic for union members?
>

Why should we not warn in this case? I think in general we might want to
exempt char-arrays as they are commonly use for alignment, but other than
that?

Daniel


> Thanks!
> Richard
>
> On Mon, Jun 4, 2012 at 10:22 AM, Jordan Rose <jordan_rose at apple.com>
> wrote:
> >
> > On Jun 4, 2012, at 1:43 , Daniel Jasper <djasper at google.com> wrote:
> >> Thanks for coming up with this!! I propose a different solution which
> utilizes the fact that friend relationships are not transitive. Thus, if a
> class has a friend, it only needs to check whether all methods and nested
> classes of the friend are defined but does not need to consider the
> friend's friends.
> >
> > Oh, of course! Good catch.
> >
> >> Could you take one last look as the implementation of
> IsRecordFullyDefined has changed substantially?
> >
> > Thank you for being careful. :-) Only small comments this time, I think:
> >
> > - Please attach the & to the variable name for references, per LLVM
> style conventions (same as pointers).
> >
> > - You've got an isa followed by dyn_cast for CXXMethodDecl; please
> change to just dyn_cast (like CXXRecordDecl).
> >
> > - DenseMap has an operator[], which is a little easier to read than
> insert+make_pair.
> >
> > - This is very nitpicky, but the style guide says to put all comments on
> their own line, and to end them with periods (full stops). So "// This is a
> template friend, give up" should be reformatted as such.
> >
> > Also, it took me a while to convince myself that CXXRecordDecls coming
> out of getFriendDecl() are also problematic, but what convinced me is that
> if they don't have a TypeSourceInfo, we can't possibly see their
> definition. It does seem like it is possible to have CXXRecordDecls come
> out of getFriendDecl(), though, and that might be worth commenting.
> >
> > I'm tempted to say that passing around a pair of maps to two different
> methods is worth a private class, but I guess it's fine the way it is.
> >
> > I think that's it. Good work!
> >
> > Jordan
> >
> > _______________________________________________
> > cfe-commits mailing list
> > cfe-commits at cs.uiuc.edu
> > http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120604/32c9dab4/attachment.html
Richard Smith
2012-06-04 20:42:06 UTC
Permalink
On Mon, Jun 4, 2012 at 12:36 PM, Daniel Jasper <djasper at google.com> wrote:
> On Mon, Jun 4, 2012 at 8:33 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>> I have a few comments in addition to Jordy's:
>>
>> * Since pure virtual functions can be defined, the diagnostic as it
>> stands might have false positives. In general, I think that's
>> unavoidable and acceptable, but in the specific case of a pure virtual
>> destructor, I think we should require that the TU contains a
>> definition: such a destructor must actually be defined for the class
>> to be useful as a base class, and may well be the only member of the
>> class which has an out-of-line definition, and might reasonably
>> contain the only use (other than a side-effect-free initialization) of
>> a private member.
>
>
> Do you mean that we should require that as part of this warning (i.e. mark
> those classes as incomplete) or do you mean we should create a separate
> warning for that?

I mean that we should not consider a class to be completely defined if
it has a pure virtual destructor with no definition in this TU.

> Also, do you think such a destructor might actually have
> side-effects based on the otherwise unused fields. I somewhat doubt that as
> we are only looking at pointers or primitive fields.. We might have
> something like an owning pointer, but there the destructor will never be the
> only "use".

I'm imagining a case like this:

// .h
class RAIIBase {
public:
RAIIBase(const char *p) : LogMessage(p) {
my_debug_stream << "Creating " << p;
}
virtual ~RAIIBase() = 0;
private:
const char *LogMessage;
};

// .cc
RAIIBase::~RAIIBase() {
my_debug_stream << "Destroying " << LogMessage;
}

If I've understood correctly, the initialization of LogMessage won't
count as a 'use' because it has no side-effects, so any TU including
the .h file (other than the corresponding .cc file) would flag
'LogMessage' as being unused.

>> * It looks like in-class initializers and (in-constructor) member
>> initializers are handled slightly differently (particularly for
>> non-class types where the initializer has side-effects). It would seem
>> more consistent with the language semantics if in-class initializers
>> were treated exactly like member initializers, and only considered if
>> they're actually used by a constructor. [... it also strikes me that a
>> warning for an unused in-class initializer could be useful :) ...]
>
>
> Just to clarify: An in-class initializer is only used, if at least one
> constructor does not have a corresponding member initializer?

There are some additional complications if the class is a union or has
an anonymous union member. Perhaps moving the marking-as-used for
in-class initializers to CollectFieldInitializer would be best.

>> * I would like to see some test cases for unions, and for anonymous
>> struct/union members, in particular for cases like this:
>>
>> ?class S {
>> ? ?// ... no use of Aligner ...
>> ?private:
>> ? ?union {
>> ? ? ?void *Aligner;
>> ? ? ?unsigned char Data[8];
>> ? ?};
>> ?};
>
>
> I'd like to get the first version in. Thus I'd like to add this test case
> together with a FIXME. The correct solution for this (and especially for
> anonymous structs)?would be to look at the corresponding DeclContext, right?

Yes, to find the outer struct, you can walk through the parent
DeclContexts (while they're RecordDecls and
RecordDecl::isAnonymousStructOrUnion returns true).

>> (See clang::DependentFunctionTempateSpecializationInfo for a
>> real-world example of this.) It looks like we won't diagnose that case
>> (because Aligner is technically public, despite being effectively a
>> private member of S). But I suspect we will diagnose this (and
>> probably shouldn't):
>>
>> union S {
>> ?// ... no use of Aligner ...
>> private:
>> ?void *Aligner;
>> ?unsigned char Data[8];
>> };
>>
>> Perhaps we should conservatively disable the diagnostic for union members?
>
>
> Why should we not warn in this case? I think in general we might want to
> exempt char-arrays as they are commonly use for alignment, but other than
> that?

Aligner is 'unused', but has a semantic effect. Note that in the
clang::DependentFunctionTempateSpecializationInfo case, there is no
char array. I'm concerned that a warning for cases like this would
have a high false positive rate. (I have the same concern for padding
members in structs, but I would expect those to generally be public,
since such structs are usually intended to be PODs.)
Jordan Rose
2012-06-04 20:52:51 UTC
Permalink
On Jun 4, 2012, at 13:42 , Richard Smith <richard at metafoo.co.uk> wrote:
>>> union S {
>>> // ... no use of Aligner ...
>>> private:
>>> void *Aligner;
>>> unsigned char Data[8];
>>> };
>>>
>>> Perhaps we should conservatively disable the diagnostic for union members?
>>
>>
>> Why should we not warn in this case? I think in general we might want to
>> exempt char-arrays as they are commonly use for alignment, but other than
>> that?
>
> Aligner is 'unused', but has a semantic effect. Note that in the
> clang::DependentFunctionTempateSpecializationInfo case, there is no
> char array. I'm concerned that a warning for cases like this would
> have a high false positive rate. (I have the same concern for padding
> members in structs, but I would expect those to generally be public,
> since such structs are usually intended to be PODs.)

It'd be nice if we could silence this warning by making Aligner nameless, but as I read the standard that's not allowed.

[class.mem]p1: Except when used to declare friends (11.3) or to introduce the name of a member of a base class into a derived class (7.3.3), member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class.

And indeed we warn on such a case (poorly):
> class X {
> void *;
> };
> <stdin>:2:8: error: expected member name or ';' after declaration specifiers
> void *;
> ~~~~ ^
(the arrow is pointing to the semicolon, and only 'void' is underlined)

As such I would go with Richard and say union members should be allowed to be unused. Class members can be used for alignment too, but that's a lot rarer and users can always turn the warning off.

Jordan
Daniel Jasper
2012-06-05 09:45:36 UTC
Permalink
On Mon, Jun 4, 2012 at 10:42 PM, Richard Smith <richard at metafoo.co.uk>wrote:

> On Mon, Jun 4, 2012 at 12:36 PM, Daniel Jasper <djasper at google.com> wrote:
> > On Mon, Jun 4, 2012 at 8:33 PM, Richard Smith <richard at metafoo.co.uk>
> wrote:
> >> I have a few comments in addition to Jordy's:
> >>
> >> * Since pure virtual functions can be defined, the diagnostic as it
> >> stands might have false positives. In general, I think that's
> >> unavoidable and acceptable, but in the specific case of a pure virtual
> >> destructor, I think we should require that the TU contains a
> >> definition: such a destructor must actually be defined for the class
> >> to be useful as a base class, and may well be the only member of the
> >> class which has an out-of-line definition, and might reasonably
> >> contain the only use (other than a side-effect-free initialization) of
> >> a private member.
> >
> >
> > Do you mean that we should require that as part of this warning (i.e.
> mark
> > those classes as incomplete) or do you mean we should create a separate
> > warning for that?
>
> I mean that we should not consider a class to be completely defined if
> it has a pure virtual destructor with no definition in this TU.
>

Done and test added.

> Also, do you think such a destructor might actually have
> > side-effects based on the otherwise unused fields. I somewhat doubt that
> as
> > we are only looking at pointers or primitive fields.. We might have
> > something like an owning pointer, but there the destructor will never be
> the
> > only "use".
>
> I'm imagining a case like this:
>
> // .h
> class RAIIBase {
> public:
> RAIIBase(const char *p) : LogMessage(p) {
> my_debug_stream << "Creating " << p;
> }
> virtual ~RAIIBase() = 0;
> private:
> const char *LogMessage;
> };
>
> // .cc
> RAIIBase::~RAIIBase() {
> my_debug_stream << "Destroying " << LogMessage;
> }
>

I wonder, whether this is a case that really ever occurs (due to multiple
rare things being used at the same time). But ok.


> If I've understood correctly, the initialization of LogMessage won't
> count as a 'use' because it has no side-effects, so any TU including
> the .h file (other than the corresponding .cc file) would flag
> 'LogMessage' as being unused.
>
> >> * It looks like in-class initializers and (in-constructor) member
> >> initializers are handled slightly differently (particularly for
> >> non-class types where the initializer has side-effects). It would seem
> >> more consistent with the language semantics if in-class initializers
> >> were treated exactly like member initializers, and only considered if
> >> they're actually used by a constructor. [... it also strikes me that a
> >> warning for an unused in-class initializer could be useful :) ...]
> >
> >
> > Just to clarify: An in-class initializer is only used, if at least one
> > constructor does not have a corresponding member initializer?
>
> There are some additional complications if the class is a union or has
> an anonymous union member. Perhaps moving the marking-as-used for
> in-class initializers to CollectFieldInitializer would be best.
>

Moved this to CollectFieldInitializer. Added call to HasSideEffects (+test)
for symmetry with constructor initializers.

>
> >> * I would like to see some test cases for unions, and for anonymous
> >> struct/union members, in particular for cases like this:
> >>
> >> class S {
> >> // ... no use of Aligner ...
> >> private:
> >> union {
> >> void *Aligner;
> >> unsigned char Data[8];
> >> };
> >> };
> >
> >
> > I'd like to get the first version in. Thus I'd like to add this test case
> > together with a FIXME. The correct solution for this (and especially for
> > anonymous structs) would be to look at the corresponding DeclContext,
> right?
>
> Yes, to find the outer struct, you can walk through the parent
> DeclContexts (while they're RecordDecls and
> RecordDecl::isAnonymousStructOrUnion returns true).


Ok, I will do this once the first patch is in as I am not overly concerned
about this kind of false negative.


> >> (See clang::DependentFunctionTempateSpecializationInfo for a
> >> real-world example of this.) It looks like we won't diagnose that case
> >> (because Aligner is technically public, despite being effectively a
> >> private member of S). But I suspect we will diagnose this (and
> >> probably shouldn't):
> >>
> >> union S {
> >> // ... no use of Aligner ...
> >> private:
> >> void *Aligner;
> >> unsigned char Data[8];
> >> };
> >>
> >> Perhaps we should conservatively disable the diagnostic for union
> members?
> >
> >
> > Why should we not warn in this case? I think in general we might want to
> > exempt char-arrays as they are commonly use for alignment, but other than
> > that?
>
> Aligner is 'unused', but has a semantic effect. Note that in the
> clang::DependentFunctionTempateSpecializationInfo case, there is no
> char array. I'm concerned that a warning for cases like this would
> have a high false positive rate. (I have the same concern for padding
> members in structs, but I would expect those to generally be public,
> since such structs are usually intended to be PODs.)
>

Ah, (things-I-know-about-C++)++. :-)
I know exclude unions from this analysis.

New patch attached.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120605/13a41c0d/attachment-0001.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: unused-private-field.patch
Type: application/octet-stream
Size: 16101 bytes
Desc: not available
Url : http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120605/13a41c0d/attachment-0001.obj
Richard Smith
2012-06-05 22:17:10 UTC
Permalink
On Tue, Jun 5, 2012 at 2:45 AM, Daniel Jasper <djasper at google.com> wrote:

> On Mon, Jun 4, 2012 at 10:42 PM, Richard Smith <richard at metafoo.co.uk>wrote:
>
>> I mean that we should not consider a class to be completely defined if
>> it has a pure virtual destructor with no definition in this TU.
>>
>
> Done and test added.
>
[...]

> >> (See clang::DependentFunctionTempateSpecializationInfo for a
>>
> >> real-world example of this.) It looks like we won't diagnose that case
>> >> (because Aligner is technically public, despite being effectively a
>> >> private member of S). But I suspect we will diagnose this (and
>> >> probably shouldn't):
>> >>
>> >> union S {
>> >> // ... no use of Aligner ...
>> >> private:
>> >> void *Aligner;
>> >> unsigned char Data[8];
>> >> };
>> >>
>> >> Perhaps we should conservatively disable the diagnostic for union
>> members?
>> >
>> >
>> > Why should we not warn in this case? I think in general we might want to
>> > exempt char-arrays as they are commonly use for alignment, but other
>> than
>> > that?
>>
>> Aligner is 'unused', but has a semantic effect. Note that in the
>> clang::DependentFunctionTempateSpecializationInfo case, there is no
>> char array. I'm concerned that a warning for cases like this would
>> have a high false positive rate. (I have the same concern for padding
>> members in structs, but I would expect those to generally be public,
>> since such structs are usually intended to be PODs.)
>>
>
> Ah, (things-I-know-about-C++)++. :-)
> I know exclude unions from this analysis.
>
> New patch attached.
>

Thanks! The new patch looks good to me, other than some redundant braces
(in particular in IsRecordFullyDefined and CollectFieldInitializer). I'll
leave final approval on this to Jordy.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.cs.uiuc.edu/pipermail/cfe-commits/attachments/20120605/89143a47/attachment.html
Jordan Rose
2012-06-05 23:32:00 UTC
Permalink
Oops! The original intent of the test-on-add was to avoid doing work when the diagnostic is ignored. But it might as well avoid doing work when the diagnostic is /temporarily/ ignored, i.e. using pragmas. Please change the test in ActOnCXXMemberDeclarator to actually use the FieldDecl's SourceLocation.

With that and the braces Richard caught in IsRecordFullyDefined, it can go in. Thanks, Daniel!

Jordan
Loading...