www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - null this, classes, methods and "null this" assertion

reply ketmar <ketmar ketmar.no-ip.org> writes:
let's take a code like this:

import std.stdio;

class A {
  size_t len =3D 42;
  final size_t length () const { return (this !is null ? len : 0); }
}

void main () {
  A a;
  writeln(a.length);
  a =3D new A();
  writeln(a.length);
}

in "-release" mode this code outputs "0" and "42", as expected (by me).
but without "-release" is raises "null this" assertion.

while such check *may* be useful, it actually does nothing at all for=20
virtual functions (if we'll remove `final` from `length`, we'll get=20
segfault), and prevents writing non-virtual functions that can do=20
something sane for "null objects".

my example with `length` actually has sense, as it mimicking the built-in=20
array behavior. instead of writing

  if (obj !is null && obj.length > 0)

i can simply write:

  if (obj.length > 0)

yet there is no way to tell the compiler (and this assert is generated by=20
the compiler) to not generate the assert for given method.

i see that assert as completely unnecessary:
1. it does nothing to protect me from calling virtual method with null=20
object.
2. it forces me to write unnecessary null checks everywhere in my code=20
instead of delegating that to class methods.


we can add another attribute that turns off such checks, but i'm=20
proposing to remove that check altogether, cause:
1. there are too many attributes already.
2. let non-virtual methods segfaults as virtual ones already does, thus=20
making 'em consistent.


please, kill that counter-productive limiting "feature" for good!=
Apr 10 2015
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/10/15 9:59 PM, ketmar wrote:
 let's take a code like this:

 import std.stdio;

 class A {
    size_t len = 42;
    final size_t length () const { return (this !is null ? len : 0); }
 }

 void main () {
    A a;
    writeln(a.length);
    a = new A();
    writeln(a.length);
 }

 in "-release" mode this code outputs "0" and "42", as expected (by me).
 but without "-release" is raises "null this" assertion.
In non-release mode, it's calling the invariant, which is a virtual function. -Steve
Apr 10 2015
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Fri, 10 Apr 2015 22:06:53 -0400, Steven Schveighoffer wrote:

 On 4/10/15 9:59 PM, ketmar wrote:
 let's take a code like this:

 import std.stdio;

 class A {
    size_t len =3D 42;
    final size_t length () const { return (this !is null ? len : 0); }
 }

 void main () {
    A a;
    writeln(a.length);
    a =3D new A();
    writeln(a.length);
 }

 in "-release" mode this code outputs "0" and "42", as expected (by me).
 but without "-release" is raises "null this" assertion.
=20 In non-release mode, it's calling the invariant, which is a virtual function. =20 -Steve
yet compiler can generate `CondExp` instead of `AssertExp`, and don't=20 fail when `this` is `null`.=
Apr 10 2015
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/10/15 10:17 PM, ketmar wrote:
 On Fri, 10 Apr 2015 22:06:53 -0400, Steven Schveighoffer wrote:

 In non-release mode, it's calling the invariant, which is a virtual
 function.
yet compiler can generate `CondExp` instead of `AssertExp`, and don't fail when `this` is `null`.
A virtual function cannot be called on a null pointer. I would have actually expected it to crash, but maybe it does something special for invariant. -Steve
Apr 10 2015
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Fri, 10 Apr 2015 22:49:07 -0400, Steven Schveighoffer wrote:

 On 4/10/15 10:17 PM, ketmar wrote:
 On Fri, 10 Apr 2015 22:06:53 -0400, Steven Schveighoffer wrote:

 In non-release mode, it's calling the invariant, which is a virtual
 function.
yet compiler can generate `CondExp` instead of `AssertExp`, and don't fail when `this` is `null`.
A virtual function cannot be called on a null pointer. I would have actually expected it to crash, but maybe it does something special for invariant.
it does literally this: Expression *v =3D new ThisExp(Loc()); v->type =3D vthis->type; if (ad->isStructDeclaration()) v =3D v->addressOf(); Expression *se =3D new StringExp(Loc(), (char *)"null this"); se =3D se->semantic(sc); se->type =3D Type::tchar->arrayOf(); e =3D new AssertExp(Loc(), v, se); what i suggest is to change `AssertExp` to `CondExp` (when=20 global.params.useAssert and global.params.useInvariants are both set). it=20 *should* do the same, i think. i.e. do something like this: if (global.params.useAssert && global.params.useInvariants) { Expression *v =3D new ThisExp(Loc()); v->type =3D vthis->type; if (ad->isStructDeclaration()) v =3D v->addressOf(); e =3D new CondExp(Loc(), v, v->syntaxCopy(), new NullExp(Loc())); } else { e =3D new HaltExp(Loc()); } e =3D e->semantic(sc); e->type =3D Type::tvoid; dunno if `syntaxCopy` is necessary here, though, and how is it all=20 related to calling invariant function. it's just a blind guess.=
Apr 10 2015
prev sibling parent reply "Meta" <jared771 gmail.com> writes:
On Saturday, 11 April 2015 at 01:59:28 UTC, ketmar wrote:
 let's take a code like this:

 import std.stdio;

 class A {
   size_t len = 42;
   final size_t length () const { return (this !is null ? len : 
 0); }
 }

 void main () {
   A a;
   writeln(a.length);
   a = new A();
   writeln(a.length);
 }

 in "-release" mode this code outputs "0" and "42", as expected 
 (by me).
 but without "-release" is raises "null this" assertion.

 while such check *may* be useful, it actually does nothing at 
 all for
 virtual functions (if we'll remove `final` from `length`, we'll 
 get
 segfault), and prevents writing non-virtual functions that can 
 do
 something sane for "null objects".

 my example with `length` actually has sense, as it mimicking 
 the built-in
 array behavior. instead of writing

   if (obj !is null && obj.length > 0)

 i can simply write:

   if (obj.length > 0)

 yet there is no way to tell the compiler (and this assert is 
 generated by
 the compiler) to not generate the assert for given method.

 i see that assert as completely unnecessary:
 1. it does nothing to protect me from calling virtual method 
 with null
 object.
 2. it forces me to write unnecessary null checks everywhere in 
 my code
 instead of delegating that to class methods.


 we can add another attribute that turns off such checks, but i'm
 proposing to remove that check altogether, cause:
 1. there are too many attributes already.
 2. let non-virtual methods segfaults as virtual ones already 
 does, thus
 making 'em consistent.


 please, kill that counter-productive limiting "feature" for 
 good!
As an aside, you could easily make length a UFCS function that avoids the null check.
Apr 10 2015
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Sat, 11 Apr 2015 02:19:50 +0000, Meta wrote:

 As an aside, you could easily make length a UFCS function that avoids
 the null check.
sure i can workaround the check. but this makes my code messier, as some=20 class methods are now defined outside the class.=
Apr 10 2015