View mode: basic / threaded / horizontal-split · Log in · Help
July 02, 2012
state of an object
Is it possible to figure out how is the state of an object at 
compile time?
E.g. if the object is null or not:

class Foo { }

Foo f;

static if (is_null(f)) { }
July 02, 2012
Re: state of an object
Namespace:

> Is it possible to figure out how is the state of an object at 
> compile time?
> E.g. if the object is null or not:
>
> class Foo { }
>
> Foo f;
>
> static if (is_null(f)) { }

In general you need a tool that analyzes D code statically (and 
maybe in some cases doesn't give a certain answer).

Bye,
bearophile
July 02, 2012
Re: state of an object
> In general you need a tool that analyzes D code statically (and 
> maybe in some cases doesn't give a certain answer).
>
> Bye,
> bearophile

Short: not so easy. Too bad.
July 02, 2012
Re: state of an object
On Monday, July 02, 2012 08:19:52 Namespace wrote:
> Is it possible to figure out how is the state of an object at
> compile time?
> E.g. if the object is null or not:
> 
> class Foo { }
> 
> Foo f;
> 
> static if (is_null(f)) { }

What are you trying to test exactly? Whether f default initializes to null? 
That's as easy as

static if(typeof(f).init is null);

But if what you want to test is what f is initialized to  - such that you can 
get the value if it were

Foo f = initialize();

then no, you can't do that.  D won't let you use any global or static variable 
at compile time in order to avoid subtle errors caused by ordering and 
circular dependencies.

If f were an enum you could, but then it's a manifest constant rather than a 
variable, and you can't currenly initialize classes to anything other than 
null at compile time.

- Jonathan M Davis
July 02, 2012
Re: state of an object
My intention was to avoid something like this:

[code]
class Foo { }

Foo f; // f is null

NotNull!(Foo) test = f; // should throw an compiler error, 
because f is null
[/code]

I can avoid

NotNull!(Foo) test = null; with

@disable
this(typeof(null));

but how can i avoid null objects?
July 02, 2012
Re: state of an object
On Monday, July 02, 2012 10:41:03 Namespace wrote:
> My intention was to avoid something like this:
> 
> [code]
> class Foo { }
> 
> Foo f; // f is null
> 
> NotNull!(Foo) test = f; // should throw an compiler error,
> because f is null
> [/code]
> 
> I can avoid
> 
> NotNull!(Foo) test = null; with
> 
> @disable
> this(typeof(null));
> 
> but how can i avoid null objects?

The _only_ way to statically disallow assigning a null Foo to a NotNull!Foo 
object is to statically disallow assigning a Foo to it period. Its nullity is 
not part of its type and therefore cannot be checked statically. As far as I 
can tell, you have two choices:

1. Throw when a NotNull is assigned or constructed from a null Foo.

2. Disallow assignment and construction of a NotNull!Foo from any and all Foos 
- in which case the NotNull!Foo needs to construct any Foo that it contains.

- Jonathan M Davis
July 02, 2012
Re: state of an object
Can you show me an example of your two options?
I'm not sure what do you exactly mean.
July 02, 2012
Re: state of an object
I cannot create the Foo objects _in_ the NotNull struct because i 
need the reference of an specific object.
And if i throw an error if the object paramter is null, it's not 
a compiler error.
So there is no way that a null reference as paramter can be 
detect at compile time? I tried different __traits but i wondered 
that there is nothing like
__traits(ThrowError, { object.toString(); }) which would be 
perfect to detect that cases.
July 02, 2012
Re: state of an object
On Monday, July 02, 2012 17:25:03 Namespace wrote:
> I cannot create the Foo objects _in_ the NotNull struct because i
> need the reference of an specific object.
> And if i throw an error if the object paramter is null, it's not
> a compiler error.
> So there is no way that a null reference as paramter can be
> detect at compile time? I tried different __traits but i wondered
> that there is nothing like
> __traits(ThrowError, { object.toString(); }) which would be
> perfect to detect that cases.

The state of an object is entirely a runtime artifact. Take this function for 
example

void func(Foo foo) {}

How is the compiler going to know whether Foo is null or not? Whether foo is 
null or not is runtime state. You could have called it with func(null). You 
could have called it with func(new Foo(7)). You could have called it with 
func(bar()), and who knows what the value is that bar returns. It could be 
null. It could be Foo(42). It could be any valid value of Foo. The compiler 
can't know what the value of foo is anymore than it the sqrt function can know 
whether you're going to pass it a 0. That's all runtime-dependent.

Now that typeof(null) is its own type, it's possible to specialize on that and 
prevent a function from taking a null literal.

void func(T)(T value)
if(is(T : Foo) && !is(T == typeof(null))
{}

or

void func()(typeof(null) value) {static assert(0;}
void func()(Foo value) {}

or in the case of a constructor

@disable this(typeof(this));

But _all_ that that prevents is null being passed directly - i.e. func(null) 
is illegal. But func(bar()) could still result in func being passed a null Foo 
if bar returns a null Foo.

Whether an object is null or not is not known until runtime and the compiler 
_cannot_ determine that for you. Object state is a runtime artifact. The only 
stuff that can be tested at compile time are types, whether a particular piece 
of code will compile or not, and the state of a particular variable which is 
created as part of CTFE. You can't test the state of variable which only 
exists at runtime. Its state doesn't exist yet!

On Monday, July 02, 2012 12:33:26 Namespace wrote:
> Can you show me an example of your two options?
> I'm not sure what do you exactly mean.

1. Throw if passed a null Foo:

struct NotNull(T)
if(is(T == class) || isPointer!T)
{
this(T value)
{
enforce(value !is null);
}

NotNull opAssign(NotNull rhs)
{
_value = rhs._value;
}

NotNull opAssign(Foo rhs)
{
enforce(value !is null);
_value = rhs;
}

T get() pure nothrow
{
return _value;
}

alias get this;

private:
T _value;
}

As an alternative to enforce, you can assert, which indicates that it's a bug 
in the program using NotNull if it ever assigns a null Foo to a NotNull!Foo, 
whereas with enforce, you're indicating that it's not a bug in the program if 
it occurs (it's just something that's considered an error when it occurs). 
With the enforce, it's perfectly okay for the program to not worry about 
assigning a null Foo to NotNull!Foo, whereas with an assertion, it needs to 
check in any case where it might occur in order to prevent passing a null Foo 
to a NotNull!Foo.

2. Don't ever construct or assign a NotNull!Foo from a Foo:

struct NotNull(T)
if(is(T == class) || isPointer!T)
{
this(Args...)(Args args)
if(is(typeof(new T(args))))
{
_value = new T(args);
}

T get() pure nothrow
{
return _value;
}

alias get this;

private:
T _value;
}


There's currently a pull request for Phobos which takes the approach of 
asserting that NotNull!Foo isn't passed a null Foo. Once it's been fixed up, 
it'll likely end up in Phobos:

https://github.com/D-Programming-Language/phobos/pull/477

- Jonathan M Davis
Top | Discussion index | About this forum | D home