digitalmars.D.bugs - [Issue 15600] New: Missing functions in a template struct cause
- via Digitalmars-d-bugs (136/136) Jan 24 2016 https://issues.dlang.org/show_bug.cgi?id=15600
https://issues.dlang.org/show_bug.cgi?id=15600 Issue ID: 15600 Summary: Missing functions in a template struct cause linker errors instead of being caught by compiler. Product: D Version: D2 Hardware: x86_64 OS: Linux Status: NEW Severity: major Priority: P1 Component: dmd Assignee: nobody puremagic.com Reporter: epi atari8.info Probably related to 14425 (but that one is marked as fixed). The following is the most reduced case I could get: pipeline.d: ---- module pipeline; struct Pipeline(Stages...) { private template Component(S) { static if (is(S == struct)) alias Component = S; } private template PipelineBuilder(int begin, int cur, int end) { static assert(begin <= end && begin <= cur && cur <= end && end <= Stages.length, "Invalid parameters: " ~ Stages.stringof ~ "[" ~ begin.stringof ~ "," ~ cur.stringof ~ "," ~ end.stringof ~ "]"); static if (cur < end) { alias Cur = Stages[cur]; alias Lhs = PipelineBuilder!(begin, begin, cur); alias Rhs = PipelineBuilder!(cur + 1, cur + 1, end); static if (is(Component!(Lhs.Impl) _Li)) alias LhsImpl = _Li; static if (is(Component!(Rhs.Impl) _Ri)) alias RhsImpl = _Ri; static if (begin + 1 == end && is(Cur _Impl)) { alias Impl = _Impl; pragma(msg, "Match: ", Stages[begin].stringof); } else static if (cur + 1 == end && is(Cur!LhsImpl _Impl)) { alias Impl = _Impl; pragma(msg, "Match: ", Stages[cur .. end].stringof); } else static if (cur == begin && is(Cur!RhsImpl _Impl)) { alias Impl = _Impl; pragma(msg, "Match: ", Stages[begin .. cur + 1].stringof); } else static if (is(Cur!(LhsImpl, RhsImpl) _Impl)) { alias Impl = _Impl; pragma(msg, "Match: ", Stages[begin .. end].stringof); } static if (cur + 1 < end) { alias Next = PipelineBuilder!(begin, cur + 1, end); static if (is(Next.Impl)) alias PipelineBuilder = Next; } } } auto pipe(alias NextStage)() { return Pipeline!(Stages, NextStage)(); } template ImplType() { alias Builder = PipelineBuilder!(0, 0, Stages.length); alias ImplType = Builder.Impl; } void run()() { alias Impl = ImplType!(); static assert(is(Impl), "Could not build pipeline out of the following list of stages: " ~ Stages.stringof); Impl impl; impl.run(); } } auto pipe(alias Stage1)() { return Pipeline!Stage1(); } struct PullPush(Source, Sink) { Source source = void; Sink sink = void; void run() { ubyte[4096] buf; for (;;) { size_t n = source.pull(buf[]); if (n == 0) break; if (sink.push(buf[0 .. n]) < n) break; } } } struct Foo(Source) { Source source; size_t pull(T)(T[] data) { return 0; } auto peek(size_t n) { return source.peek(n); } // (1) void consume(size_t n) {} // (2) } struct NullSink { size_t push(T)(const(T[]) buf) { return buf.length; } } struct NullSource { size_t pull(T)(T[] buf) { return 0; } } unittest { auto s = pipe!NullSource.pipe!Foo.pipe!PullPush.pipe!NullSink; pragma(msg, s.ImplType!().stringof); // (3) // static assert(is(s.ImplType!() == PullPush!(Foo!NullSource, NullSink))); // (4) s.run(); // (5) } ---- Compiling with 2.069.2 and 2.070-b2 gives the same result: $ dmd pipeline.d -main -unittest -allinst Match: NullSink Match: NullSource Match: tuple(Foo(Source)) Match: tuple((NullSource), Foo(Source), PullPush(Source, Sink), (NullSink)) Cur!(Cur!(NullSource), NullSink) pipeline.o: In function `_D8pipeline81__T8PullPushTS8pipeline31__T3FooTS8pipeline10NullSourceZ3FooTS8pipeline8NullSinkZ8PullPush3runMFNaNbNiNfZv': __main.d:(.text._D8pipeline81__T8PullPushTS8pipeline31__T3FooTS8pipeline10NullSourceZ3FooTS8pipeline8NullSinkZ8PullPush3runMFNaNbNiNfZv+0x6e): undefined reference to `_D8pipeline31__T3FooTS8pipeline10NullSourceZ3Foo11__T4pullThZ4pullMFNaNbNiNfAhZm' collect2: error: ld returned 1 exit status --- errorlevel 1 The linker complains about missing function "pure nothrow nogc safe ulong pipeline.Foo!(pipeline.NullSource).Foo.pull!(ubyte).pull(ubyte[])" However, the problem is somewhere else. Uncomment (4), and you'll see that what's actually missing is NullSource.peek(); To fix this, Foo.peek and Foo.consume ((1) and (2)) should be changed to template functions. Interestingly, the error is detected by compiler when the template is instantiated directly: PullPush!(Foo!NullSource, NullSink), but not when Pipeline.ImplType is used, although they both resolve to the same type. --
Jan 24 2016