[Issue 15600] New: Missing functions in a template struct cause linker errors instead of being caught by compiler.
via Digitalmars-d-bugs
digitalmars-d-bugs at puremagic.com
Sun Jan 24 02:06:06 PST 2016
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 at puremagic.com
Reporter: epi at 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.
--
More information about the Digitalmars-d-bugs
mailing list