Occurs when compiling with `-i` and semantic errors have occurred in non-root modules.
Reduced test incoming.
Comment #1 by ibuclaw — 2021-02-06T10:15:56Z
Created attachment 1816
reduced testcase
issue21614.d
---
module issue21614;
real logmdigammaInverse(real y)
{
import imports.issue21614a;
findRoot(1 , y, 2);
}
---
imports/issue21414a.d
---
module imports.issue21614a;
template AliasSeq(TList...)
{
alias AliasSeq = TList;
}
uint formattedWrite(Writer, Char)(Writer , Char)
{
alias spec = FormatSpec!Char();
return 0;
}
struct FormatSpec(Char)
{
import imports.issue21614a;
}
template ElementEncodingType(R)
{
static if (is(R : E[], E))
alias ElementEncodingType = E;
}
struct Appender(A)
{
inout(ElementEncodingType!A)[] data() {
return [];
}
}
Appender!A appender(A)()
{
return Appender!A();
}
immutable(Char)[] format(Char, Args)(Char[] fmt, Args)
{
auto w = appender!(Char[]);
formattedWrite(w, fmt);
return w.data;
}
template Tuple(Specs)
{
template parseSpecs(Specs)
{
alias parseSpecs = AliasSeq!FieldSpec;
}
template FieldSpec {
}
alias fieldSpecs = parseSpecs!Specs;
string injectNamedFields()
{
foreach (i, name; fieldSpecs)
format("%s", i);
return "";
}
struct Tuple
{
mixin(injectNamedFields);
this()
{
}
}
}
T findRoot(T, DF, DT)(DF f, T a, T b, DT tolerance)
{
findRoot(f, a, b, 0, 1, tolerance);
}
T findRoot(T, DF)(DF f, T a, T b)
{
findRoot(f, a, b, false);
}
Tuple!(T) findRoot(T, R, DF, DT)(DF , T , T , R , R , DT )
---
Comment #2 by ibuclaw — 2021-02-06T10:32:29Z
In the unreduced test, a function that is marked `inferRetType` has a return statement, but the return type is inferred as `void` (possibly because semantic never finished for it). Hence the assert is triggered.
Comment #3 by ibuclaw — 2021-02-06T17:23:38Z
Created attachment 1817
reduced test without errors
Attached reduced test that hits the assert error without any compiler errors.
issue21614.d
---
real logmdigammaInverse(real y)
{
import imports.issue21614.numeric;
findRoot(1, y, 2);
return y;
}
---
imports/issue21614/numeric.d
---
module imports.issue21614.numeric;
import imports.issue21614.typecons;
T findRoot(T, DF, DT)(DF f, T a, T b, DT tolerance)
{
immutable r = findRoot(f, a, b, a, b, tolerance);
return r[2];
}
T findRoot(T, DF)(DF f, T a, T b)
{
return findRoot(f, a, b, false);
}
Tuple!(T, T, R) findRoot(T, R, DF, DT)(DF, T, T, R, R, DT)
{
return Tuple!(T, T, R)();
}
---
imports/issue21614/typecons.d
---
module imports.issue21614.typecons;
template AliasSeq(TList...)
{
alias AliasSeq = TList;
}
template staticMap(alias F, T...)
{
static if (T.length == 1)
alias staticMap = AliasSeq!(F!T);
else
alias staticMap = AliasSeq!(staticMap!(F, T[0]), staticMap!(F, T[$/2 .. $]));
}
uint formattedWrite(Writer, Char)(Writer , Char)
{
auto spec = FormatSpec!Char();
return 0;
}
struct FormatSpec(Char)
{
import imports.issue21614.typecons;
}
template ElementEncodingType(R)
{
static if (is(R : E[], E))
alias ElementEncodingType = E;
}
struct Appender(A)
{
inout(ElementEncodingType!A)[] data()
{
return [];
}
}
Appender!A appender(A)()
{
return Appender!A();
}
immutable(Char)[] format(Char, Args...)(Char[] fmt)
{
auto w = appender!(Char[]);
formattedWrite(w, fmt);
return w.data;
}
template Tuple(Specs...)
{
template parseSpecs(Specs...)
{
static if (Specs.length == 0)
alias parseSpecs = AliasSeq!();
static if (is(Specs[0]))
alias parseSpecs = AliasSeq!(FieldSpec!(Specs[0]), parseSpecs!(Specs[1 .. $]));
}
template FieldSpec(T, string s = "")
{
alias Type = T;
alias name = s;
}
alias fieldSpecs = parseSpecs!Specs;
alias extractType(alias spec) = spec.Type;
alias extractName(alias spec) = spec.name;
string injectNamedFields()
{
string decl;
foreach (i; staticMap!(extractName, fieldSpecs))
format(i);
return decl;
}
struct Tuple
{
alias Types = staticMap!(extractType, fieldSpecs);
Types expand;
mixin(injectNamedFields);
alias expand this;
this(Types)
{
}
}
}
---
Comment #4 by ibuclaw — 2021-02-06T17:52:26Z
(In reply to Iain Buclaw from comment #2)
> In the unreduced test, a function that is marked `inferRetType` has a return
> statement, but the return type is inferred as `void` (possibly because
> semantic never finished for it). Hence the assert is triggered.
This assumption was wrong. What really happens is that there's a constructor marked as `inferRetType`. It's body is empty so it is inferred `void`. Then an automatic `return this` statement is appended to the body (because it isCtorDeclaration), and an assert is triggered because the return type is `void`.
Comment #5 by ibuclaw — 2021-02-06T18:38:24Z
(In reply to Iain Buclaw from comment #4)
> (In reply to Iain Buclaw from comment #2)
> > In the unreduced test, a function that is marked `inferRetType` has a return
> > statement, but the return type is inferred as `void` (possibly because
> > semantic never finished for it). Hence the assert is triggered.
> This assumption was wrong. What really happens is that there's a
> constructor marked as `inferRetType`. It's body is empty so it is inferred
> `void`. Then an automatic `return this` statement is appended to the body
> (because it isCtorDeclaration), and an assert is triggered because the
> return type is `void`.
And to add insult to injury, because of a self-reference (the import statement that leads back to current module), during the semantic1 pass of `struct Tuple`, the semantic1 pass of the constructor is skipped, jumping straight to `semantic2`.
Comment #6 by ibuclaw — 2021-02-09T15:09:25Z
Created attachment 1818
Further reduced test without errors
Reduced the test down even further. Now it is just two modules ~25 sloc.
issue21614.d
---
void logmdigammaInverse(real y)
{
import imports.issue21614a;
findRoot(y);
}
---
imports/issue21614a.d
---
module imports.issue21614a;
struct FormatSpec(Char)
{
import imports.issue21614a;
}
template Tuple(Specs...)
{
struct Tuple
{
alias spec = FormatSpec!char();
this(Specs)
{
}
}
}
auto findRoot(T)(T)
{
return Tuple!(T)();
}
---
Comment #7 by dlang-bot — 2021-02-09T19:17:25Z
@ibuclaw created dlang/dmd pull request #12191 "fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure" fixing this issue:
- fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure
Template instances may import modules that have not finished semantic1,
and in the case of cyclic imports, semantic2 could be ran on a function
symbol before semantic1 has begun, which can lead to inferred functions
given the wrong return type.
https://github.com/dlang/dmd/pull/12191
Comment #8 by dlang-bot — 2021-02-11T14:57:01Z
dlang/dmd pull request #12191 "fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure" was merged into stable:
- 7637e61675b2249d640c44ffc792fff4d1fecb69 by Iain Buclaw:
fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure
Template instances may import modules that have not finished semantic1,
and in the case of cyclic imports, semantic2 could be ran on a function
symbol before semantic1 has begun, which can lead to auto functions
given the wrong return type.
https://github.com/dlang/dmd/pull/12191
Comment #9 by dlang-bot — 2021-02-13T12:30:04Z
dlang/dmd pull request #12195 "Merge stable" was merged into master:
- 599c1b32a92d6d7606b3ec475d2229cde2d4a412 by Iain Buclaw:
fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure
Template instances may import modules that have not finished semantic1,
and in the case of cyclic imports, semantic2 could be ran on a function
symbol before semantic1 has begun, which can lead to auto functions
given the wrong return type.
https://github.com/dlang/dmd/pull/12195
Comment #10 by dlang-bot — 2021-04-02T05:45:31Z
dlang/dmd pull request #12339 "[dmd-cxx] Backport fixes and trivial features from upstream dmd" was merged into dmd-cxx:
- 6692347215181ee125177cf23ff71e766cd1f5ef by Iain Buclaw:
[dmd-cxx] fix Issue 21614 - AssertError@src/dmd/semantic3.d(812): Assertion failure
https://github.com/dlang/dmd/pull/12339