digitalmars.D - Invariants are useless the way they are defined
- deadalnix (18/18) Aug 25 2013 As they are defined now, invariant are plain useless. I find
- bearophile (5/6) Aug 25 2013 Don't forget to add this issue to Bugzilla. So we will keep
- deadalnix (4/10) Aug 25 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10889
- bearophile (7/9) Aug 25 2013 It's not a bug, it's an enhancement, because according to you it
- Andrei Alexandrescu (10/28) Aug 25 2013 But this is what they are for - check the state of an object upon access...
- deadalnix (56/100) Aug 25 2013 Yes that can be worked around, but the code ends up more and more
- Jonathan M Davis (17/40) Aug 25 2013 What drives me nuts is the fact that it gets checked when opAssign is ca...
- Andrei Alexandrescu (3/33) Aug 25 2013 emplace() should not call assignment for elaborate types.
- Paolo Invernizzi (8/53) Aug 26 2013 A pull for that is ready, and need review...
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (16/19) Aug 25 2013 That's a very interesting observation. Could the solution be running the...
- deadalnix (7/26) Aug 25 2013 No need for runtime check :D
- Timon Gehr (3/6) Aug 26 2013 The issue with this is that the invariant is not part of the public
- H. S. Teoh (10/18) Aug 26 2013 The fact that contracts are not part of the public interface greatly
- Dmitry Olshansky (4/19) Aug 26 2013 --
- Timon Gehr (3/18) Aug 26 2013 (This was not a statement about Ds implementation. The invariant is by
- H. S. Teoh (5/29) Aug 26 2013 Oh. I misunderstood what you said, then.
- Andrei Alexandrescu (3/15) Aug 26 2013 You can attach contracts to an interface. What else would need to be don...
- H. S. Teoh (16/33) Aug 26 2013 [...]
- Dicebot (4/7) Aug 26 2013 I thought it is a common practice - contracts are not the only
- Christopher Bergqvist (37/44) Aug 26 2013 Why not have the compiler generate internal versions of all
- H. S. Teoh (9/51) Aug 26 2013 I like this idea, actually. In fact, it did occur to me when skimming
- Davidson Corry (49/67) Aug 26 2013 If you will indulge a D newbie, lurker and former Eiffelist:
- Dicebot (5/6) Aug 26 2013 By the way it makes me think that invariant should run via CTFE
- Peter Alexander (2/8) Aug 26 2013 Not all invariants can run at compile time.
- Dicebot (7/17) Aug 26 2013 True, but it will at least make a notable cluster of potential
- Peter Williams (12/30) Aug 26 2013 I just discovered another (surprising to me) shortcoming with the
As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.
Aug 25 2013
deadalnix:As they are defined now, invariant are plain useless.Don't forget to add this issue to Bugzilla. So we will keep memory of it. Bye, bearophile
Aug 25 2013
On Sunday, 25 August 2013 at 13:44:49 UTC, bearophile wrote:deadalnix:http://d.puremagic.com/issues/show_bug.cgi?id=10889 Done but I'm not sure this is a bug. This work as designed, it is simply that the design is improvable :DAs they are defined now, invariant are plain useless.Don't forget to add this issue to Bugzilla. So we will keep memory of it. Bye, bearophile
Aug 25 2013
deadalnix:Done but I'm not sure this is a bug. This work as designed, it is simply that the design is improvable :DIt's not a bug, it's an enhancement, because according to you it could be a small design bug. Don't forged to add to the bug report good comments coming from Andrei or other people from this thread. Bye, bearophile
Aug 25 2013
On 8/25/13 5:16 AM, deadalnix wrote:As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant.But this is what they are for - check the state of an object upon access through the client-accessible interface. Simple code factorization (use a private method forwarded to by the public one) takes care of this situation.For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls.I don't get this case. Code?Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.There might be something here, but more like a performance problem. I do see how calls of public methods from an invariant could cause trouble, but I'd say that's a problem with the invariant design. Andrei
Aug 25 2013
On Sunday, 25 August 2013 at 16:22:01 UTC, Andrei Alexandrescu wrote:On 8/25/13 5:16 AM, deadalnix wrote:Yes that can be worked around, but the code ends up more and more complex, with many function forwarding to one another. This can become very messy with class hierarchies.As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant.But this is what they are for - check the state of an object upon access through the client-accessible interface. Simple code factorization (use a private method forwarded to by the public one) takes care of this situation.struct Identifiable { union { Symbol sym; Expression expr; Type type; } import d.ast.base : TypeQualifier; import std.bitmanip; mixin(bitfields!( Tag, "tag", 2, TypeQualifier, "qual", 3, uint, "", 3, )); disable this(); // For type inference. this(typeof(null)); this(Identifiable i) { this = i; } this(Symbol s) { tag = Tag.Symbol; sym = s; } this(Expression e) { tag = Tag.Expression; expr = e; } this(QualType qt) { tag = Tag.Type; qual = qt.qualifier; type = qt.type; } } I used to have an invariant that check the sanity of the tagged union, but now I have to double the interface to the bitfield. This one is still fixable by doubling the interface, but the same thing with the bitfield in a super class from another module and you are doomed.For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls.I don't get this case. Code?This is a performance issue as well. But mostly it makes invariant useless in many cases when class hierarchies are in place and probably the worst part is that invariant are not even ensured as they do not trigger when writing to public fields (as triggered by the callee, and we have no callee in this case).Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.There might be something here, but more like a performance problem. I do see how calls of public methods from an invariant could cause trouble, but I'd say that's a problem with the invariant design. Andrei
Aug 25 2013
On Sunday, August 25, 2013 14:16:28 deadalnix wrote:As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.What drives me nuts is the fact that it gets checked when opAssign is called (rather than just when exiting it). That makes it impossible to have a member in an invalid state (either through an init which has an invalid state similar to how floating point values due or because the object hasn't been initialized yet) and have an invariant. At present, emplace will blow up in your face if you try and have an invariant with such types. The result is that I almost never use invariant. It ends up working better to just create a private function which is the invariant and asserting explicitly in every function where you want the check, but most of the time, that's enough of a pain that the result is that it's just not checked. Unfortunately, my enhancement request on that (not having the invariant called on entry to opAssign) was shot down, because there are some opAssigns which require that the previous state be valid rather than just replacing it (which means that what you really need is a way to declare an invariant but disable it in certain places). - Jonathan M Davis
Aug 25 2013
On 8/25/13 2:56 PM, Jonathan M Davis wrote:On Sunday, August 25, 2013 14:16:28 deadalnix wrote:emplace() should not call assignment for elaborate types. AndreiAs they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.What drives me nuts is the fact that it gets checked when opAssign is called (rather than just when exiting it). That makes it impossible to have a member in an invalid state (either through an init which has an invalid state similar to how floating point values due or because the object hasn't been initialized yet) and have an invariant. At present, emplace will blow up in your face if you try and have an invariant with such types.
Aug 25 2013
On Sunday, 25 August 2013 at 23:02:29 UTC, Andrei Alexandrescu wrote:On 8/25/13 2:56 PM, Jonathan M Davis wrote:A pull for that is ready, and need review... Just by coincidence I've hit this problem with emplace and invariant and assignment just yesterday, and talked with monarch dodra about the very same issue. https://github.com/D-Programming-Language/phobos/pull/1082 - Paolo InvernizziOn Sunday, August 25, 2013 14:16:28 deadalnix wrote:emplace() should not call assignment for elaborate types. AndreiAs they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.What drives me nuts is the fact that it gets checked when opAssign is called (rather than just when exiting it). That makes it impossible to have a member in an invalid state (either through an init which has an invalid state similar to how floating point values due or because the object hasn't been initialized yet) and have an invariant. At present, emplace will blow up in your face if you try and have an invariant with such types.
Aug 26 2013
On 08/25/2013 05:16 AM, deadalnix wrote:The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant.That's a very interesting observation. Could the solution be running the invariant only once, at the outermost public function call? Hm... It would have to be a runtime feature then, right? Every public function would have calls to the invariant but those calls would have to be elided at runtime. I think... Here is another interesting observation: It is acceptable and quite normal that the object is in limbo state during a public member function. As a consequence, any function that operates on the object must use the object in a write-only fashion during that time frame. This is true even for non-member functions that the object is passed to. So, in theory, even a logging function cannot use the object. Hm... I jokingly propose a new function parameter attribute: 'static out', meaning write-only. ('out' already has a conflicting meaning and 'static' is not overloaded enough yet. :p) Ali
Aug 25 2013
On Monday, 26 August 2013 at 06:14:02 UTC, Ali Çehreli wrote:On 08/25/2013 05:16 AM, deadalnix wrote:No need for runtime check :D We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The problem is that invariant are checked at thebeginning/end on publicfunction calls. As a consequence, it is impossible to use anypublicmethod in an invariant.That's a very interesting observation. Could the solution be running the invariant only once, at the outermost public function call? Hm... It would have to be a runtime feature then, right? Every public function would have calls to the invariant but those calls would have to be elided at runtime. I think...Here is another interesting observation: It is acceptable and quite normal that the object is in limbo state during a public member function. As a consequence, any function that operates on the object must use the object in a write-only fashion during that time frame. This is true even for non-member functions that the object is passed to. So, in theory, even a logging function cannot use the object. Hm...The question of constness of invariant/contracts has been raised, I do think the object should be const, but Andrei think otherwise.
Aug 25 2013
On 08/26/2013 08:49 AM, deadalnix wrote:We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:On 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. But this isn't the first time this is brought up, and it still seems no solution is in sight. :-( T -- Nothing in the world is more distasteful to a man than to take the path that leads to himself. -- Herman HesseWe simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
26-Aug-2013 20:47, H. S. Teoh пишет:On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:s/solution/implementation/On 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. But this isn't the first time this is brought up, and it still seems no solution is in sight. :-(We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.T-- Dmitry Olshansky
Aug 26 2013
On 08/26/2013 06:47 PM, H. S. Teoh wrote:On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:(This was not a statement about Ds implementation. The invariant is by design a place to state facts about implementation details.)On 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. But this isn't the first time this is brought up, and it still seems no solution is in sight. :-( TWe simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
On Mon, Aug 26, 2013 at 07:20:21PM +0200, Timon Gehr wrote:On 08/26/2013 06:47 PM, H. S. Teoh wrote:Oh. I misunderstood what you said, then. T -- The two rules of success: 1. Don't tell everything you know. -- YHLOn Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:(This was not a statement about Ds implementation. The invariant is by design a place to state facts about implementation details.)On 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. But this isn't the first time this is brought up, and it still seems no solution is in sight. :-( TWe simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
On 8/26/13 9:47 AM, H. S. Teoh wrote:On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:You can attach contracts to an interface. What else would need to be done? AndreiOn 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it.We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
On Mon, Aug 26, 2013 at 11:01:55AM -0700, Andrei Alexandrescu wrote:On 8/26/13 9:47 AM, H. S. Teoh wrote:[...] Sorry, I meant the API, not the OO 'interface'. IIRC, there is a problem with precompiled shared libraries, because the contracts are compiled into the callee rather than the caller, so there is no way for user code to specify whether or not the contracts will be compiled in. If the library was compiled with -release, then no contracts will be included even if the user code was compiled without -release; if the library was compiled without -release, then the contracts will be included even if the user code was compiled with -release. This forces the library vendor to have to ship two versions of the binaries, one compiled with -release, one not. T -- Кто везде - тот нигде.On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:You can attach contracts to an interface. What else would need to be done?On 08/26/2013 08:49 AM, deadalnix wrote:The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it.We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called.The issue with this is that the invariant is not part of the public interface.
Aug 26 2013
On Monday, 26 August 2013 at 18:15:25 UTC, H. S. Teoh wrote:This forces the library vendor to have to ship two versions of the binaries, one compiled with -release, one not.I thought it is a common practice - contracts are not the only troublemakers here, same goes, for example, for debug symbols or bounds checks.
Aug 26 2013
On Monday, 26 August 2013 at 18:25:21 UTC, Dicebot wrote:On Monday, 26 August 2013 at 18:15:25 UTC, H. S. Teoh wrote:Why not have the compiler generate internal versions of all public methods of classes with invariants, which do not perform invariant checking, but contain the actual function body? class Foo { private int bar() const { return 5; } public int baz() { return bar() * bar(); } public int third() { return baz() * 2; } invariant() { assert(bar() < 6); } } ..gets translated into.. class Foo { private int bar() const { return 5; } public int baz() { invariant(); int r = __internal_baz(); invariant(); return r; } private int __internal_baz() { return bar() * bar(); } public int third() { invariant(); int r = __internal_third(); invariant(); return r; } private int __internal_third() { return baz() * 2; } invariant() { assert(bar() < 6); } } And then in release builds simply avoid doing this generation of __internal_ methods, keeping the ABI (int baz() and int third()) the same. A variation would be to have __internal_s call __internal_s: private int __internal_third() { return __internal_baz() * 2; } but that diminishes ease of debugging. Anyway, this kind of code transformation is probably impractical for a number of reasons. I just felt like bikeshedding.This forces the library vendor to have to ship two versions of the binaries, one compiled with -release, one not.I thought it is a common practice - contracts are not the only troublemakers here, same goes, for example, for debug symbols or bounds checks.
Aug 26 2013
On Mon, Aug 26, 2013 at 11:47:07PM +0200, Christopher Bergqvist wrote: [...]Why not have the compiler generate internal versions of all public methods of classes with invariants, which do not perform invariant checking, but contain the actual function body? class Foo { private int bar() const { return 5; } public int baz() { return bar() * bar(); } public int third() { return baz() * 2; } invariant() { assert(bar() < 6); } } ..gets translated into.. class Foo { private int bar() const { return 5; } public int baz() { invariant(); int r = __internal_baz(); invariant(); return r; } private int __internal_baz() { return bar() * bar(); } public int third() { invariant(); int r = __internal_third(); invariant(); return r; } private int __internal_third() { return baz() * 2; } invariant() { assert(bar() < 6); } } And then in release builds simply avoid doing this generation of __internal_ methods, keeping the ABI (int baz() and int third()) the same. A variation would be to have __internal_s call __internal_s: private int __internal_third() { return __internal_baz() * 2; } but that diminishes ease of debugging. Anyway, this kind of code transformation is probably impractical for a number of reasons. I just felt like bikeshedding.I like this idea, actually. In fact, it did occur to me when skimming over this thread. I don't see why this code transformation would be impractical -- D *is* quite big on templates and compile-time code generation, after all. :) T -- If you think you are too small to make a difference, try sleeping in a closed room with a mosquito. -- Jan van Steenbergen
Aug 26 2013
On Monday, 26 August 2013 at 06:14:02 UTC, Ali Çehreli wrote:On 08/25/2013 05:16 AM, deadalnix wrote:If you will indulge a D newbie, lurker and former Eiffelist: In design-by-contract theory, the invariant is part of the definition of a user-defined type. If type T has an invariant, an object t of that type is not a "real T" if it does not meet the invariant while publicly accessible (that is, while it is not in the hands of a member function of class T). For instance, the purpose of the constructor is to establish the invariant -- some Eiffelists argue that this is the *sole* purpose of the constructor, and that constructors which perform initialization beyond that are overstepping their bounds. For another point, T t should be capable of being tested for its invariant (and passing that test) anywhere and at any time it is publicly accessible. It is only a matter of convenience that implementations test the invariant at entry to and exit from public member functions of class T. That convenience relies on the guarantee that *only* T member functions can be allowed to modify the state of object t -- all other operations in the program must treat t as const (and can, of course, *rely* on t being const)... which, in turn, has all kinds of implications about what you can safely make public about a T object. All of this has implications for D's contract guarantees. For instance, you may not design an invariant that fails on T.init (which is the state of t after you call t.clear() ). In other words, D's object model doesn't necessarily match strict DbC theory. And Jonathon Davis's notion of a t that "isn't really a T" but should be acceptable by T.opAssign() also falls outside the theory. Offhand, I can think of two approaches that might address these desires without too badly weakening the invariant's guarantee: + attach an internal "depth gauge" to each T t. At entry to any public T member function, the depth gauge is incremented; at exit, decremented. Invariant is tested *only* when the depth gauge transitions from or to zero: that is, at its transition from "surfaced" (public) to "submerged" (in the hands of T class functions) or vice-versa. This would allow the implementation to elide any invariant tests while T member functions call other functions, even functions that are themselves public T member functions. * an attribute which tags a public T function as accepting "broken Ts". Such a function would be expected to establish the invariant in the object, and test that invariant, at exit, but would *not* test the invariant at entry. These two overlap but not entirely, and I'm still thinking about whether you would want both, or just one or the other. As I say, this is off the top of my head. I hope I have contributed a useful notion, and not just muddied the waters. Thanks for listening. -- DaiThe problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant.That's a very interesting observation. Could the solution be running the invariant only once, at the outermost public function call? Hm... It would have to be a runtime feature then, right? Every public function would have calls to the invariant but those calls would have to be elided at runtime. I think... Here is another interesting observation: It is acceptable and quite normal that the object is in limbo state during a public member function. As a consequence, any function that operates on the object must use the object in a write-only fashion during that time frame. This is true even for non-member functions that the object is passed to. So, in theory, even a logging function cannot use the object. Hm...
Aug 26 2013
On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:...By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
Aug 26 2013
On Monday, 26 August 2013 at 14:16:04 UTC, Dicebot wrote:On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:Not all invariants can run at compile time....By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
Aug 26 2013
On Monday, 26 August 2013 at 16:49:01 UTC, Peter Alexander wrote:On Monday, 26 August 2013 at 14:16:04 UTC, Dicebot wrote:True, but it will at least make a notable cluster of potential issues into compile-time errors. Actually, I'd expect non-CTFE invariant to be quite rare - those rarely do any I/O or some weird pointer forging. If invariant is not CTFE-able it should be deferred to runtime to the initial point of the aggregate construction.On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:Not all invariants can run at compile time....By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
Aug 26 2013
On 25/08/13 22:16, deadalnix wrote:As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks.I just discovered another (surprising to me) shortcoming with the implementation of invariants: namely they don't get checked during the default initiation of a struct. By default, I mean the initialization process that you get when you don't provide a this() method. This probably doesn't matter in the grand scheme of things as I imagine an instance of the struct that breaks the invariant cant be used without triggering a check. However, for me, it meant I had to use two statements in my unit tests (instead of one) to test the invariant. Also, the deferred triggering of the invariant could make debugging more difficult. Peter
Aug 26 2013