digitalmars.D.bugs - Nasty invariant bug
- Andy Friesen <andy ikagames.com> Aug 21 2004
- Arcane Jill <Arcane_member pathlink.com> Aug 21 2004
- "Matthew" <admin.hat stlsoft.dot.org> Aug 21 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Aug 22 2004
- "Matthew" <admin.hat stlsoft.dot.org> Aug 21 2004
- "Walter" <newshound digitalmars.com> Aug 22 2004
- Regan Heath <regan netwin.co.nz> Aug 22 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- Arcane Jill <Arcane_member pathlink.com> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- Andy Friesen <andy ikagames.com> Aug 22 2004
- Andy Friesen <andy ikagames.com> Aug 22 2004
- Arcane Jill <Arcane_member pathlink.com> Aug 22 2004
- "Matthew" <admin.hat stlsoft.dot.org> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- "antiAlias" <fu bar.com> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 24 2004
- "antiAlias" <fu bar.com> Aug 24 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- "Bent Rasmussen" <exo bent-rasmussen.info> Aug 23 2004
- "Walter" <newshound digitalmars.com> Aug 23 2004
- Arcane Jill <Arcane_member pathlink.com> Aug 23 2004
- Nick <Nick_member pathlink.com> Aug 23 2004
- "Vathix" <vathixSpamFix dprogramming.com> Aug 23 2004
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
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: # /* temporarily break invariant */ # _x = x; // invoke a getter function # /* restore invariant */ 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
"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: # /* temporarily break invariant */ # _x = x; // invoke a getter function # /* restore invariant */ 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
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
"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
"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
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 { # bool insideInvariant; int _i; int i() { # if (!insideInvariant) invariant(); return _i; # if (!insideInvariant) invariant(); } invariant { # insideInvariant = true; assert(i != 13); # insideInvariant = false; } } (lines with # are compiler generated) 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
"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
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
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
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
"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
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
that my debug build (two-entry-points for each member function, two-vtbls)
would solve the problem with no additional exception handling required. It
add size to each class (a second vtbl, and glue-code for each function),
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
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
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
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
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
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"
"in" that instance's methods, and invariants are then suspended. If it's not appropriate to have the flag in each instance, then each
instance (pointer) and the flag. I'm guessing that there are complications to this, but this may at least
Discuss ... "Arcane Jill" <Arcane_member pathlink.com> wrote in message
In article <cgbl12$1mf1$1 digitaldaemon.com>, Andy Friesen says...errr... I noticed wrong. The invariant is called within the method,
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
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
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"
functions within L will still be tested. (Preconditions are there, after
help callers find bugs in /their/ code). This can only happen if
are handled by the caller, not the callee. For class invariants, it should be allowed for the invariant to be
broken when inside member functions, and therefore it should only be
when called from outside. One way to achieve this is by placing
on the callee. /However/, once the callee (the library L) goes into
build, the invariant should not be checked at all, regardless of the debug/release state of the caller (the application A). Think about it.
solution is to give two entry-points to each member function (but only
build) - one for use when the function is called from inside the class
calls of the form f(...)) which does not call the invariant test, and
functions called from outside the class (i.e. for calls on the form
which does. In a release build, those two entry points collapse to one.
this could be done by giving each class a separate "vtbl for internal
a debug build. Only for postconditions does it makes sense for the callee to perform
regardless of whence the call came. Arcane Jill
Aug 23 2004
"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
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
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
caller, indicating whether or not invariants should execute. Those
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
"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
performed by the invariant itself. You might even expose that as an
invariant-type ...
I don't see how any calls to the invariant can work, then <g>.
Aug 23 2004
"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
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
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
same (containing) module. Calls from a different module will have a return-address that is outside
range of the called module and, therefore, the invariant would execute. Am I missing something here?
Aug 23 2004
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
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
same (containing) module. Calls from a different module will have a return-address that is outside
range of the called module and, therefore, the invariant would execute. Am I missing something here?
Aug 23 2004
"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
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
"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
at? You'd only be looking one frame removed (the call immediately prior
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
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
"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
callee, not the caller ... whoops. Is there a known stack-frame you
peekat? You'd only be looking one frame removed (the call immediately
tothe invariant call) ... I think it's fair to say that circuitous
/will/ invoke the invariant.
It starts getting fairly complicated handling all the special cases when
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
"Walter" <newshound digitalmars.com> wroteThe 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
"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
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
"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
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
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.) # import std.stdio; # # int g_invariantFlags[Object]; # # class A # { # int xx, yy; # # int x() {return xx;} # int y() {return yy;} # # void set(int i) { xx = i; yy = i; } # # invariant # { # // Compiler generated code # if( g_invariantFlags[this] != 0 ) # return; # g_invariantFlags[this] = 1; # // End of compiler generated code # # assert(x == y); # # // Compiler generated code # g_invariantFlags[this] = 0; # // End of compiler generated code # } # } # # void main() # { # A a = new A; # a.set(10); # writefln(a.y()); # } Is this doable, or is it way too expensive? Nick
Aug 23 2004
"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
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
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
object instance itself. This could for example be done using an
array with 'this' as an index, see example below. (By the way, using
inside invariants (and unittests?) seems to work, it should probably be documented.) # import std.stdio; # # int g_invariantFlags[Object]; # # class A # { # int xx, yy; # # int x() {return xx;} # int y() {return yy;} # # void set(int i) { xx = i; yy = i; } # # invariant # { # // Compiler generated code # if( g_invariantFlags[this] != 0 ) # return; # g_invariantFlags[this] = 1; # // End of compiler generated code # # assert(x == y); # # // Compiler generated code # g_invariantFlags[this] = 0; # // End of compiler generated code # } # } # # void main() # { # A a = new A; # a.set(10); # writefln(a.y()); # } 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









"Bent Rasmussen" <exo bent-rasmussen.info> 