October 12, 2012
On Friday, October 12, 2012 10:29:06 monarch_dodra wrote:
> On Friday, 12 October 2012 at 08:20:42 UTC, Jonathan M Davis
> 
> wrote:
> > On Friday, October 12, 2012 10:09:22 monarch_dodra wrote:
> > 
> > If that's what you're "supposed" to do, it's only because
> > opAssign is annoying
> > enough to check its invariant. Without the invariant, that's
> > not something
> > that would normally make sense to do. And it's _not_ what you
> > do with a built-
> > in type.
> > 
> > int i = void;
> > i = 5;
> > 
> > is perfectly legal. I see no reason why
> > 
> > S s = void;
> > s = S(17);
> > 
> > [SNIP]
> > 
> > - Jonathan M Davis
> 
> The issue with initializing with void actually has nothing to do with invariants.
> 
> Try that code defining S as RefCounted!int and see what happens.

That just means that the problem goes further than just invariants. It's still a big problem for invariants.

- Jonathan M Davis
October 12, 2012
On Friday, 12 October 2012 at 08:34:31 UTC, Jonathan M Davis wrote:
>> 
>> Try that code defining S as RefCounted!int and see what happens.
>
> That just means that the problem goes further than just invariants. It's still
> a big problem for invariants.
>
> - Jonathan M Davis

I appologize, but I don't see how this is a "big problem". It is no different then doing:

S s = void;
s.__ctor(args);

Initializing something to void _is_ unsafe, and must be used with precautions. opAssign is no different. If you want it called, then you _HAVE_ to make sure the target is valid first.

The only reason:
int a = void;
a = 5;

works is because a doesn't define an opAssign, and int doesn't have any invalid states anyways.

It's once S becomes complex that you can't just go rushing in assigning and constructing without proper initialization.

Using emplace makes the "problem" go away entirely*, as it will just do a straight-up memcopy if that is "good enough" (no extra cost for ints), and do "what is needed" for the rest (".init + .__ctor" or ".init + opAssign").

alias RefCounted!int S;
int i = void;
S s1 = void;
S s2 = void;
emplace(&i, 5);     //OK! Do a memcpy assignement
emplace(&s1, 5);    //OK! Do a .init memcpy + .__ctor
emplace(&s2, S(5)); //OK! Do a .init memcpy + .opAssign**

*Technically, once my fix goes through. It currently chokes.
**Actually, RefCounted has a CC, so that is the one that will be used. Just wanted to illustrate it *could* be one of the things that could happen.

//--------
Bask on subject, I _have_ started working with invariants. I think they are nice, but there indeed some times where you'd wish they wouldn't trigger.

How about the @noinvariant function attribute? Sounds like a simple enough solution.

At that point, the developer can just insert "assert(&this);" in said functions, if and where he judges it necessary.

October 12, 2012
On Friday, October 12, 2012 11:31:36 monarch_dodra wrote:
> Bask on subject, I _have_ started working with invariants. I think they are nice, but there indeed some times where you'd wish they wouldn't trigger.
> 
> How about the @noinvariant function attribute? Sounds like a simple enough solution.
> 
> At that point, the developer can just insert "assert(&this);" in said functions, if and where he judges it necessary.

That sounds like a decent solution to me, but I think that there's a good chance that Walter would reject it on principle (since in general, skipping the invariant pretty much defeats the purpose of having one). This is the only case that I'm aware of where it really makes sense to not have an invariant triggered, and he seems to be against the idea that opAssign wouldn't trigger the invariant when called, so I expect that he'd be against this as well if the whole purpose was to enable that case. Other, solid use case would probably be needed as well.

- Jonathan M Davis
October 12, 2012
On Friday, 12 October 2012 at 09:42:19 UTC, Jonathan M Davis wrote:
> That sounds like a decent solution to me, but I think that there's a good
> chance that Walter would reject it on principle (since in general, skipping
> the invariant pretty much defeats the purpose of having one).

But he already suggested implementing a _custom_ mechanism for skipping the invariant somewhere else in this thread (i.e. a "valid" flag) which is arguably even worse…

David
October 12, 2012
On Friday, October 12, 2012 11:49:48 David Nadlinger wrote:
> On Friday, 12 October 2012 at 09:42:19 UTC, Jonathan M Davis
> 
> wrote:
> > That sounds like a decent solution to me, but I think that
> > there's a good
> > chance that Walter would reject it on principle (since in
> > general, skipping
> > the invariant pretty much defeats the purpose of having one).
> 
> But he already suggested implementing a _custom_ mechanism for skipping the invariant somewhere else in this thread (i.e. a "valid" flag) which is arguably even worse…

