D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 12000 - Forward reference issue with RefCounted
Summary: Forward reference issue with RefCounted
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: All All
: P3 normal
Assignee: No Owner
URL:
Keywords:
Depends on: 12008 12009
Blocks:
  Show dependency treegraph
 
Reported: 2014-01-25 18:37 UTC by Andrei Alexandrescu
Modified: 2023-01-16 00:45 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Andrei Alexandrescu 2014-01-25 18:37:19 UTC
Consider:

import std.typecons;

struct GroupBy(R)
{
    struct SharedInput
    {
        Group unused;
    }

    struct Group
    {
        private RefCounted!SharedInput _allGroups;
    }
}

unittest
{
    GroupBy!(int[]) g1;
}

This came about while I was working on #5968. The intent here is to have each group have a backreference to the mother hen of all groups. However, the code does not compile due to protests about forward reference issues (specifically not knowing the size). It should, seeing as RefCounted is just a pointer so the size is not needed to define Group's layout.
Comment 1 Walter Bright 2014-01-25 20:48:39 UTC
Eliminating the dependency on Phobos:
--------------------------------------------
struct RefCounted(T)
{
    struct RefCountedStore
    {
        private struct Impl
        {
            SharedInput _payload;
        }

        private void initialize(A...)(auto ref A args)
        {
            import core.memory;
        }

        void ensureInitialized()
        {
            initialize();
        }

    }
    RefCountedStore _refCounted;

    void opAssign(SharedInput rhs)
    {
    }

    int refCountedPayload()
    {
        _refCounted.ensureInitialized();
        return 0;
    }

    int refCountedPayload() inout;

    alias refCountedPayload this;
}

struct SharedInput
{
    Group unused;
}

struct Group
{
    RefCounted!SharedInput _allGroups;
}
------------------------------------------------
foo.d(6): Error: struct foo.RefCounted!(SharedInput).RefCounted.RefCountedStore.Impl unable to resol
ve forward reference in definition
foo.d(46): Error: template instance foo.RefCounted!(SharedInput) error instantiating
Comment 2 Walter Bright 2014-01-25 21:08:42 UTC
Just noticed this compiles with 2.064, marking as regression
Comment 3 Walter Bright 2014-01-25 21:10:37 UTC
Actually, example 1 still fails with 2.064, while example 2 succeeds.
Comment 4 Kenji Hara 2014-01-27 01:27:41 UTC
(In reply to comment #1)
> Eliminating the dependency on Phobos:
[snip]

I separated compiler regression into issue 12008.

(In reply to comment #0)
> Consider:
> 
> import std.typecons;
> 
> struct GroupBy(R)
> {
>     struct SharedInput
>     {
>         Group unused;
>     }
> 
>     struct Group
>     {
>         private RefCounted!SharedInput _allGroups;
>     }
> }
> 
> unittest
> {
>     GroupBy!(int[]) g1;
> }
> 
> This came about while I was working on #5968. The intent here is to have each
> group have a backreference to the mother hen of all groups. However, the code
> does not compile due to protests about forward reference issues (specifically
> not knowing the size). It should, seeing as RefCounted is just a pointer so the
> size is not needed to define Group's layout.

The root cause is in the implementation of RefCounted.


import std.traits : hasIndirections;

struct RefCounted(T)
{
    ~this()
    {
        static if (hasIndirections!T) { /* ... */ }
    }
}

hasIndirections!T requires the full representation of T to calculate result. Therefore, contrary to the Andrei's thought, current RefCounted does not become just a pointer to T.

To fix the issue, following fix would be necessary.

struct RefCounted(T)
{
    ~this()
    {
        static if (!__traits(compiles, hasIndirections!T) || hasIndirections!T) 
        { /* ... */ }
    }
}

It means that, if hasIndirections!T fails, assume T has forward reference to RefCounted!T.
Comment 5 Kenji Hara 2014-01-27 01:47:30 UTC
I found one more compiler issue.
Issue 12009 - local import and "unable to resolve forward reference" error
Comment 6 monarchdodra 2014-04-05 04:24:32 UTC
See also:
http://forum.dlang.org/thread/wokatnixzkulvmfujamr@forum.dlang.org

Reduced to:
//----
struct S(T) 
{
    static assert(hasIndirections!T);
}

class A(T) 
{
    S!A a;
}

A!int dummy;
//----

In this case, it should work, since you don't need to know about A to know that it's a class, and as such, is an indirection.
Comment 8 github-bugzilla 2014-04-25 23:54:02 UTC
Commit pushed to master at https://github.com/D-Programming-Language/phobos

https://github.com/D-Programming-Language/phobos/commit/bc1876626bbefd54941154733f667012e290a4d3
Re-implement hasIndirections

Avoids requesting the RepresentationTypeTuple of T unless really needed.

Partially solves 12000:
https://d.puremagic.com/issues/show_bug.cgi?id=12000

Fix Issue 12000 - Forward reference issue with RefCounted
Comment 9 Iain Buclaw 2023-01-16 00:45:43 UTC
No longer reproducible, the lack of any reduction makes it unclear if the compiler was at fault.