www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - chunkBy bug?

reply JG <someone somewhere.com> writes:
The following code won't compile:
----
import std.algorithm;
import std.range;
import std.stdio;

void main() {
  auto v = [2,4,8,3,6,9,1,5,7];
  foreach(i; 2..4) {
   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
  }
}
----
/opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: 
function tmp.main.ChunkByImpl!(__lambda1, 
int[]).ChunkByImpl.Group.popFront cannot access frame of function 
D main
----

Is this expected behavior?

I can get it to run as expected by modifying the source of 
chunkBy as follows:

----

import std.range;
import std.stdio;
import std.traits;
import std.functional;
private template ChunkByImplIsUnary(alias pred, Range)
{
  alias e = lvalueOf!(ElementType!Range);

  static if (is(typeof(binaryFun!pred(e, e)) : bool))
                                               enum 
ChunkByImplIsUnary = false;
  else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) 
: bool))
                                                                   
  enum ChunkByImplIsUnary = true;
  else
   static assert(0, "chunkBy expects either a binary predicate or 
"~
     "a unary predicate on range elements of type: "~
     ElementType!Range.stringof);
}

// Outer range
struct Impl(Range)
{
  size_t groupNum;
  Range  current;
  Range  next;
}
// Inner range
struct Group(alias eq, Range)
{
  import std.typecons : RefCounted;
  private size_t groupNum;
  private Range  start;
  private Range  current;

  private RefCounted!(Impl!Range) mothership;

  this(RefCounted!(Impl!Range) origin)
  {
   groupNum = origin.groupNum;

   start = origin.current.save;
   current = origin.current.save;
   assert(!start.empty, "Passed range 'r' must not be empty");

   mothership = origin;

   // Note: this requires reflexivity.
   assert(eq(start.front, current.front),
     "predicate is not reflexive");
  }

   property bool empty() { return groupNum == size_t.max; }
   property auto ref front() { return current.front; }

  void popFront()
  {
   current.popFront();

   // Note: this requires transitivity.
   if (current.empty || !eq(start.front, current.front))
   {
    if (groupNum == mothership.groupNum)
    {
     // If parent range hasn't moved on yet, help it along by
     // saving location of start of next Group.
     mothership.next = current.save;
    }

    groupNum = size_t.max;
   }
  }

   property auto save()
  {
   auto copy = this;
   copy.current = current.save;
   return copy;
  }
}

private struct ChunkByImpl(alias pred, Range)
if (isForwardRange!Range)
{
  import std.typecons : RefCounted;

  enum bool isUnary = ChunkByImplIsUnary!(pred, Range);

  static if (isUnary)
   alias eq = binaryFun!((a, b) => unaryFun!pred(a) == 
unaryFun!pred(b));
  else
   alias eq = binaryFun!pred;

  static assert(isForwardRange!(Group!(eq,Range)));

  private RefCounted!(Impl!Range) impl;

  this(Range r)
  {
   impl = RefCounted!(Impl!Range)(0, r, r.save);
  }

   property bool empty() { return impl.current.empty; }

   property auto front()
  {
   static if (isUnary)
   {
    import std.typecons : tuple;
    return tuple(unaryFun!pred(impl.current.front), Group(impl));
   }
   else
   {
    return Group!(eq,Range)(impl);
   }
  }

  void popFront()
  {
   // Scan for next group. If we're lucky, one of our Groups would 
have
   // already set .next to the start of the next group, in which 
case the
   // loop is skipped.
   while (!impl.next.empty && eq(impl.current.front, 
impl.next.front))
   {
    impl.next.popFront();
   }

   impl.current = impl.next.save;

   // Indicate to any remaining Groups that we have moved on.
   impl.groupNum++;
  }

   property auto save()
  {
   // Note: the new copy of the range will be detached from any 
existing
   // satellite Groups, and will not benefit from the .next 
acceleration.
   return typeof(this)(impl.current.save);
  }

  static assert(isForwardRange!(typeof(this)), 
typeof(this).stringof
    ~ " must be a forward range");
}

auto chunkBy(alias pred, Range)(Range r)
  if (isInputRange!Range)
{
      return ChunkByImpl!(pred, Range)(r);
}