Clearly, I missed that. But that's definitely not particularly clean (though it _can_ be done right now without making any changes to the language, which for most things is the better approach). It's not as bad now that with have version(assert), since it makes it so that the valid flag can be compiled out in release mode, but it's still messier than @noinvariant would be (if nothing else, it requires more code and a version(assert) block every time that the valid flag is used), and more importantly, it only solves the T.init case and not the case where the struct is initialized to void. So, @noinvariant makes a lot more sense, but it _does_ require an update the language, so I wouldn't expect Walter to be all that enthused about it, but if he already thinks that a "valid" flag is okay, then he wouldn't necessarily be opposed to the idea of it being possible to explicitly skip invariant checks in some cases.

- Jonathan M Davis
October 12, 2012
On Friday, 12 October 2012 at 08:20:42 UTC, Jonathan M Davis wrote:

> Really, I think that it's a bad design decision to require that the invariant be called before opAssign. It does _not_ play nice with some of D's other features, and the result is likely to be that invariants get used less, meaning that code is more likely to be buggy.

 You make a good argument, but you can also override opAssign for things that are not it's type exact type. So...

 struct S {
   float x;

   ref S opAssign(int y) {
     x = y;
     return this;
   }
 }

 S s;
 int i;
 s = i; //opAssign, correct?


 In cases like this opAssign would need an invariant before and after the call. But if you were just replacing the whole object you wouldn't.

 I'll say a @novariant is the better answer, and automatically used on the default copy/opAssign/postblitz (before the call, but still needed after).
October 12, 2012
On Friday, 12 October 2012 at 20:33:11 UTC, Era Scarecrow wrote:
>  I'll say a @novariant is the better answer, and automatically used on the default copy/opAssign/postblitz (before the call, but still needed after).

The language already states that the invariant is only called at the end of construction (ergo copy/postblit).

opAssign, IMO, is not (much) different than any other function. invariant checks should be disabled on it, if and when the developer explicitly requests it. That's the safest route anyway.
October 15, 2012
On Thu, 11 Oct 2012 13:23:10 -0400, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> Any situation where the init value is essentially invalid (like it would be
> with floating point types) makes it so that you can't have an invariant, and in
> many of those cases, having a default constructor which was always called
> would solve the problem. I'm still in favor of _not_ trying to add default
> constructors given all of the issues involved, and I agree that on the whole,
> init is a superior solution (even if it isn't perfect), but there _are_ cases
> where you can't have an invariant because of it.


Isn't it possible to customize when an "invariant" is called using contracts?

For example:

struct S
{
   private bool isValid;
   private void _invariant() {assert(isValid);}

   void foo()
   in { _invariant();} out {_invariant();} body
   {
      // whatever
   }

   void opAssign(ref S other)
   out {_invariant();} body
   {
      isValid = other.isValid;
   }
}

???

Yeah, It's extra work.  But essentially, isn't this what you want?  The thing about disabling invariant checks on some specific function in some specific case is that someone else has a valid case for requiring it.

The only sucky part about the above is, _invariant is compiled in even in release mode (though it should inline to a noop).

-Steve
October 15, 2012
On Monday, 15 October 2012 at 15:56:33 UTC, Steven Schveighoffer wrote:
>
>
> Isn't it possible to customize when an "invariant" is called using contracts?
>
> For example:
>
> struct S
> {
>    private bool isValid;
>    private void _invariant() {assert(isValid);}
>
>    void foo()
>    in { _invariant();} out {_invariant();} body
>    {
>       // whatever
>    }
>
>    void opAssign(ref S other)
>    out {_invariant();} body
>    {
>       isValid = other.isValid;
>    }
> }
>
> ???
>
> Yeah, It's extra work.  But essentially, isn't this what you want?  The thing about disabling invariant checks on some specific function in some specific case is that someone else has a valid case for requiring it.
>
> The only sucky part about the above is, _invariant is compiled in even in release mode (though it should inline to a noop).
>
> -Steve

There is now an "assert" word for version blocks so you can put your code inside that, and it doesn't get compiled in during release.
October 16, 2012
On Monday, October 15, 2012 11:56:33 Steven Schveighoffer wrote:
> Yeah, It's extra work. But essentially, isn't this what you want? The thing about disabling invariant checks on some specific function in some specific case is that someone else has a valid case for requiring it.

I've considered it, and I may end up doing that for SysTime, but it's also kind of ridiculous to have to add assertions to _every_ function like that just to avoid having it called on one function.

> The only sucky part about the above is, _invariant is compiled in even in release mode (though it should inline to a noop).

version(assert) now fixes that problem.
1 2 3 4 5
Next ›   Last »