Constructors vs Copy Constructors in Clang

Hi,

One of the things that I'm seeing in several places is where VarDecl::getInit() is returning an Expr that is CXXConstructExpr when a class variable is declared. However, the logic that follows the getInit() call usually assumes that if a non-NULL value is returned, there is a value being assigned. In the case of a constructor, that's sort of true but not completely unless it is a copy constructor. So, a question of philosophy:

Should getInit() return NULL when the constructor is not a copy constructor to support the logic in most cases? This might break or require a rework of things that do handle getInit() correctly like CodeGen and require a different call to get a non-copy constructor initializer.

Or should the calls to getInit() be qualified by an additional call to an 'isCopyConstructor()' function where appropriate?

  - jim

Hi,

One of the things that I'm seeing in several places is where
VarDecl::getInit() is returning an Expr that is CXXConstructExpr when
a class variable is declared. However, the logic that follows the
getInit() call usually assumes that if a non-NULL value is returned,
there is a value being assigned. In the case of a constructor, that's
sort of true but not completely unless it is a copy constructor.

It need not always be a copy constructor. Any constructor could occur there. A CXXConstructExpr occurs anywhere that we're performing initialization by constructor.

So,
a question of philosophy:

Should getInit() return NULL when the constructor is not a copy
constructor to support the logic in most cases? This might break or
require a rework of things that do handle getInit() correctly like
CodeGen and require a different call to get a non-copy constructor initializer.

Or should the calls to getInit() be qualified by an additional call
to an 'isCopyConstructor()' function where appropriate?

Why do you consider copy constructors to be special? They are elidable in some cases (and CXXConstructExpr tells us when the construction can be elided), but they aren't the only kinds of constructors that are elidable (C++0x allows move constructors to be elided, too). Static analysis need not treat them as special, unless for some reason that improves results.

  - Doug

> Hi,
>
> One of the things that I'm seeing in several places is where
> VarDecl::getInit() is returning an Expr that is CXXConstructExpr when
> a class variable is declared. However, the logic that follows the
> getInit() call usually assumes that if a non-NULL value is returned,
> there is a value being assigned. In the case of a constructor, that's
> sort of true but not completely unless it is a copy constructor.

It need not always be a copy constructor. Any constructor could occur there. A CXXConstructExpr occurs anywhere that we're performing initialization by constructor.

Yes, I am aware of that. The problem is distinguishing between:

foo f;
foo f1 = f;

Both declarations will return a CXXConstructExpr from getInit(), but the code I am finding is assuming that there is an assignment happening which is not the case in the first declaration.

> So,
> a question of philosophy:
>
> Should getInit() return NULL when the constructor is not a copy
> constructor to support the logic in most cases? This might break or
> require a rework of things that do handle getInit() correctly like
> CodeGen and require a different call to get a non-copy constructor initializer.
>
> Or should the calls to getInit() be qualified by an additional call
> to an 'isCopyConstructor()' function where appropriate?

Why do you consider copy constructors to be special? They are elidable in some cases (and CXXConstructExpr tells us when the construction can be elided), but they aren't the only kinds of constructors that are elidable (C++0x allows move constructors to be elided, too). Static analysis need not treat them as special, unless for some reason that improves results.

        - Doug

Again, I'm looking at code in the static analyzer and other places that is assuming an assignment is taking place because getInit() is not returning a NULL. My question is how to distinguish between the two examples I gave before? One way is to check the result for a constructor and then look at the constructor and see if it is a copy constructor or not. So,