void main() {
  auto v = [2,4,8,3,6,9,1,5,7];
  foreach(i; 2..4) {
   writeln(v.chunkBy!((a,b)=>a%i==b%i));
  }
}
---
Sep 04 2020
next sibling parent JG <someone somewhere.com> writes:
Sorry for posting to general. I meant to post to learn.
Sep 04 2020
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Sep 05, 2020 at 03:30:33AM +0000, JG via Digitalmars-d wrote:
 The following code won't compile:
 ----
 import std.algorithm;
 import std.range;
 import std.stdio;
 
 void main() {
  auto v = [2,4,8,3,6,9,1,5,7];
  foreach(i; 2..4) {
   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
  }
 }
 ----
 /opt/local/include/phobos/std/algorithm/iteration.d(2009): Error: function
 tmp.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot
 access frame of function D main
 ----
[...] Are you using LDC? IIRC there was a recent bug fixed in dmd that fixes this problem in some cases, but LDC still has this bug. Maybe try with dmd to see if it works? --T
Sep 04 2020
parent reply JG <someone somewhere.com> writes:
On Saturday, 5 September 2020 at 04:10:58 UTC, H. S. Teoh wrote:
 On Sat, Sep 05, 2020 at 03:30:33AM +0000, JG via Digitalmars-d 
 wrote:
 The following code won't compile:
 ----
 import std.algorithm;
 import std.range;
 import std.stdio;
 
 void main() {
  auto v = [2,4,8,3,6,9,1,5,7];
  foreach(i; 2..4) {
   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
  }
 }
 ----
 /opt/local/include/phobos/std/algorithm/iteration.d(2009): 
 Error: function
 tmp.main.ChunkByImpl!(__lambda1, 
 int[]).ChunkByImpl.Group.popFront cannot
 access frame of function D main
 ----
[...] Are you using LDC? IIRC there was a recent bug fixed in dmd that fixes this problem in some cases, but LDC still has this bug. Maybe try with dmd to see if it works? --T
Thanks for your reply. I was getting the same error with both dmd and ldc, I will try updating. For clarity do you know what precisely is causing the problem? My "fix" was to un-nest the structs used in chunkBy. Was the bug in dmd, something to do with nested structs?
Sep 05 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Sep 05, 2020 at 11:14:08AM +0000, JG via Digitalmars-d wrote:
[...]
 Thanks for your reply. I was getting the same error with both dmd and
 ldc, I will try updating. For clarity do you know what precisely is
 causing the problem? My "fix" was to un-nest the structs used in
 chunkBy. Was the bug in dmd, something to do with nested structs?
The problem was caused by nested structs needing a context pointer, and an alias function parameter also needing a context pointer. Older versions of dmd could only handle 1 context pointer at a time, so was unable to handle this case. T -- Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
Sep 06 2020
prev sibling parent reply Seb <seb wilzba.ch> writes:
On Saturday, 5 September 2020 at 03:30:33 UTC, JG wrote:
 The following code won't compile:
 ----
 import std.algorithm;
 import std.range;
 import std.stdio;

 void main() {
  auto v = [2,4,8,3,6,9,1,5,7];
  foreach(i; 2..4) {
   writeln(v.myChunkBy!((a,b)=>a%i==b%i));
  }
 }
 ----
 /opt/local/include/phobos/std/algorithm/iteration.d(2009): 
 Error: function tmp.main.ChunkByImpl!(__lambda1, 
 int[]).ChunkByImpl.Group.popFront cannot access frame of 
 function D main
 ----

 Is this expected behavior?
No.
 I can get it to run as expected by modifying the source of 
 chunkBy as follows:
It's hard to read this and compare against the original implementation here. If this issue still persists with master, why don't you simply open a PR for Phobos? Bug fixes are always welcome!
Sep 06 2020
parent JG <g g.com> writes:
On Sunday, 6 September 2020 at 17:08:06 UTC, Seb wrote:

 It's hard to read this and compare against the original 
 implementation here.
 If this issue still persists with master, why don't you simply 
 open a PR for Phobos?
 Bug fixes are always welcome!
I compiled dmd and phobos from source. Using the same code as before: ---- import std.algorithm; import std.range; import std.stdio; void main() { auto v = [2,4,8,3,6,9,1,5,7]; foreach(i; 2..4) { writeln(v.myChunkBy!((a,b)=>a%i==b%i)); } } ---- and compiling with the new version of dmd produces: ../../dlang/dmd/generated/osx/release/64/../../../../../phobos/std/algorith /iteration.d(2058): Error: function chunkByFix.main.ChunkByImpl!(__lambda1, int[]).ChunkByImpl.Group.popFront cannot access delegate __lambda1 in frame of function D main I will see if I can figure out how to attempt a PR for phobos.
Sep 07 2020