www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - Nasty invariant bug

reply Andy Friesen <andy ikagames.com> writes:
     struct Foo {
         int x() {
             return _x;
         }
         void x(int i) {
             _x = i;
         }
         int _x;

         invariant {
             assert (x != 13); // guard against bad luck
         }
     }

     int main() {
         Foo foo;
         foo.x = 999;
         int x = foo.x;
         printf("%i\n", x);
         return x;
     }

Setting foo.x causes a stack overflow because Foo.x checks the 
invariant, which calls Foo.x() which itself cheks the invariant again, 
ad infinitum.

This isn't really a bug per se, but I somehow doubt that anybody would 
ever expect or desire this behaviour.

It would be nice if method calls didn't trigger invariant tests from 
within the invariant itself.  If that's unfeasable, a good error message 
would be sufficient.

I think it's pretty safe to say that this one is /way/ too subtle to 
leave as-is. :)

  -- andy
Aug 21 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cg830p$289n$1 digitaldaemon.com>, Andy Friesen says...
     struct Foo {
         int x() {
             return _x;
         }
         void x(int i) {
             _x = i;
         }
         int _x;

         invariant {
             assert (x != 13); // guard against bad luck
         }
     }

     int main() {
         Foo foo;
         foo.x = 999;
         int x = foo.x;
         printf("%i\n", x);
         return x;
     }

Setting foo.x causes a stack overflow because Foo.x checks the 
invariant, which calls Foo.x() which itself cheks the invariant again, 
ad infinitum.

This isn't really a bug per se, but I somehow doubt that anybody would 
ever expect or desire this behaviour.

It would be nice if method calls didn't trigger invariant tests from 
within the invariant itself.  If that's unfeasable, a good error message 
would be sufficient.

I think it's pretty safe to say that this one is /way/ too subtle to 
leave as-is. :)

  -- andy
There was a thread a while back - though possibly in the other forum - called something like "a temporary relaxation of the class invariant", in which I mentioned a similar problem. I had code which did something like this: Walter's basic response at the time was "well don't do that then - access the variable directly". However, there are sometimes very good reasons why you might /want/ to abstract things away to a getter/setter function, and it /might/ do something a lot more complicated than just reference a variable, so you'd have to cut-and-paste and duplicate code all over the place. This is the kind of place where a #define would be handy - but D's answer to #define (the inline function) checks the damn invariant. I've suggested before that the class invariant should be suspended (that is, not checked) when a function is called from /within/ that class, and only invoked when a member function is called from outside. Sadly, the only realistic workaround we coders can use is to /not/ supply a class invariant in these circumstances. D wants to encourange DbC, but by being /too/ overzealous about checking, it can actually discourage it. I'd like to see this relaxed. Arcane Jill
Aug 21 2004
parent reply "Matthew" <admin.hat stlsoft.dot.org> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cg89on$2bva$1 digitaldaemon.com...
 In article <cg830p$289n$1 digitaldaemon.com>, Andy Friesen says...
     struct Foo {
         int x() {
             return _x;
         }
         void x(int i) {
             _x = i;
         }
         int _x;

         invariant {
             assert (x != 13); // guard against bad luck
         }
     }

     int main() {
         Foo foo;
         foo.x = 999;
         int x = foo.x;
         printf("%i\n", x);
         return x;
     }

Setting foo.x causes a stack overflow because Foo.x checks the
invariant, which calls Foo.x() which itself cheks the invariant again,
ad infinitum.

This isn't really a bug per se, but I somehow doubt that anybody would
ever expect or desire this behaviour.

It would be nice if method calls didn't trigger invariant tests from
within the invariant itself.  If that's unfeasable, a good error message
would be sufficient.

I think it's pretty safe to say that this one is /way/ too subtle to
leave as-is. :)

  -- andy
