January 25, 2015
On Sunday, 25 January 2015 at 22:46:56 UTC, Ali Çehreli wrote:
> On 01/25/2015 02:06 PM, Bayan Rafeh wrote:
>
>> is invariant() called during the destruction phase?
>
> Something is fishy.
>
> import std.stdio;
>
> void main(){
>     writeln("entered main");
>     auto a = new A();
>     writeln("leaving main");
> }
>
> class A {
>     File file;
>
>     this() {
>         writeln("this");
>     }
>
>     ~this() {
>         writeln("~this");
>     }
>
>     invariant() {
>         writeln("invariant");
>     }
> }
>
> The program produces the following output:
>
> entered main
> this
> invariant
> leaving main
> invariant
> invariant    <-- ?
> ~this
> invariant    <-- ?
>
> Removing the File member changes the output. Now two of the invariant calls are missing:
>
> entered main
> this
> invariant
> leaving main
> invariant
> ~this
>
> However, the last invariant is still there and I think by design: The destructor should see a valid state.
>
> Ali

Could this be a bug in D?
January 26, 2015
On Sun, 25 Jan 2015 22:06:26 +0000, Bayan Rafeh wrote:

> This is another problematic example program:
> import std.stdio;
> 
> void main(){
>      auto a = new A("/tmp/invalid");
> }
> 
> class A {
>      File f;
>      string path;
> 
>      this(string path) {
>          this.path = path;
> //        f = File(path, "r");
>      }
> 
>      invariant() {
>          File test = File(path, "r");
>      }
> }
> 
> is invariant() called during the destruction phase?

the thing is that your invariant is not a correct invariant at all. invariants are meant to check *internal* object consistency, not external conditions. compiler is free to call invariant block at any time after object is properly initialised (i.e. after ctor is complete) and is not executing member method. so it's perfectly legal to call invariant before dtor, as you should not check anything that is not belonging to the object itself in in.

in other words: you can't check any contents of any reference-typed variables in your invariant block. `string` is reference-typed (it's a dynamic array managed by GC in your case), so you can't check it contents in invariant. you CAN, however, use `f` methods in your invariant, as `f` is a struct which lives *inside* your object, and not a reference var.

but note that it's a bad practice anyway, as some struct can use some GC- managed objects which can't be safely used in invariant block.

January 26, 2015
On Sun, 25 Jan 2015 22:06:26 +0000, Bayan Rafeh wrote:

p.s. yet creating new `File` in invariant is wrong nevertheless, as it changes the program state. invariant checks SHOULD NEVER CHANGE THE PROGRAM STATE.

January 26, 2015
>
> the thing is that your invariant is not a correct invariant at all.
> invariants are meant to check *internal* object consistency, not external
> conditions. compiler is free to call invariant block at any time after
> object is properly initialised (i.e. after ctor is complete) and is not
> executing member method. so it's perfectly legal to call invariant before
> dtor, as you should not check anything that is not belonging to the
> object itself in in.

> in other words: you can't check any contents of any reference-typed
> variables in your invariant block. `string` is reference-typed (it's a
> dynamic array managed by GC in your case), so you can't check it contents
> in invariant. you CAN, however, use `f` methods in your invariant, as `f`
> is a struct which lives *inside* your object, and not a reference var.
>
> but note that it's a bad practice anyway, as some struct can use some GC-
> managed objects which can't be safely used in invariant block.

There are 2 problems here:

1. By definition, after the destructor is called the object state
is destroyed. It makes no sense to check the invariant after the
destructor is called because there is no state for us to check.

2. Invariants theoretically describe the legal states the object
can be in which includes variables that are GC managed, therefore
I should be able to check the invariant on GC managed objects as
well. What's the point of having this feature if I can't even
check the invariants of a simple data structure like a set, let
alone classes involving files?
January 26, 2015
On Mon, 26 Jan 2015 05:25:48 +0000, Bayan Rafeh wrote:

> There are 2 problems here:
> 
> 1. By definition, after the destructor is called the object state is destroyed. It makes no sense to check the invariant after the destructor is called because there is no state for us to check.
i agree that calling invariant after destruction is invalid. i believe that this is a bug.

> 2. Invariants theoretically describe the legal states the object can be in which includes variables that are GC managed, therefore I should be able to check the invariant on GC managed objects as well. What's the point of having this feature if I can't even check the invariants of a simple data structure like a set, let alone classes involving files?
checking something that is not owned by an object is invalid. invariants MUST NOT be dependent on external data or system state. invariants that tries to check for GC objects ARE dependent of external state. let alone invariants that checking for files, which can be removed while the object is still alive.

object can own another object, but it can't own another object *state*.

if you want to check something that involves checking something like that, you should use `assert()`s in function body and/or in/out function parts.

you are trying to use invariants for the things that invariants can't (and must not) check. invariants are meant for checking *internal* *object* *consistency*. NOT correctness. NOT applicability. ONLY consistency. object can be in "inapplicable" state, but still consistent.

January 26, 2015
> you are trying to use invariants for the things that invariants can't
> (and must not) check. invariants are meant for checking *internal*
> *object* *consistency*. NOT correctness. NOT applicability. ONLY
> consistency. object can be in "inapplicable" state, but still consistent.

Then I must have misunderstood the documentation, I apologize, I thought the point of invariant was to ensure correctness of the object's state. I'll go ahead and report the bug.
January 26, 2015
Bug report at https://issues.dlang.org/show_bug.cgi?id=14051
January 26, 2015
On Monday, 26 January 2015 at 15:54:12 UTC, Bayan Rafeh wrote:
> I apologize, I thought the point of invariant was to ensure correctness of the
object's state

It is simply matter of "internal correctness" vs "in-program correctness". Sorry, documentation should probably mention that explicitly. I have created a documentation PR :  https://github.com/D-Programming-Language/dlang.org/pull/851

January 26, 2015
On Mon, 26 Jan 2015 11:37:12 +0000, Bayan Rafeh wrote:

>> you are trying to use invariants for the things that invariants can't (and must not) check. invariants are meant for checking *internal* *object* *consistency*. NOT correctness. NOT applicability. ONLY consistency. object can be in "inapplicable" state, but still consistent.
> 
> Then I must have misunderstood the documentation, I apologize, I thought the point of invariant was to ensure correctness of the object's state. I'll go ahead and report the bug.

but please, take my words with a grain of salt. i'm in no way a representative of D devs. that is how *i* understand invariants. it seems to be consistent with the cases where invariant works, but the designers may have different opinion, and don't have time to properly fix invariant handling.

January 27, 2015
> but please, take my words with a grain of salt. i'm in no way a
> representative of D devs. that is how *i* understand invariants. it seems to be consistent with the cases where invariant works,

This is the first serious project I do with D, so I'm kind of discovering the language as I write my code. I found the contracts page and got overly excited about it, so you're probably right.

> but the designers may have different opinion, and don't have time to
> properly fix invariant handling.

That's fair. Useful as it may be for debugging, it's something I can live without.