Bug 17730 – [scope][dip1000] std.algorithm.move escapes scope variable in @safe code

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P3
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-08-07T22:45:29Z
Last change time
2018-01-03T17:53:46Z
Keywords
accepts-invalid, pull, safe
Assigned to
No Owner
Creator
Moritz Maxeiner

Comments

Comment #0 by moritz — 2017-08-07T22:45:29Z
The following compiles with `dmd -dip1000 escape_scope_class.d` - escape_scope_class.d - import core.stdc.stdio : printf; import std.algorithm : move; class A { int i; this() @safe { i = 0; } } void inc(scope A a) @safe { a.i += 1; } void print(scope A a) @trusted { printf("A@%x: %d\n", cast(void*) a, a.i); } auto makeA() @safe { scope a = new A(); a.print(); return move(a); } void main() @safe { auto a = makeA(); foreach (i; 0..3) { a.print(); a.inc(); } } --- and outputs something like --- A@198d1568: 0 A@198d1568: 0 A@198d1568: 1 A@198d1568: 2 --- , i.e. a reference to the `makeA.a` object is escaped and assigned to `main.a` within @safe code. What needs fixing is that the above code should error out with an appropriate message about escaping the reference to the scope class. Additionally, it would be nice if the following code where to compile with `-dip1000`, but work like moving a std.typecons.scoped: --- void main() @safe { scope a = makeA(); foreach (i; 0..3) { a.print(); a.inc(); } } --- would then output something like --- A@198d1568: 0 // scope class object makeA.a (on makeA's stack frame) A@198d1578: 0 // scope class object main.a (on main's stack frame) A@198d1578: 1 A@198d1578: 2 --- so essentially the scope class object is blitted from makeA to main the same way it would if it were a scoped!A (and has the same dangers w.r.t. self-references, of course).
Comment #1 by bugzilla — 2017-08-26T21:18:24Z
Breaking it down to the following code: import std.algorithm; T moveImpl(T)(ref T source) { T result = void; moveEmplace(source, result); return result; } T trustedMoveImpl(T)(ref T source) @trusted { return moveImpl(source); } T move(T)(ref T source) { // test @safe destructible static if (__traits(compiles, (T t) @safe {})) return trustedMoveImpl(source); // this is the path taken else return moveImpl(source); } class A { } A makeA() @safe { scope a = new A(); return move(a); // should fail } The trouble is that moveEmplace() is @system, but trustedMoveImpl() paints it to be @trusted, thus defeating the scope checks. Re-categorized as a Phobos issue.
Comment #2 by code — 2017-10-23T19:50:27Z
cat > bug.d << CODE import std.algorithm : move; int* escapeLocal() @safe { int var; scope int* p = &var; return move(p); // should error } void test() @safe { auto p = escapeLocal; } CODE dmd -c -o- -dip1000 bug.d ---- Reduced example not specific to scope classes, seems like move should take it's argument as return scope, no?
Comment #3 by code — 2017-10-23T20:18:43Z
Comment #4 by github-bugzilla — 2017-10-27T17:44:28Z
Commits pushed to master at https://github.com/dlang/phobos https://github.com/dlang/phobos/commit/a34a25ead947fe6492fb0724b23dc5135c19ea1f fix Issue 17730 - move escapes scope variable in @safe code - needs to be annotated with return scope, so that the return value lifetime depends on the argument's lifetime - cannot be tested because phobos doesn't yet work with DIP1000 and also because of Issue 17932 https://github.com/dlang/phobos/commit/61b7b517b0ab2a3eed45116f7476a0bd7a68837a Merge pull request #5798 from MartinNowak/fix17730 fix Issue 17730 - move escapes scope variable in @safe code
Comment #5 by github-bugzilla — 2017-12-18T22:55:33Z
Commits pushed to stable at https://github.com/dlang/phobos https://github.com/dlang/phobos/commit/a34a25ead947fe6492fb0724b23dc5135c19ea1f fix Issue 17730 - move escapes scope variable in @safe code https://github.com/dlang/phobos/commit/61b7b517b0ab2a3eed45116f7476a0bd7a68837a Merge pull request #5798 from MartinNowak/fix17730
Comment #6 by code — 2018-01-03T17:53:46Z
*** Issue 18128 has been marked as a duplicate of this issue. ***