Bug 15651 – filter: only parameters or stack based variables can be inout

Status
NEW
Severity
normal
Priority
P3
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2016-02-07T09:28:35Z
Last change time
2024-12-01T16:25:57Z
Assigned to
No Owner
Creator
Infiltrator
See also
https://issues.dlang.org/show_bug.cgi?id=9983, https://issues.dlang.org/show_bug.cgi?id=12408
Moved to GitHub: phobos#10158 →

Comments

Comment #0 by lt.infiltrator — 2016-02-07T09:28:35Z
import std.algorithm.iteration : filter; import std.array : array; class D { int x; this(int n) { x = n; } } class C { @property auto fun() inout nothrow pure @safe { return members.filter!(m => m.x == 0).array; } D[] members; } void main() { auto c = new C; c.members = [ new D(0), new D(1) ]; auto foo = c.fun; } ============ Compilation output: /opt/compilers/dmd2/include/std/algorithm/iteration.d(980): Error: variable f534.C.fun.FilterResult!(__lambda1, inout(D)[]).FilterResult._input only parameters or stack based variables can be inout /opt/compilers/dmd2/include/std/algorithm/iteration.d(944): Error: template instance f534.C.fun.FilterResult!(__lambda1, inout(D)[]) error instantiating /d456/f534.d(8): instantiated from here: filter!(inout(D)[]) /d456/f534.d(8): Error: template std.algorithm.iteration.filter cannot deduce function from argument types !((m) => m.x == 0)(inout(D[])), candidates are: /opt/compilers/dmd2/include/std/algorithm/iteration.d(940): std.algorithm.iteration.filter(alias predicate) if (is(typeof(unaryFun!predicate))) ============
Comment #1 by simen.kjaras — 2019-08-16T12:17:46Z
This issue is only partially fixable - filter() could use the solution described in issue 13428 to turn inout(T) into const(T). This would lead to fun() returning a const(D)[] result instead of inout(D)[], so it's not a complete solution. A complete solution, in fact, is not actually possible - consider the case of returning SomeStruct!(inout(D)). inout(D) is only a valid type inside the inout function - after returning, inout(D) should turn into D, const(D), or immutable(D). That means SomeStruct!(inout(D)) should turn into SomeStruct!(const(D)) or one of the other constancy variations, and this type might be completely different from SomeStruct!(inout(D)), due to the way D's templates work. Perhaps a solution could be found to this problem, but it would very likely require a DIP.
Comment #2 by dfj1esp02 — 2019-08-21T10:04:29Z
See also workaround in issue 9983
Comment #3 by schveiguy — 2019-08-22T14:09:34Z
(In reply to Simen Kjaeraas from comment #1) > This issue is only partially fixable - filter() could use the solution > described in issue 13428 to turn inout(T) into const(T). This would lead to > fun() returning a const(D)[] result instead of inout(D)[], so it's not a > complete solution. It's fully fixable -- if we can create an filter of inout elements. As I mentioned in issue 9983, this simply involves allowing structs to declare inout members. In fact, we should allow inout to be used ANYWHERE, except global variables (as putting inout variables in global space can change the meaning from call to call). They will just be another type of immutable in functions which don't have inout parameters, or when used in structs outside of inout functions. (In reply to anonymous4 from comment #2) > See also workaround in issue 9983 This is not a valid workaround, as an inout filter would not be iterable. I actually thought that maybe with closures, we could make a "workaround" but it doesn't work because there's no way to return an inout from a function that has no inout parameters. Another stupid limitation that we should omit. I'd try my hand at an inout improvement DIP, but I feel it would likely be rejected on principle, since inout is so disfavored by leadership.
Comment #4 by dfj1esp02 — 2019-08-23T13:37:42Z
It will need separation between collections and iterators --- struct A { int[][] a; inout(int[])[] f() inout { return a.filter(x=>x!=null).array; } } inout(FilterCollection) filter(inout int[][] r, bool function(const int[]) pred) { return inout FilterCollection(r,pred); } struct FilterCollection { int[][] r; bool function(const int[]) pred; FilterRange range() const { auto r=FilterRange(&this); r.next(); return r; } } struct FilterRange { const(FilterCollection)* src; long index; inout(int[]) front(ref inout FilterCollection src2) { assert(src==&src2); return src2.r[index]; } void popFront() { index++; next(); } package void next() { foreach(i,a;src.r[index..$]) { if(src.pred(a)){ index+=i; return; } } index=src.r.length; } bool empty() { return index==src.r.length; } } inout(int[])[] array(inout FilterCollection src) { inout FilterCollection c=src; inout(int[])[] a; auto r=c.range; while(!r.empty) { //a~=r.front(c); inout int[] b=r.front(c); a~=b; r.popFront(); } return a; } int main() { A a; int[][4] b; b[0]=[1]; b[3]=[2]; a.a=b; int[][] c=a.f; assert(c.length==2); assert(c[0][0]==1); assert(c[1][0]==2); return 0; } ---
Comment #5 by htvennik — 2019-08-24T10:03:03Z
(In reply to Steven Schveighoffer from comment #3) > I'd try my hand at an inout improvement DIP, but I feel it would likely be > rejected on principle, since inout is so disfavored by leadership. I already have a partial draft. But as the discussion on various inout related issues evolved, I stopped writing for the moment, as there seem to be quite different opinions. My intent was to keep inout semantics the same as much as possible. The main proposal in my draft is to allow a function that somehow has acces to an inout variable from an enclosing scope to return inout. This requires some additional scoping rules for delegates to prevent escaping references to inout variables. In theory DIP25 already gets close, though its implementation currently does not seem to detect return ref violations that involve delegates. About inout fields in structs: I think those could be allowed inside inout functions, but such struct should not be allowed to escape from the scope of that function.
Comment #6 by htvennik — 2019-08-25T06:55:04Z
(In reply to Steven Schveighoffer from comment #3) > In fact, we should allow inout to be used ANYWHERE, except global variables > (as putting inout variables in global space can change the meaning from call > to call). They will just be another type of immutable in functions which don't > have inout parameters, or when used in structs outside of inout functions. There have to be more restrictions. The problem is that inout is not really a type modifier like const or immutable, rather it is a type modifier placeholder. Even when disallowing inout variables in global space, structs or class instances with inout members might still reference global data. Those may even have inout member functions, redefining the meaning of inout on every call. Inout would become the perfect way to modify immutable data with no way for the compiler to detect it: ---- class C { private inout(int)[] _arr; this(inout int[] a) { _arr = a; } ref inout(int) getRef(size_t i) inout { return _arr[i]; } } void main() { immutable int[] iArr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; /* inout resolves to immutable in constructor call */ auto c = new C(iArr); /* inout resolves to mutable in call to getRef */ c.getRef(5) = 8; // Undefined behavior } ----
Comment #7 by schveiguy — 2019-08-28T14:08:18Z
(In reply to Harry Vennik from comment #6) > /* inout resolves to mutable in call to getRef */ > c.getRef(5) = 8; // Undefined behavior That would fail because c.getRef(5) would return ref const(int). In order to allow inout modifiers on structs or classes, you need to consider the type modifier of those members in the inout placeholder calculation. In this case, you have mutable C and inout C._arr, which result in const. In other words, the rules aren't written yet, but they can be constructed in a valid way. What we would be trying to accomplish is allowing encapsulation of functionality in a struct or class, even with inout data. Most likely you don't write getRef as an inout function.
Comment #8 by dfj1esp02 — 2019-08-29T08:54:04Z
If you want the mutable ranges to process inout data, they should be able to return inout data unless you want to do the front(inout collection) thing.
Comment #9 by robert.schadek — 2024-12-01T16:25:57Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/phobos/issues/10158 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB