-
-
Notifications
You must be signed in to change notification settings - Fork 740
std.container.slist: Fix a @safe cannot call @system issue #6295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks for your pull request and interest in making D better, @carblue! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. |
@wilzbach
The decoupling is required, though ugly dup-ing, otherwise I'll run into this:
|
std/container/slist.d
Outdated
@@ -140,7 +140,8 @@ Constructor taking a number of nodes | |||
*/ | |||
this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) | |||
{ | |||
insertFront(values); | |||
U[] valuesDuped = values.dup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is painful and it's inane that we have to do this. Would manually inlining the insertFront
loop remove the need to copy the array for DIP1000?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly is meant by manually inlining the insertFront loop
?
I tried foreach lowered to for (auto item=stuff.front...
That didn't change the error: Error: scope variable item may not be returned
Removing trivial label due to discussion. |
Thanks for reviewing. For some SList(T). struct Node. T _payload; |
std/container/slist.d
Outdated
initialize(); | ||
size_t result; | ||
Node * n, newRoot; | ||
foreach (item; stuff) | ||
{ | ||
auto newNode = new Node(null, item); | ||
static if (hasIndirections!T) | ||
auto newNode = new Node(null, item.dup); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok this same thing came up in #6281 and I'm at the point where I believe this should be considered a fatal flaw of DIP1000.
How is anyone supposed to implement DIP1000 if any time they need to assign a scope
variable to a result struct
/class
they need to copy it?
Am I missing an obvious solution here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Welcome to the club. Yes DIP1000 is about as useful as head const
. Or probably worse.
@carblue Thanks for your work on this.
Make this change only: diff --git a/std/container/slist.d b/std/container/slist.d
index 8c2f2fc6d..9c3bc0e7b 100644
--- a/std/container/slist.d
+++ b/std/container/slist.d
@@ -140,7 +140,15 @@ Constructor taking a number of nodes
*/
this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
{
- insertFront(values);
+ initialize();
+ Node * n, newRoot;
+ foreach (item; values)
+ {
+ auto newNode = new Node(null, item);
+ (newRoot ? n._next : newRoot) = newNode;
+ n = newNode;
+ }
+ _first = newRoot;
}
/** On my machine this removes all DIP1000 problems from slist.d without making a copy of the |
Credit and thanks to @n8sh ! My blockhead: What else than what You're describing is 'manually inlining'! Great idea. |
std/container/slist.d
Outdated
std/container/slist.d(938): Error: cannot take address of local j in @safe function __unittest_L935_C7 | ||
std/container/slist.d(942): Error: cannot take address of local i in @safe function __unittest_L935_C7 | ||
std/container/slist.d(943): Error: cannot take address of local j in @safe function __unittest_L935_C7 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use version(DIP1000)
as proposed in #6281?
version(DIP1000)
isn't defined yet, but
- it could be once all Phobos code is passing
- it allows easy grepping for such tests
- in theory we could already add it to the individual
.test
target by doing a replacement likes/-dip1000/-dip1000 -version(DIP1000)
@carblue now that we have https://github.com/dlang/phobos/blob/master/dip1000.mak, could you set the respective modules to |
std/container/slist.d
Outdated
{ | ||
insertFront(values); | ||
initialize(); | ||
Node * n, newRoot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DStyle: Node*
@wilzbach Yep, I'll update dip1000.mak when something got merged and thus ready for -dip1000; the only one that magically got -dip1000 again in the meantime (without depends on) is: Next to come: (?) and std.exception will be an easy fix with a workaround acc. to https://issues.dlang.org/show_bug.cgi?id=18637 |
@carblue Oh sorry. I was thinking about updating |
@wilzbach My fault. That's better, combining them. It looks like I get blockheads when looking at a screen for more than 12h. |
I also added |
Since this is congruent with the D vision, I added the Vision label and this is a priority issue. https://wiki.dlang.org/Vision/2018H1 |
FYI: there's an easy way to set this label to all (screenshot of https://github.com/dlang/phobos/pulls?q=is%3Aopen+is%3Apr+project%3Adlang%2Fphobos%2F6) |
I feel like we should dive into why dip1000 requires the duplication first as this might severely hurt the usability and adoption of dip1000. struct SList(T)
{
import std.range.primitives : isInputRange, isForwardRange, ElementType;
import std.traits : isImplicitlyConvertible;
private struct Node
{
Node * _next;
T _payload;
}
private struct NodeWithoutPayload
{
Node* _next;
}
static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof);
private Node * _root;
private void initialize() @trusted nothrow pure
{
if (_root) return;
_root = cast (Node*) new NodeWithoutPayload();
}
private ref inout(Node*) _first() @property @safe nothrow pure inout
{
assert(_root);
return _root._next;
}
this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
{
insertFront(values);
}
this(Stuff)(Stuff stuff)
if (isInputRange!Stuff
&& isImplicitlyConvertible!(ElementType!Stuff, T)
&& !is(Stuff == T[]))
{
insertFront(stuff);
}
@property bool empty() const
{
return _root is null || _first is null;
}
@property ref T front()
{
assert(!empty, "SList.front: List is empty");
return _first._payload;
}
@safe unittest
{
auto s = SList!int(1, 2, 3);
s.front = 42;
assert(s == SList!int(42, 2, 3));
}
size_t insertFront(Stuff)(Stuff stuff)
if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
{
initialize();
size_t result;
Node * n, newRoot;
foreach (item; stuff)
{
auto newNode = new Node(null, item);
(newRoot ? n._next : newRoot) = newNode;
n = newNode;
++result;
}
if (!n) return 0;
// Last node points to the old root
n._next = _first;
_first = newRoot;
return result;
}
/// ditto
size_t insertFront(Stuff)(Stuff stuff)
if (isImplicitlyConvertible!(Stuff, T))
{
initialize();
auto newRoot = new Node(_first, stuff);
_first = newRoot;
return 1;
}
}
///
@safe unittest
{
auto s = SList!int(1, 2, 3);
} Interesting
|
Here's a dustmited version of the error: struct SList(T)
{
struct Node
{
Node* _next;
T _payload;
}
struct Range
{
Node _head;
@property front()
{
return _head._payload;
}
enum empty = true;
void popFront(){}
}
this(Stuff)(Stuff stuff)
{
insertFront(stuff);
}
@property dup()
{
SList(this[]);
}
Range opSlice()
{
return Range();
}
size_t insertFront(Stuff)(scope Stuff stuff)
{
foreach (item; stuff)
new Node(null, item);
return 0;
}
}
void main()
{
SList!string s;
} |
|
Reduced it even further: struct SList(T)
{
struct Range
{
T payload;
}
void insertFront(Stuff)(scope Stuff stuff)
{
new Range(stuff.payload);
}
}
void main()
{
SList!string s;
typeof(s).Range r;
s.insertFront(r);
}
|
Odd, I get a completely different error with both 2.079 and nightly
|
Yeah, I get the same error for 2.079, but note that the nightlies are currently broken. |
In the example you gave, it is indeed copying a scope variable into allocated memory:
Scope means the pointer does not escape the scope. Copying it into allocated memory is escaping it. |
@WalterBright This PR is not a viable solution, as all it does is copy and paste the function contents of I'm going to revert this. @carblue Please feel free to open a new PR addressing this issue. |
I would note that @wilzbach's approval was on an earlier version of this PR, and not this code. |
Yes, the problem of DIP1000 seemingly requiring code duplication (to get slist.d to compile with current master) still isn't solved. I'm about to open a bugzilla issue for that, using @wilzbach 's analysis. |
make -j6 -f posix.mak unittest
results in:
...
sources used (on Linux x86_64):
dmd : Latest commit 17bc56c
druntime: Latest commit f2711a3
phobos : Latest commit 6947d80