Bug 19877 – [dip1000] std.container.rbtree is unsafely accessing private data

Status
RESOLVED
Resolution
FIXED
Severity
major
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2019-05-15T18:12:42Z
Last change time
2022-04-03T08:33:55Z
Keywords
rejects-valid
Assigned to
No Owner
Creator
hsteoh

Comments

Comment #0 by hsteoh — 2019-05-15T18:12:42Z
Code: ------ import std.container.rbtree; alias Grid = RedBlackTree!(GridPoint); struct GridPoint { private string _srcStr; int opCmp(in GridPoint p) const { return 0; } } ------ Compiler output (with -preview=dip1000): ------ /usr/src/d/phobos/std/container/rbtree.d(1111): Error: @safe function std.container.rbtree.RedBlackTree!(GridPoint, "a < b", false).RedBlackTree.toHash cannot call @system function core.internal.hash.hashOf!(GridPoint).hashOf /usr/src/d/druntime/import/core/internal/hash.d(510): core.internal.hash.hashOf!(GridPoint).hashOf is declared here numid.d(3): Error: template instance `std.container.rbtree.RedBlackTree!(GridPoint, "a < b", false)` error instantiating ------ Compiling without -preview=dip1000 works. The problem appears to be the 'private': removing 'private' from _srcStr makes the problem go away. Why 'private' makes toHash un-@safe is anybody's guess.
Comment #1 by hsteoh — 2019-05-16T00:54:32Z
Here's a much more reduced case: ------ struct S { private int _x; } struct RedBlackTree { size_t toHash() nothrow @safe { return .hashOf(S.init); } } void main() { } ------
Comment #2 by bugzilla — 2020-03-12T09:31:34Z
The cause of this is core.internal.hash.hashOf() is getting its attributes inferred. Having it access private members of a struct causes it to be inferred as @system, and @safe RedBlackTree.toHash() is not allowed to call an @system function. The problem is in std.container.rbtree, where the call to .toHash() should be put in an @trusted block.
Comment #3 by Ajieskola — 2021-06-07T10:40:12Z
I'd argue it behaves correctly now. Private state should be private. If `.hashOf`, or any other external funtion, needs to access it directly, it ought to fail. The correct thing to do is to add `toHash` member function to the element `struct`. Current `-dip1000` prevents errors like this: ``` struct Ternary { private ubyte state; @safe auto opEquals(const Ternary rhs) const { if (state >= 2) return rhs.state >= 2; else return state == rhs.state; } } @safe void main() { Ternary a = void, b = void; //fails with high likelihood if(a == b) assert(.hashOf(a) == .hashOf(b), "WAT?"); } ``` Perhaps the error message should suggest adding the `toHash` function, though.
Comment #4 by dkorpel — 2022-04-03T08:33:55Z