Thread overview
A temporary breach of the class invariant
Jun 10, 2004
Arcane Jill
Jun 10, 2004
Mike Swieton
Jun 11, 2004
Walter
Jun 11, 2004
Arcane Jill
Jun 11, 2004
Daniel Horn
Jun 12, 2004
J Anderson
Jun 22, 2004
Oskar Linde
Jun 22, 2004
Ant
Jun 22, 2004
Oskar Linde
Jun 22, 2004
Ant
June 10, 2004
I had to take the class invariants out of Int, I'm afraid. D is just TOO strict.

The problem comes with inlining stuff. Say you had:

>    a = 1;
>    b = 2;

and that compiled ok and runs fine. Let's say, for sake of argument, that the class invariant does not hold between the two lines.

Now the problem comes when you replace these two lines with hopefully inline functions. Now the thing about inline functions is - even though they're inline, they're still FUNCTIONS, which means that the class invariant gets tested. In fact, if you use any "getter" functions within the invariant test itself, you've got an infinite loop and a stack overflow.

I would like to propose that functions be allowed temporarily to suspend the class invariant. Currently, the compiler checks it on entry and exit to every function. I would argue that it should only be checked whenever a function is called /from outside the class/. Otherwise a simple getter method can totally foil the most audacious design by contract. (And in D, getter functions include properties).

This is similar to the encoder/decoder calling each other problem.

There are a number of ways to fix this, including simply looking out for recursion during all DbC, and refusing to recurse. But when DbC stops other DbC from working, things have gone too far. So I would like to add my support to some relaxation in this area.

Arcane Jill


June 10, 2004
On Thu, 10 Jun 2004 21:48:52 +0000, Arcane Jill wrote:
> I would like to propose that functions be allowed temporarily to suspend the class invariant. Currently, the compiler checks it on entry and exit to every function. I would argue that it should only be checked whenever a function is called /from outside the class/. Otherwise a simple getter method can totally foil the most audacious design by contract. (And in D, getter functions include properties).
> 

I'll second this. This would be tremendously useful. One specific instance in which it would be handy is enforcing that mixins are initialized properly, by allowing an init method to be called outside of the invariant, and having the invariant check that it was called.

A temporary solution is to have the 'real' methods be implemented on a nested class, and have the invariants (located in the outer class) assert things about the nested class. The nested class's calls would be direct. This would solve your problem in an extremely ugly and extra-work-ish manner, but it would work ;)

Mike Swieton
__
One of the main causes of the fall of the Roman Empire was that, lacking zero,
they had no way to indicate successful termination of their C programs.
	- Robert Firth

June 11, 2004
Invariants only get called on entry/exit on *public* functions. Public functions shouldn't need to access getter/setter functions, they can operate directly on the private data.

"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:caal04$1v0d$1@digitaldaemon.com...
>
> I had to take the class invariants out of Int, I'm afraid. D is just TOO
strict.
>
> The problem comes with inlining stuff. Say you had:
>
> >    a = 1;
> >    b = 2;
>
> and that compiled ok and runs fine. Let's say, for sake of argument, that
the
> class invariant does not hold between the two lines.
>
> Now the problem comes when you replace these two lines with hopefully
inline
> functions. Now the thing about inline functions is - even though they're
inline,
> they're still FUNCTIONS, which means that the class invariant gets tested.
In
> fact, if you use any "getter" functions within the invariant test itself,
you've
> got an infinite loop and a stack overflow.
>
> I would like to propose that functions be allowed temporarily to suspend
the
> class invariant. Currently, the compiler checks it on entry and exit to
every
> function. I would argue that it should only be checked whenever a function
is
> called /from outside the class/. Otherwise a simple getter method can
totally
> foil the most audacious design by contract. (And in D, getter functions
include
> properties).
>
> This is similar to the encoder/decoder calling each other problem.
>
> There are a number of ways to fix this, including simply looking out for recursion during all DbC, and refusing to recurse. But when DbC stops
other DbC
> from working, things have gone too far. So I would like to add my support
to
> some relaxation in this area.
>
> Arcane Jill
>
>


June 11, 2004
In article <cabiqn$9a9$1@digitaldaemon.com>, Walter says...
>
>Invariants only get called on entry/exit on *public* functions. Public functions shouldn't need to access getter/setter functions, they can operate directly on the private data.

That isn't relevant. Getter and setter functions allow you to change the implementation of the property without having to go through and recode your whole source file. They enable you to have a single point of failure, in the event of a bug. They allow your code to be more maintainable by allowing you avoid massive duplication of code.

And when your getter and setter functions have if/else logic inside them; access union elements depending on what's in the union; and so on, you DON'T want to be duplicating all that code all over the place. Regardless of whether they are inlined, I still want to be able to use them.

I see three possible solutions, in terms of changing the language, and at least one workaround if those won't happen.

(a) Introduce an attribute keyword which marks a function as being callable without invoking the class invariant test on its entry and exit;

(b) Introduce a statement modifier keyword which indicates that within a statement or statement block, the class invariant will not be tested no matter what functions are called

(c) Never test the class invariant if the callee is a class member variable.

My favorite is (c).

The workaround is ugly, but it does work. What you do is you replace your getter functions as follows. Before:

>    class A
>    {
>        T getValue() { /* whatever */
>    }

After:

>    class A
>    {
>        T getValue() { invariantSafeGetValue(this); }
>    }
>
>    package T invariantSafeGetValue(A a) { /* whatever */ }

then replace all calls to getValue() with invariantSafeGetValue(this);

..but obviously I'd rather you went for plan (c).

Jill


June 11, 2004
This is the reason that I never put invariants or inout checks in my code.
In this case the testing solution causes most of the bugs (i.e. endless recursion in an unforseen function call order)
I mean you could have the unit tests muck up something at the last iteration of a loop when you finally visit that function that calls the recursive unit test...and then you lose your days worth of results...

so ya until I have to stop worrying about runtime endless loops occuring, these tests IMHO cause more problems than they solve.

c) is fine, but what if you test another class's result which tests your result...it pushes the error down one level--which may be enough for practical purposes...but it doesn't solve the main problem...which is recursion. I Think the compiler should detect recursion in these unit tests (it could do it at runtime by saving a hashtable of previously called functions... and then if it sees itself just abort with "everything is fine")
-Daniel
Arcane Jill wrote:
> In article <cabiqn$9a9$1@digitaldaemon.com>, Walter says...
> 
>>Invariants only get called on entry/exit on *public* functions. Public
>>functions shouldn't need to access getter/setter functions, they can operate
>>directly on the private data.
> 
> 
> That isn't relevant. Getter and setter functions allow you to change the
> implementation of the property without having to go through and recode your
> whole source file. They enable you to have a single point of failure, in the
> event of a bug. They allow your code to be more maintainable by allowing you
> avoid massive duplication of code.
> 
> And when your getter and setter functions have if/else logic inside them; access
> union elements depending on what's in the union; and so on, you DON'T want to be
> duplicating all that code all over the place. Regardless of whether they are
> inlined, I still want to be able to use them.
> 
> I see three possible solutions, in terms of changing the language, and at least
> one workaround if those won't happen.
> 
> (a) Introduce an attribute keyword which marks a function as being callable
> without invoking the class invariant test on its entry and exit;
> 
> (b) Introduce a statement modifier keyword which indicates that within a
> statement or statement block, the class invariant will not be tested no matter
> what functions are called
> 
> (c) Never test the class invariant if the callee is a class member variable.
> 
> My favorite is (c).
> 
> The workaround is ugly, but it does work. What you do is you replace your getter
> functions as follows. Before:
> 
> 
>>   class A
>>   {
>>       T getValue() { /* whatever */
>>   }
> 
> 
> After:
> 
> 
>>   class A
>>   {
>>       T getValue() { invariantSafeGetValue(this); }
>>   }
>>
>>   package T invariantSafeGetValue(A a) { /* whatever */ }
> 
> 
> then replace all calls to getValue() with invariantSafeGetValue(this);
> 
> ..but obviously I'd rather you went for plan (c).
> 
> Jill
> 
> 
June 12, 2004
Walter wrote:

>Invariants only get called on entry/exit on *public* functions. Public
>functions shouldn't need to access getter/setter functions, they can operate
>directly on the private data.
>
>  
>
I found this ability of D quite useful when I needed to remove a public member variable from a class.  That way I did not need to go find all the locations of that variable (in itself and inherited classes) and replace it with its new location.

-- 
-Anderson: http://badmama.com.au/~anderson/
June 22, 2004
Walter wrote:
> Invariants only get called on entry/exit on *public* functions. Public
> functions shouldn't need to access getter/setter functions, they can operate
> directly on the private data.

Is this true for the current dmd compiler? The following piece of code gives an AssertError:

<code>
class Test {
    this() { a = 0; b = 0; }
    void test() { incA(); decB(); }

private:
    int a,b;

    void incA() { a++; }
    void decB() { b--; }

    invariant {
        assert(a+b == 0);
    }
}

void main() {
    Test t = new Test();
    t.test();
}
</code>

If invariants are only tested at entry and exit of public functions, there should be no assertion in test() between the private calls?

/Oskar
June 22, 2004
In article <cba6h3$ddj$1@digitaldaemon.com>, Oskar Linde says...
>
>Walter wrote:
>> Invariants only get called on entry/exit on *public* functions. Public functions shouldn't need to access getter/setter functions, they can operate directly on the private data.

but that's not always desirable...

Ant


June 22, 2004
Ant wrote:

> In article <cba6h3$ddj$1@digitaldaemon.com>, Oskar Linde says...
> 
>>Walter wrote:
>>
>>>Invariants only get called on entry/exit on *public* functions. Public
>>>functions shouldn't need to access getter/setter functions, they can operate
>>>directly on the private data.
> 
> 
> but that's not always desirable...
> 
> Ant
> 
> 

You can always do:

class C {
public:
    int get() { return privateGet(); }
    int temporaryInvariantBreach() {
	/* use privateGet instead of get()*/
    }
private:
    int privateGet() {...}
}

/Oskar
June 22, 2004
In article <cba92q$hdj$1@digitaldaemon.com>, Oskar Linde says...
>
>Ant wrote:
>
>> In article <cba6h3$ddj$1@digitaldaemon.com>, Oskar Linde says...
>> 
>>>Walter wrote:
>>>
>>>>Invariants only get called on entry/exit on *public* functions. Public functions shouldn't need to access getter/setter functions, they can operate directly on the private data.
>> 
>> 
>> but that's not always desirable...
>> 
>> Ant
>> 
>> 
>
>You can always do:
>
>class C {
>public:
>     int get() { return privateGet(); }
>     int temporaryInvariantBreach() {
>	/* use privateGet instead of get()*/
>     }
>private:
>     int privateGet() {...}
>}
>
>/Oskar

it's a bit artificial, isn't it?...

Ant