There was a thread a while back - though possibly in the other forum - called something like "a temporary relaxation of the class invariant", in which I mentioned a similar problem. I had code which did something like this: Walter's basic response at the time was "well don't do that then - access the variable directly". However, there are sometimes very good reasons why you might /want/ to abstract things away to a getter/setter function, and it /might/ do something a lot more complicated than just reference a variable, so you'd have to cut-and-paste and duplicate code all over the place. This is the kind of place where a #define would be handy - but D's answer to #define (the inline function) checks the damn invariant. I've suggested before that the class invariant should be suspended (that is, not checked) when a function is called from /within/ that class, and only invoked when a member function is called from outside. Sadly, the only realistic workaround we coders can use is to /not/ supply a class invariant in these circumstances. D wants to encourange DbC, but by being /too/ overzealous about checking, it can actually discourage it. I'd like to see this relaxed.
I'd like to see a reasoned treatment of the consequences of suspending invariant calls for all but the outermost instance method call. It sounds nice, and I have no immediate counter arguments, but I think it needs some thinking about.
Aug 21 2004
parent "Bent Rasmussen" <exo bent-rasmussen.info> writes:
 I'd like to see a reasoned treatment of the consequences of suspending
 invariant calls for all but the outermost
 instance method call. It sounds nice, and I have no immediate counter
 arguments, but I think it needs some thinking
 about.
OOSC page 364 and onwards. "In spite of its name, the invariant does not need to be satisfied at all times..." "it is perfectly acceptable for a procedure g to begin by trying to work towards its goal -- its postcondition -- and in the process to destroy the invariant (as in human affairs, trying to do something useful may disrupt the established order of things); then it spends the second part of its execution trying to restore the invariant without loosing too much of whatever ground has been gained" "Qualified calls of the form a.f ...), executed on behalf of a client, are the only ones that must allways start from a state satisfying the invariant and leave a state satisfying the invariant; there is no such rule for unqualified calls of the form f(...), which are not directly executed by clients but only serve as auxiliary tools for carrying out the needs of qualified calls." Better quotes may be somewhwere, these are just some i dug out. The reasoning is as you can see quite simple. I think the relaxation is justified since its relatively easy for a class to maintain its invariant compared to the same class trying to maintain the invariant of other classes; at least the scope is easier to grasp. But that's just one way of looking at it....
Aug 22 2004
prev sibling next sibling parent "Matthew" <admin.hat stlsoft.dot.org> writes:
"Andy Friesen" <andy ikagames.com> wrote in message
news:cg830p$289n$1 digitaldaemon.com...
      struct Foo {
          int x() {
              return _x;
          }
          void x(int i) {
              _x = i;
          }
          int _x;

          invariant {
              assert (x != 13); // guard against bad luck
          }
      }

      int main() {
          Foo foo;
          foo.x = 999;
          int x = foo.x;
          printf("%i\n", x);
          return x;
      }

 Setting foo.x causes a stack overflow because Foo.x checks the
 invariant, which calls Foo.x() which itself cheks the invariant again,
 ad infinitum.

 This isn't really a bug per se, but I somehow doubt that anybody would
 ever expect or desire this behaviour.

 It would be nice if method calls didn't trigger invariant tests from
 within the invariant itself.  If that's unfeasable, a good error message
 would be sufficient.

 I think it's pretty safe to say that this one is /way/ too subtle to
 leave as-is. :)
I ran into this myself with the first version of std.recls. Can't remember OTTOMH how I got around it, but it was basically an avoidance thing, rather than anything particularly clever.
Aug 21 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Andy Friesen" <andy ikagames.com> wrote in message
news:cg830p$289n$1 digitaldaemon.com...
 Setting foo.x causes a stack overflow because Foo.x checks the
 invariant, which calls Foo.x() which itself cheks the invariant again,
 ad infinitum.
Yes. Invariant checks are placed at the beginning and end of all public member functions.
 This isn't really a bug per se, but I somehow doubt that anybody would
 ever expect or desire this behaviour.

 It would be nice if method calls didn't trigger invariant tests from
 within the invariant itself.  If that's unfeasable, a good error message
 would be sufficient.
Unfortunately, this is infeasible to implement. There's no way that the invariant compilation can check all possible paths that might result in a call to another public member function, which will cause the infinite recursion. The other way is for the invariant itself to see if it is nested inside a call to itself. This will require either walking the stack, or adding a flag to the object instance data. The former is expensive, and the latter is a problem since the instance layout is already fixed.
 I think it's pretty safe to say that this one is /way/ too subtle to
 leave as-is. :)
I'll have to document this better.
Aug 22 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Sun, 22 Aug 2004 18:13:30 -0700, Walter <newshound digitalmars.com> 
wrote:
 "Andy Friesen" <andy ikagames.com> wrote in message
 news:cg830p$289n$1 digitaldaemon.com...
 Setting foo.x causes a stack overflow because Foo.x checks the
 invariant, which calls Foo.x() which itself cheks the invariant again,
 ad infinitum.
Yes. Invariant checks are placed at the beginning and end of all public member functions.
 This isn't really a bug per se, but I somehow doubt that anybody would
 ever expect or desire this behaviour.

 It would be nice if method calls didn't trigger invariant tests from
 within the invariant itself.  If that's unfeasable, a good error message
 would be sufficient.
Unfortunately, this is infeasible to implement. There's no way that the invariant compilation can check all possible paths that might result in a call to another public member function, which will cause the infinite recursion. The other way is for the invariant itself to see if it is nested inside a call to itself. This will require either walking the stack, or adding a flag to the object instance data. The former is expensive, and the latter is a problem since the instance layout is already fixed.
Can't you simply use a flag, i.e. (lines marked with // are compiler generated) class A { int _i; int i() { return _i; } invariant { assert(i != 13); } } or something?
 I think it's pretty safe to say that this one is /way/ too subtle to
 leave as-is. :)
I'll have to document this better.
Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 22 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsc5vqgsx5a2sq9 digitalmars.com...
 On Sun, 22 Aug 2004 18:13:30 -0700, Walter <newshound digitalmars.com>
 wrote:
 "Andy Friesen" <andy ikagames.com> wrote in message
 news:cg830p$289n$1 digitaldaemon.com...
 Setting foo.x causes a stack overflow because Foo.x checks the
 invariant, which calls Foo.x() which itself cheks the invariant again,
 ad infinitum.
Yes. Invariant checks are placed at the beginning and end of all public member functions.
 This isn't really a bug per se, but I somehow doubt that anybody would
 ever expect or desire this behaviour.

 It would be nice if method calls didn't trigger invariant tests from
 within the invariant itself.  If that's unfeasable, a good error
message
 would be sufficient.
Unfortunately, this is infeasible to implement. There's no way that the invariant compilation can check all possible paths that might result in
a
 call to another public member function, which will cause the infinite
 recursion. The other way is for the invariant itself to see if it is
 nested
 inside a call to itself. This will require either walking the stack, or
 adding a flag to the object instance data. The former is expensive, and
 the
 latter is a problem since the instance layout is already fixed.
Can't you simply use a flag, i.e. (lines marked with // are compiler generated)
That's the "add a flag to the object instance data" method. The trouble there is it will add size to every object.
Aug 23 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cgc9l7$24u2$1 digitaldaemon.com>, Walter says...

That's the "add a flag to the object instance data" method. The trouble
there is it will add size to every object.
And don't forget to clear that flag in the event of an exception's being thrown! Maybe you'd better make it a flag wrapped in an auto class. I don't know too much about how vtbls are implemented, but I would have thought that my debug build (two-entry-points for each member function, two-vtbls) idea would solve the problem with no additional exception handling required. It would add size to each class (a second vtbl, and glue-code for each function), but only in a debug build. Jill
Aug 23 2004
parent "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgck5t$2am9$1 digitaldaemon.com...
 In article <cgc9l7$24u2$1 digitaldaemon.com>, Walter says...

