Conversation
|
Unit tests fail. |
|
@Hackerpilot Thanks. |
std/typecons.d
Outdated
| this()(S s) @trusted | ||
| { | ||
| // we preserve tail immutable guarantees so cast is OK | ||
| payload = cast(Unqual!S)s; |
There was a problem hiding this comment.
we preserve tail immutable guarantees so cast is OK
Are you sure? Casting away const is UB in any situation.
There was a problem hiding this comment.
Casting away const is UB in any situation.
Just casting is ok. You just can't mutate through the casted reference.
There was a problem hiding this comment.
If the payload has an opAssign, it will be able to mutate through it.
There was a problem hiding this comment.
OK, maybe that's why existing Rebindable uses a union to get around the problem.
There was a problem hiding this comment.
@schuetzm I've changed the implementation to use a union now, I think that solves the mutable opAssign problem. (I'm not entirely sure about the destructor, I'll try to do some tests).
|
@Hackerpilot can you remove (or explain) the needs work label please ;-) All unit tests currently pass. |
std/typecons.d
Outdated
| S payload; | ||
| } | ||
|
|
||
| @trusted: |
There was a problem hiding this comment.
How can this be true when S can have an arbitrarily unsafe opAssign or dtor?
There was a problem hiding this comment.
I think it's OK now, opAssign is never called, and S.~this has its safety inferred. Rebindable.thisalso uses inferrence for safety of S.this.
|
(keeping the "needs work" label up to date is impossible for people without commit access, so removing it for now) |
|
Also there's another problem: unions can't have struct members that themselves define destructors or postblits. Edit: I'll try to fix this using |
std/typecons.d
Outdated
|
|
||
| void opAssign(S s) @trusted | ||
| { | ||
| auto rs = Rebindable(s); |
There was a problem hiding this comment.
Unless I'm missing something, this runs S's postblit which may be unsafe, breaking opAssign's @trusted promise.
There was a problem hiding this comment.
Thanks, should be fixed now.
Unions don't allow struct members which have postblit or dtors.
|
Implemented changing union -> |
|
Ping? This is part of making Rebindable generic (which should be simple once this is this merged). This will help to implement functions like Also, should this go in |
wilzbach
left a comment
There was a problem hiding this comment.
A couple of first impressions
|
|
||
| /// ditto | ||
| Rebindable!S rebindable(S)(S s) | ||
| if (is(S == struct) && !isInstanceOf!(Rebindable, S)) |
There was a problem hiding this comment.
The Phobos style is to avoid such type overloads and hide them as implementation details.
| () @trusted {emplace!S(mutPayload, s);}(); | ||
| else | ||
| emplace!S(mutPayload, s); | ||
| } |
There was a problem hiding this comment.
How about pushing this @safe improvement to std.conv.emplace?
There was a problem hiding this comment.
I think it's only safe if S.~this is run when mutPayload goes out of scope, which emplace can't enforce.
|
|
||
| // immutable S cannot be passed to auto ref S above | ||
| static if (!is(S == immutable)) | ||
| this()(immutable S s) |
There was a problem hiding this comment.
that's what inout was made for ;-) -> inout auto ref S s
There was a problem hiding this comment.
Doesn't work. I'm a bit rusty on this PR but I remember struggling with compile errors - inout triggers them.
| static if (!is(S == immutable)) | ||
| void opAssign()(immutable S s) | ||
| { | ||
| emplace(s); |
There was a problem hiding this comment.
why is there no movePayload (and thus call to of destructor of the internal payload)?
| } | ||
|
|
||
| static if (!is(S == immutable)) | ||
| ref S Rebindable_getRef() @property |
There was a problem hiding this comment.
Can't be because it's used for alias this.
| } | ||
|
|
||
| static if (is(S == immutable)) | ||
| S Rebindable_get() @property |
| * temporary copy of `S` in order to enforce _true immutability. | ||
| */ | ||
| template Rebindable(S) | ||
| if (is(S == struct)) |
There was a problem hiding this comment.
It would be awesome if you could put all Rebindable types under this template as sth. like __traits(isSame, TemplateOf!(typeof(ri2)), Rebindable)
would work. Currently Rebindable references to the first symbol - the one for classes etc.
| emplace(s); | ||
| } | ||
|
|
||
| void opAssign()(auto ref S s) |
There was a problem hiding this comment.
for
Rebindable!(immutable S) ri = S(new int);
auto ri2 = ri;this yields conflicting overloads.
There was a problem hiding this comment.
Works for me - I added a test for this, let's see if the autotester passes.
std/typecons.d
Outdated
| ~this() | ||
| { | ||
| // call destructor with proper constness | ||
| S s = movePayload; |
There was a problem hiding this comment.
You can remove the S s = part ;-)
While usually it's not needed, it seems that your PR got quite stalled and for I made a simple test for your code to check the lifetime by counting postblit and destructor calls. Last but not least
unittest
{
import std.stdio;
int del;
int post;
struct S
{
int* ptr;
int level;
this(this) {
writeln("S:this(this)");
post++;
level++;
}
~this() {
writeln("S:~this");
del++;
}
}
// test const
{
Rebindable!(const S) rc = S(new int);
assert(post == 1);
assert(rc.level == 1);
assert(post == 1);
assert(rc.level == 1); // no copy is created
}
assert(post == 1);
assert(del == 2);
del = 0, post = 0;
// immutable
{
// on every field access a temporary immutable copy gets created
Rebindable!(immutable S) ri = S(new int);
assert(post == 1);
assert(ri.level == 2);
assert(post == 2);
assert(ri.level == 2); // however it's a copy of the payload's level
}
assert(post == 3);
assert(del == 4);
del = 0, post = 0;
{
// the initial value is copied and destructed
Rebindable!(const S) rc = S(new int);
assert(post == 1);
assert(del == 1);
assert(rc.level == 1);
// on an assignment to another value is simply post-blitted
const S cs = rc;
assert(del == 1);
assert(post == 2);
assert(rc.level == 1);
assert(cs.level == 2);
// however on an assignment, the old payload gets destructed
rc = cs;
assert(del == 2);
assert(post == 3);
assert(cs.level == 2);
assert(rc.level == 3);
}
assert(post == 3);
assert(del == 4);
del = 0, post = 0;
{
// the initial value is copied and destructed
Rebindable!(immutable S) ri = S(new int);
assert(post == 1);
assert(del == 1);
// creates temporary (gets immediately destroyed), actual level is still 1
assert(ri.level == 2);
assert(del == 2);
assert(post == 2);
// on an assignment to another value is simply post-blitted
const S cs = ri;
assert(del == 2);
assert(post == 3);
// creates temporary (gets immediately destroyed), actual level is still 1
assert(ri.level == 2);
assert(cs.level == 2);
assert(del == 3);
assert(post == 4);
// however, on an assignment to Rebindable the old value gets destructed
auto ri2 = ri;
static assert(is(typeof(ri2) == Rebindable!(immutable S)));
static assert(TemplateOf!(typeof(ri2)).stringof == "Rebindable(S) if (is(S == struct))");
assert(del == 3);
assert(post == 4);
assert(ri2.level == 2); // should be 3??
assert(del == 4);
// similarly an assignment
//ri = ri2; // fails to compile due to overload conflicts
}
assert(post == 5);
assert(del == 7);
} |
|
@wilzbach Thanks for reviewing this! I'm updating my toolchain so I can work against the latest master. Also I will move the changes to |
@ntrel Did you ever manage to find time for this? |
|
It's been a year so it looks like this is pretty much dead. I'm going to close this but @ntrel please comment if you want me to re-open it. |
|
Hmm, I still think this is quite useful (I have had the need for it a few times over the last months), so I have revived this PR to #6136 (I have already addressed all my comments, except for the |
Rebindable!Struct:"Models safe reassignment of otherwise constant struct instances.
A constant struct with a field of reference type cannot be assigned to a mutable
struct of the same type. This protects the constant reference field from being
mutably aliased, potentially allowing mutation of
immutabledata. However, theassignment could be safe if all reference fields are only exposed as
const.Rebindable!(const S)accepts assignment froma
const Swhile enforcing only constant access to its fields.Rebindable!(immutable S)does the same but field access may create atemporary copy of
Sin order to enforce true immutability."