if (Expr *Init = D->getInit()) {

becomes

Expr *Init = D->getInit();
if (Init && (!isa<CXXConstructExpr>(Init) ||
                     dyn_cast<CXXConstructExpr>(Init)->getConstructor()->isCopyConstructor())) {

  - jim

> Hi,
>
> One of the things that I'm seeing in several places is where
> VarDecl::getInit() is returning an Expr that is CXXConstructExpr when
> a class variable is declared. However, the logic that follows the
> getInit() call usually assumes that if a non-NULL value is returned,
> there is a value being assigned. In the case of a constructor, that's
> sort of true but not completely unless it is a copy constructor.

It need not always be a copy constructor. Any constructor could occur there. A CXXConstructExpr occurs anywhere that we're performing initialization by constructor.

Yes, I am aware of that. The problem is distinguishing between:

foo f;
foo f1 = f;

Both declarations will return a CXXConstructExpr from getInit(), but the code I am finding is assuming that there is an assignment happening which is not the case in the first declaration

The AST for the first will be a CXXConstructExpr that calls the default constructor.
The AST for the second will be a CXXConstructExpr calls the copy constructor, with f as its argument.

> So,
> a question of philosophy:
>
> Should getInit() return NULL when the constructor is not a copy
> constructor to support the logic in most cases? This might break or
> require a rework of things that do handle getInit() correctly like
> CodeGen and require a different call to get a non-copy constructor initializer.
>
> Or should the calls to getInit() be qualified by an additional call
> to an 'isCopyConstructor()' function where appropriate?

Why do you consider copy constructors to be special? They are elidable in some cases (and CXXConstructExpr tells us when the construction can be elided), but they aren't the only kinds of constructors that are elidable (C++0x allows move constructors to be elided, too). Static analysis need not treat them as special, unless for some reason that improves results.

       - Doug

Again, I'm looking at code in the static analyzer and other places that is assuming an assignment is taking place because getInit() is not returning a NULL. My question is how to distinguish between the two examples I gave before? One way is to check the result for a constructor and then look at the constructor and see if it is a copy constructor or not. So,

if (Expr *Init = D->getInit()) {

becomes

Expr *Init = D->getInit();
if (Init && (!isa<CXXConstructExpr>(Init) ||
                   dyn_cast<CXXConstructExpr>(Init)->getConstructor()->isCopyConstructor())) {

Why are you trying to treat the copy constructor as special here? In both cases the static analyzer needs to model a call to the constructor. If you're planning to treat a call to the copy constructor as a "copy" (in some semantic sense), then you'll want to recognize any CXXConstructExpr/CXXTemporaryObjectExpr expressions that calls the copy constructor as being a "copy", not just ones that show up when initializing variables.

  - Doug

Hi,

One of the things that I’m seeing in several places is where

VarDecl::getInit() is returning an Expr that is CXXConstructExpr when

a class variable is declared. However, the logic that follows the

getInit() call usually assumes that if a non-NULL value is returned,

there is a value being assigned. In the case of a constructor, that’s

sort of true but not completely unless it is a copy constructor.

It need not always be a copy constructor. Any constructor could

occur there. A CXXConstructExpr occurs anywhere that we’re

performing initialization by constructor.

Yes, I am aware of that. The problem is distinguishing between:

foo f;
foo f1 = f;

Both declarations will return a CXXConstructExpr from getInit(), but
the code I am finding is assuming that there is an assignment
happening which is not the case in the first declaration.

So,

a question of philosophy:

Should getInit() return NULL when the constructor is not a copy

constructor to support the logic in most cases? This might break or

require a rework of things that do handle getInit() correctly like

CodeGen and require a different call to get a non-copy

constructor initializer.

Or should the calls to getInit() be qualified by an additional call

to an ‘isCopyConstructor()’ function where appropriate?

Why do you consider copy constructors to be special? They are

elidable in some cases (and CXXConstructExpr tells us when the

construction can be elided), but they aren’t the only kinds of

constructors that are elidable (C++0x allows move constructors to be

elided, too). Static analysis need not treat them as special, unless

for some reason that improves results.

  • Doug

Again, I’m looking at code in the static analyzer and other places
that is assuming an assignment is taking place because getInit() is
not returning a NULL. My question is how to distinguish between the
two examples I gave before?

See VarDecl’s hasCXXDirectInitializer():

/// hasCXXDirectInitializer - If true, the initializer was a direct
/// initializer, e.g: “int x(1);”. The Init expression will be the expression
/// inside the parens or a “ClassType(a,b,c)” class constructor expression for
/// class types. Clients can distinguish between “int x(1);” and “int x=1;”
/// by checking hasCXXDirectInitializer.

-Argiris