Thread overview
chunkBy bug?
Sep 05, 2020
JG
Sep 05, 2020
JG
Sep 05, 2020
H. S. Teoh
Sep 05, 2020
JG
Sep 06, 2020
H. S. Teoh
Sep 06, 2020
Seb
Sep 07, 2020
JG
September 05, 2020
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));
 }
}
---
September 05, 2020
Sorry for posting to general. I meant to post to learn.
September 04, 2020
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
September 05, 2020
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?

September 06, 2020
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
September 06, 2020
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!
September 07, 2020
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/algorithm/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.