That's the "add a flag to the object instance data" method. The trouble
there is it will add size to every object.
And don't forget to clear that flag in the event of an exception's being
thrown!
 Maybe you'd better make it a flag wrapped in an auto class.

 I don't know too much about how vtbls are implemented, but I would have
thought
 that my debug build (two-entry-points for each member function, two-vtbls)
idea
 would solve the problem with no additional exception handling required. It
would
 add size to each class (a second vtbl, and glue-code for each function),
but
 only in a debug build.
It would not solve the problem. All the invariant would have to do is: void bar(Foo f) { f.func(); } class Foo { void func(Foo f) { } invariant { bar(this); } }
Aug 23 2004
prev sibling next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Walter wrote:

It would be nice if method calls didn't trigger invariant tests from
within the invariant itself.  If that's unfeasable, a good error message
would be sufficient.
Unfortunately, this is infeasible to implement. There's no way that the invariant compilation can check all possible paths that might result in a call to another public member function, which will cause the infinite recursion. The other way is for the invariant itself to see if it is nested inside a call to itself. This will require either walking the stack, or adding a flag to the object instance data. The former is expensive, and the latter is a problem since the instance layout is already fixed.
One thing I noticed is that invariant calls are handled by the caller, not the callee. Would it be feasable for internal method calls to skip the invariant test? This would be reasonable behaviour in the majority of cases, I would think. In those cases when it isn't, an explicit "assert(this);" is easy enough to write. -- andy
Aug 22 2004
next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Andy Friesen wrote:

 One thing I noticed is that invariant calls are handled by the caller, 
 not the callee.
 
 Would it be feasable for internal method calls to skip the invariant 
 test?  This would be reasonable behaviour in the majority of cases, I 
 would think.  In those cases when it isn't, an explicit "assert(this);" 
 is easy enough to write.
errr... I noticed wrong. The invariant is called within the method, not by the caller. Is it feasable to turn this around? -- andy
Aug 22 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cgbl12$1mf1$1 digitaldaemon.com>, Andy Friesen says...

errr... I noticed wrong.  The invariant is called within the method, not 
by the caller. Is it feasable to turn this around?
Complicated. Consider this: Suppose I release a library L, and someone else develops an application A which statically links with L. Let us suppose that L has been through its developmental phase, so now there's only a release build of it. A, on the other hand, is still being developed, and so is being compiled in debug build. So when A calls a function in L, I would /hope/ that the "in" preconditions of functions within L will still be tested. (Preconditions are there, after all, to help callers find bugs in /their/ code). This can only happen if preconditions are handled by the caller, not the callee. For class invariants, it should be allowed for the invariant to be temporarily broken when inside member functions, and therefore it should only be checked when called from outside. One way to achieve this is by placing responsibility on the callee. /However/, once the callee (the library L) goes into release build, the invariant should not be checked at all, regardless of the debug/release state of the caller (the application A). Think about it. One solution is to give two entry-points to each member function (but only in debug build) - one for use when the function is called from inside the class (i.e. for calls of the form f(...)) which does not call the invariant test, and one for functions called from outside the class (i.e. for calls on the form a.f(...)) which does. In a release build, those two entry points collapse to one. I think this could be done by giving each class a separate "vtbl for internal calls" in a debug build. Only for postconditions does it makes sense for the callee to perform the test regardless of whence the call came. Arcane Jill
Aug 22 2004
parent reply "Matthew" <admin.hat stlsoft.dot.org> writes:
In debug mode, each class instance should have "associated with it" (defined
below) a flag that indicates whether we're
"in" that instance's methods, and invariants are then suspended.

If it's not appropriate to have the flag in each instance, then each classinfo
could have a debug-time mapping between
instance (pointer) and the flag.

I'm guessing that there are complications to this, but this may at least be a
step forward.

Discuss ...

"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgc32m$201f$1 digitaldaemon.com...
 In article <cgbl12$1mf1$1 digitaldaemon.com>, Andy Friesen says...

errr... I noticed wrong.  The invariant is called within the method, not
by the caller. Is it feasable to turn this around?
Complicated. Consider this: Suppose I release a library L, and someone else develops an application A which statically links with L. Let us suppose that L has been through its developmental phase, so now there's only a release build of it. A, on the other hand, is still being developed, and so is being compiled in debug build. So when A calls a function in L, I would /hope/ that the "in" preconditions of functions within L will still be tested. (Preconditions are there, after all, to help callers find bugs in /their/ code). This can only happen if preconditions are handled by the caller, not the callee. For class invariants, it should be allowed for the invariant to be temporarily broken when inside member functions, and therefore it should only be checked when called from outside. One way to achieve this is by placing responsibility on the callee. /However/, once the callee (the library L) goes into release build, the invariant should not be checked at all, regardless of the debug/release state of the caller (the application A). Think about it. One solution is to give two entry-points to each member function (but only in debug build) - one for use when the function is called from inside the class (i.e. for calls of the form f(...)) which does not call the invariant test, and one for functions called from outside the class (i.e. for calls on the form a.f(...)) which does. In a release build, those two entry points collapse to one. I think this could be done by giving each class a separate "vtbl for internal calls" in a debug build. Only for postconditions does it makes sense for the callee to perform the test regardless of whence the call came. Arcane Jill
Aug 23 2004
parent reply "antiAlias" <fu bar.com> writes:
The flag approach has merit, but needs to be appropriately
exception-checked; worse, it's not thread safe. You'd potentially need to
use thread locals instead.

A different approach would be for the function-calling protocol to be
modified slightly in debug mode: a flag gets pushed onto the stack by the
caller, indicating whether or not invariants should execute. Those methods
within the class itself push a 'false' for their own methods, while all
external calls push a 'true' instead.

No exception issues; no thread-safety issues.





"Matthew" <admin.hat stlsoft.dot.org> wrote in message
news:cgc57s$21ko$1 digitaldaemon.com...
 In debug mode, each class instance should have "associated with it"
(defined below) a flag that indicates whether we're
 "in" that instance's methods, and invariants are then suspended.

 If it's not appropriate to have the flag in each instance, then each
classinfo could have a debug-time mapping between
 instance (pointer) and the flag.

 I'm guessing that there are complications to this, but this may at least
be a step forward.
 Discuss ...

 "Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cgc32m$201f$1 digitaldaemon.com...
 In article <cgbl12$1mf1$1 digitaldaemon.com>, Andy Friesen says...

errr... I noticed wrong.  The invariant is called within the method,
not
by the caller. Is it feasable to turn this around?
Complicated. Consider this: Suppose I release a library L, and someone else develops an application
A which
 statically links with L. Let us suppose that L has been through its
 developmental phase, so now there's only a release build of it. A, on
the other
 hand, is still being developed, and so is being compiled in debug build.

 So when A calls a function in L, I would /hope/ that the "in"
preconditions of
 functions within L will still be tested. (Preconditions are there, after
all, to
 help callers find bugs in /their/ code). This can only happen if
preconditions
 are handled by the caller, not the callee.

 For class invariants, it should be allowed for the invariant to be
temporarily
 broken when inside member functions, and therefore it should only be
checked
 when called from outside. One way to  achieve this is by placing
responsibility
 on the callee. /However/, once the callee (the library L) goes into
release
 build, the invariant should not be checked at all, regardless of the
 debug/release state of the caller (the application A). Think about it.
One
 solution is to give two entry-points to each member function (but only
in debug
 build) - one for use when the function is called from inside the class
(i.e. for
 calls of the form f(...)) which does not call the invariant test, and
one for
 functions called from outside the class (i.e. for calls on the form
a.f(...))
 which does. In a release build, those two entry points collapse to one.
I think
 this could be done by giving each class a separate "vtbl for internal
calls" in
 a debug build.

 Only for postconditions does it makes sense for the callee to perform
the test
 regardless of whence the call came.

 Arcane Jill
Aug 23 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"antiAlias" <fu bar.com> wrote in message
news:cgd6r6$2jn8$1 digitaldaemon.com...
 The flag approach has merit, but needs to be appropriately
 exception-checked; worse, it's not thread safe. You'd potentially need to
 use thread locals instead.

 A different approach would be for the function-calling protocol to be
 modified slightly in debug mode: a flag gets pushed onto the stack by the
 caller, indicating whether or not invariants should execute. Those methods
 within the class itself push a 'false' for their own methods, while all
 external calls push a 'true' instead.

 No exception issues; no thread-safety issues.
But then you cannot mix and match debug builds with non-debug builds, significantly reducing their utility.
Aug 23 2004
parent reply "antiAlias" <fu bar.com> writes:
Yeah; I can see how that would be desirable. Another approach is for the
invariant to check the return address; if it's within the range of the
current module then don't execute. That way, all the effort is contained and
performed by the invariant itself. You might even expose that as an optional
invariant-type ...


"Walter" <newshound digitalmars.com> wrote in message
news:cgdbvt$2nht$2 digitaldaemon.com...
 "antiAlias" <fu bar.com> wrote in message
 news:cgd6r6$2jn8$1 digitaldaemon.com...
 The flag approach has merit, but needs to be appropriately
 exception-checked; worse, it's not thread safe. You'd potentially need
to
 use thread locals instead.

 A different approach would be for the function-calling protocol to be
 modified slightly in debug mode: a flag gets pushed onto the stack by
the
 caller, indicating whether or not invariants should execute. Those
methods
 within the class itself push a 'false' for their own methods, while all
 external calls push a 'true' instead.

 No exception issues; no thread-safety issues.
But then you cannot mix and match debug builds with non-debug builds, significantly reducing their utility.
Aug 23 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"antiAlias" <fu bar.com> wrote in message
news:cgdhln$2que$1 digitaldaemon.com...
 Yeah; I can see how that would be desirable. Another approach is for the
 invariant to check the return address; if it's within the range of the
 current module then don't execute. That way, all the effort is contained
and
 performed by the invariant itself. You might even expose that as an
optional
 invariant-type ...
I don't see how any calls to the invariant can work, then <g>.
Aug 23 2004
parent reply "antiAlias" <fu bar.com> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cgdm0d$2ub8$1 digitaldaemon.com...
 I don't see how any calls to the invariant can work, then <g>.
Don't follow you ... if the invariant knows the range of executable addresses within the containing module, it can accept calls from without the range whilst ignoring those from within. The compiler would have to setup begin & end labels for the code generated within the containing module (or something like that), the linker/loader would patch them in the usual manner to represent real addresses, and the invariant would check the address of said labels against the provided return-address (from the stack) to see if it's being called from within the same (containing) module. Calls from a different module will have a return-address that is outside the range of the called module and, therefore, the invariant would execute. Am I missing something here?
Aug 23 2004
next sibling parent "antiAlias" <fu bar.com> writes:
I am, of course, using the term 'module' to loosely mean D source-file.

"antiAlias" <fu bar.com> wrote in message
news:cgdmnf$2v2t$1 digitaldaemon.com...
 "Walter" <newshound digitalmars.com> wrote in message
 news:cgdm0d$2ub8$1 digitaldaemon.com...
 I don't see how any calls to the invariant can work, then <g>.
Don't follow you ... if the invariant knows the range of executable addresses within the containing module, it can accept calls from without
the
 range whilst ignoring those from within.

 The compiler would have to setup begin & end labels for the code generated
 within the containing module (or something like that), the linker/loader
 would patch them in the usual manner to represent real addresses, and the
 invariant would check the address of said labels against the provided
 return-address (from the stack) to see if it's being called from within
the
 same (containing) module.

 Calls from a different module will have a return-address that is outside
the
 range of the called module and, therefore, the invariant would execute.

 Am I missing something here?
Aug 23 2004
prev sibling parent reply "antiAlias" <fu bar.com> writes:
Ahhh ... right. I am missing something. The invariant is invoked by the
callee, not the caller ... whoops. Is there a known stack-frame you can peek
at? You'd only be looking one frame removed (the call immediately prior to
the invariant call) ... I think it's fair to say that circuitous routes
/will/ invoke the invariant.


"antiAlias" <fu bar.com> wrote in message
news:cgdmnf$2v2t$1 digitaldaemon.com...
 "Walter" <newshound digitalmars.com> wrote in message
 news:cgdm0d$2ub8$1 digitaldaemon.com...
 I don't see how any calls to the invariant can work, then <g>.
Don't follow you ... if the invariant knows the range of executable addresses within the containing module, it can accept calls from without
the
 range whilst ignoring those from within.

 The compiler would have to setup begin & end labels for the code generated
 within the containing module (or something like that), the linker/loader
 would patch them in the usual manner to represent real addresses, and the
 invariant would check the address of said labels against the provided
 return-address (from the stack) to see if it's being called from within
the
 same (containing) module.

 Calls from a different module will have a return-address that is outside
the
 range of the called module and, therefore, the invariant would execute.

 Am I missing something here?
Aug 23 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"antiAlias" <fu bar.com> wrote in message
news:cgdnbq$2vhn$1 digitaldaemon.com...
 Ahhh ... right. I am missing something. The invariant is invoked by the
 callee, not the caller ... whoops. Is there a known stack-frame you can
peek
 at? You'd only be looking one frame removed (the call immediately prior to
 the invariant call) ... I think it's fair to say that circuitous routes
 /will/ invoke the invariant.
It starts getting fairly complicated handling all the special cases when one starts doing that.
Aug 23 2004
parent reply "antiAlias" <fu bar.com> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cgdvem$351$1 digitaldaemon.com...
 "antiAlias" <fu bar.com> wrote in message
 news:cgdnbq$2vhn$1 digitaldaemon.com...
 Ahhh ... right. I am missing something. The invariant is invoked by the
 callee, not the caller ... whoops. Is there a known stack-frame you can
peek
 at? You'd only be looking one frame removed (the call immediately prior
to
 the invariant call) ... I think it's fair to say that circuitous routes
 /will/ invoke the invariant.
It starts getting fairly complicated handling all the special cases when
one
 starts doing that.
That sounds intriguing. I can imagine special-cases via support for other languages, but from within D itself, I'm at a loss to speculate. Care to elaborate a little? Special-cases aside; would this not be a viable solution, given that it avoids exception and thread-safety issues?
Aug 23 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"antiAlias" <fu bar.com> wrote in message
news:cgeoip$fcn$1 digitaldaemon.com...
 "Walter" <newshound digitalmars.com> wrote in message
 news:cgdvem$351$1 digitaldaemon.com...
 "antiAlias" <fu bar.com> wrote in message
 news:cgdnbq$2vhn$1 digitaldaemon.com...
 Ahhh ... right. I am missing something. The invariant is invoked by
the
 callee, not the caller ... whoops. Is there a known stack-frame you
can
 peek
 at? You'd only be looking one frame removed (the call immediately
prior
 to
 the invariant call) ... I think it's fair to say that circuitous
routes
 /will/ invoke the invariant.
It starts getting fairly complicated handling all the special cases when
one
 starts doing that.
That sounds intriguing. I can imagine special-cases via support for other languages, but from within D itself, I'm at a loss to speculate. Care to elaborate a little? Special-cases aside; would this not be a viable solution, given that it avoids exception and thread-safety issues?
The compiler generates different stack frames based on things like optimization settings, debugging, exception handling, etc. Then there are the multiple calling conventions, each with their own way of doing things.
Aug 24 2004
parent "antiAlias" <fu bar.com> writes:
"Walter" <newshound digitalmars.com> wrote
 The compiler generates different stack frames based on things like
 optimization settings, debugging, exception handling, etc. Then there are
 the multiple calling conventions, each with their own way of doing things.
Thanks!
Aug 24 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Andy Friesen" <andy ikagames.com> wrote in message
news:cgbk5c$1ls4$1 digitaldaemon.com...
 Would it be feasable for internal method calls to skip the invariant
 test?
Yes, but it wouldn't solve the problem. The invariant could still call some external function, which could then call a member function, which would then invoke the invariant agian.
Aug 23 2004
next sibling parent reply "Bent Rasmussen" <exo bent-rasmussen.info> writes:
 Yes, but it wouldn't solve the problem. The invariant could still call 
 some
 external function, which could then call a member function, which would 
 then
 invoke the invariant agian.
I thought about that too, but isn't that acceptable?
Aug 23 2004
parent "Walter" <newshound digitalmars.com> writes:
"Bent Rasmussen" <exo bent-rasmussen.info> wrote in message
news:cgcfrh$28bh$1 digitaldaemon.com...
 Yes, but it wouldn't solve the problem. The invariant could still call
 some
 external function, which could then call a member function, which would
 then
 invoke the invariant agian.
I thought about that too, but isn't that acceptable?
It'll result in infinite recursion.
Aug 23 2004
prev sibling parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <cgc9l8$24u2$2 digitaldaemon.com>, Walter says...
"Andy Friesen" <andy ikagames.com> wrote in message
news:cgbk5c$1ls4$1 digitaldaemon.com...
 Would it be feasable for internal method calls to skip the invariant
 test?
Yes, but it wouldn't solve the problem. The invariant could still call some external function, which could then call a member function, which would then invoke the invariant agian.
That's okay. I don't mind that being an error. Jill
Aug 23 2004
prev sibling parent reply Nick <Nick_member pathlink.com> writes:
In article <cgbgqa$1jql$1 digitaldaemon.com>, Walter says...
Unfortunately, this is infeasible to implement. There's no way that the
invariant compilation can check all possible paths that might result in a
call to another public member function, which will cause the infinite
recursion. The other way is for the invariant itself to see if it is nested
inside a call to itself. This will require either walking the stack, or
adding a flag to the object instance data. The former is expensive, and the
latter is a problem since the instance layout is already fixed.
There is a third option, and that is to have the the flags stored outside the object instance itself. This could for example be done using an associative array with 'this' as an index, see example below. (By the way, using return; inside invariants (and unittests?) seems to work, it should probably be documented.) Is this doable, or is it way too expensive? Nick
Aug 23 2004
parent "Vathix" <vathixSpamFix dprogramming.com> writes:
"Nick" <Nick_member pathlink.com> wrote in message
news:cgcjin$2ad2$1 digitaldaemon.com...
 In article <cgbgqa$1jql$1 digitaldaemon.com>, Walter says...
Unfortunately, this is infeasible to implement. There's no way that the
invariant compilation can check all possible paths that might result in a
call to another public member function, which will cause the infinite
recursion. The other way is for the invariant itself to see if it is
nested
inside a call to itself. This will require either walking the stack, or
adding a flag to the object instance data. The former is expensive, and
the
latter is a problem since the instance layout is already fixed.
There is a third option, and that is to have the the flags stored outside
the
 object instance itself. This could for example be done using an
associative
 array with 'this' as an index, see example below. (By the way, using
return;
 inside invariants (and unittests?) seems to work, it should probably be
 documented.)





































 Is this doable, or is it way too expensive?

 Nick
It's not thread safe. All member function calls would have to be synchronized, eek! I wouldn't mind having the compiler tack on an extra bit to my class; it's just debug code.
Aug 23 2004