Bug 22310 – Template instantiation failures can be *very* costly

Status
NEW
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2021-09-15T14:43:04Z
Last change time
2024-12-13T19:18:25Z
Keywords
performance, pull
Assigned to
No Owner
Creator
kinke
Moved to GitHub: dmd#19982 →

Comments

Comment #0 by kinke — 2021-09-15T14:43:04Z
Failed template instantiations appear not to be cached, and this can have a huge effect on compile-time and memory requirements. Here's a standalone version of std.traits.BooleanTypeOf as of v2.097.2 incl. a little benchmark, with `-version=DontFail` yielding a `void` instead of `static assert(0)`, just for illustration: ``` template ModifyTypePreservingTQ(alias Modifier, T) { static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; else alias ModifyTypePreservingTQ = Modifier!T; } template OriginalType(T) { static if (is(T == enum)) { template Impl(T) { static if (is(T U == enum)) alias Impl = OriginalType!U; else alias Impl = T; } alias OriginalType = ModifyTypePreservingTQ!(Impl, T); } else { alias OriginalType = T; } } alias AliasThisTypeOf(T) = typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])); template BooleanTypeOf(T) { static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) alias X = BooleanTypeOf!AT; else alias X = OriginalType!T; static if (is(immutable X == immutable bool)) alias BooleanTypeOf = X; else { version (DontFail) alias BooleanTypeOf = void; else static assert(0, T.stringof~" is not boolean type"); } } alias AliasSeq(T...) = T; // 10 types alias SampleTypes = AliasSeq!(bool, char, wchar, dchar, byte, ubyte, short, ushort, int, uint); void main() { enum count = 5_000; static foreach (i; 0 .. count) foreach (T; SampleTypes) enum _ = is(BooleanTypeOf!T); } ``` Compiling with `-c -vtemplates` using DMD v2.097.2 on Linux x64 takes about 1.0 seconds and ~942 MB of RAM: ``` aliasThisTypeOf.d(34): vtemplate: 50000 (45001 distinct) instantiation(s) of template `BooleanTypeOf(T)` found aliasThisTypeOf.d(32): vtemplate: 45001 (45001 distinct) instantiation(s) of template `AliasThisTypeOf(T)` found aliasThisTypeOf.d(14): vtemplate: 45001 (10 distinct) instantiation(s) of template `OriginalType(T)` found aliasThisTypeOf.d(52): vtemplate: 1 (0 distinct) instantiation(s) of template `AliasSeq(T...)` found ``` Compiling with `-c -vtemplates -version=DontFail` takes ~0.46 seconds and ~308 MB: ``` aliasThisTypeOf.d(34): vtemplate: 50000 (10 distinct) instantiation(s) of template `BooleanTypeOf(T)` found aliasThisTypeOf.d(14): vtemplate: 10 (10 distinct) instantiation(s) of template `OriginalType(T)` found aliasThisTypeOf.d(32): vtemplate: 10 (10 distinct) instantiation(s) of template `AliasThisTypeOf(T)` found aliasThisTypeOf.d(52): vtemplate: 1 (0 distinct) instantiation(s) of template `AliasSeq(T...)` found ``` Note that failing to instantiate BooleanTypeOf!T for non-bool T also seems to lead to all successfully instantiated templates in there not to be cached (AliasThisTypeOf!T, OriginalType!T).
Comment #1 by kinke — 2021-09-15T14:48:47Z
(In reply to kinke from comment #0) > Note that failing to instantiate BooleanTypeOf!T for non-bool T also seems > to lead to all successfully instantiated templates in there not to be cached > (AliasThisTypeOf!T, OriginalType!T). Scratch that, OriginalType!T *is* cached, only AliasThisTypeOf!T isn't, possibly because of the speculative instantiation inside `static if (is(...))`.
Comment #2 by kinke — 2021-09-15T15:09:26Z
(In reply to kinke from comment #1) > only AliasThisTypeOf!T isn't, > possibly because of the speculative instantiation inside `static if > (is(...))`. ... and scratch that too - that's most likely just another manifestation of the same issue, as all AliasThisTypeOf instantiations fail for the testcase.
Comment #3 by dlang-bot — 2021-09-15T17:21:26Z
@kinke created dlang/dmd pull request #13075 "[stable] Fix Issue 22310 - Cache failed template instantiations" fixing this issue: - Fix Issue 22310 - Cache failed template instantiations Semantic is re-run anyway for error messages in case errors were gagged for the first instantiation but aren't gagged for the current instantiation, see line 5645. https://github.com/dlang/dmd/pull/13075
Comment #4 by dlang-bot — 2021-09-21T20:35:18Z
@kinke created dlang/dmd pull request #13093 "Fix Issue 22310 - Cache failed template instantiations" fixing this issue: - Fix Issue 22310 - Cache failed template instantiations Only allow one retry attempt for gagged (speculative) instantiations, which seems to be crucial to make Phobos unittests compile. https://github.com/dlang/dmd/pull/13093
Comment #5 by robert.schadek — 2024-12-13T19:18:25Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19982 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB