Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a global convenience package file #5916

Merged
merged 1 commit into from
Feb 9, 2018

Conversation

wilzbach
Copy link
Member

@wilzbach wilzbach commented Dec 9, 2017

So after I have been thinking about this for quite a while, I realized that I am almost weekly in a case where import std would make my life easier.
Other reasons include:

  • Very convenient for short scripts, REPLs and things like run.dlang.io
  • Promotes and test the idiom of paying for what you use
  • Phobos is usually almost entirely parsed anyways
  • A good use case for making imports even more lazily and maybe pushing DIP1005 forward
  • It doesn't hurt/cost anything for people who don't like this

For now I didn't add this file to the documentation build.
Also I didn't include std.experimental.

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @wilzbach!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

@adamdruppe
Copy link
Contributor

Even if you don't intend to documentation-build it yourself, could you do me a favor and add:

/++
Phobos is the D standard library, built on top of druntime.
+/
module std;

to the top of it? will save me a small merge issue when i pull to adrdox again (I actually use a package file to create a landing page for the library https://dpldocs.info/experimental-docs/std.html )

@andralex
Copy link
Member

andralex commented Dec 9, 2017

I have reservations about this.

  • It's not something commonly done in languages. There's no such thing in C++. Wildcard imports in Java and Python seem to be at least controversial.

  • It's a strong commitment - indiscriminately fetch all symbols defined by the standard library.

  • Ran a test, we're not yet there in terms of efficiency of unused imports. Compiling a file that just imports std takes 2.1 seconds on a Macbook. That's a large upfront cost.

That said, it wouldn't be the first time we do something that has traditionally be shunned. We do need a fresh angle on it and a clear case that it steers clear of issues while reaping good advantages. For example, it would be a great showcase of fast compilation if time spent on importing unused imports were negligible. cc @WalterBright

@JackStouffer
Copy link
Member

This is one of those changes where I see little/no reason to disallow it (and any argument against it can be made for disallowing import std.algorithm; IMO), but the marginal improvements it provides are small.

I can see how this can be useful for prototyping or just figuring something out. E.g. yesterday I forgot how nans propagated in various math and comparison functions, so I went over to dpaste to write up a script. I would have probably used this then.

Also, it's useful to remember that it's the little conveniences which make people fall in love with languages, e.g. list comprehensions in Python.

The only technical issue I could see from this is if two symbols in different modules have the same name, in which case they could add a static import and use the FQN.

@wilzbach
Copy link
Member Author

Even if you don't intend to documentation-build it yourself, could you do me a favor and add:

Sure. Done. I also added a changelog entry.

Ran a test, we're not yet there in terms of efficiency of unused imports. Compiling a file that just imports std takes 2.1 seconds on a Macbook. That's a large upfront cost.

On my machine it's ~1.5s, but at least for myself that's something I am more than willing to accept in exchange for the time saved typing all the imports.

For example, it would be a great showcase of fast compilation if time spent on importing unused imports were negligible.

The way I see import std is already quite usable today and by making it easily accessible, we are putting a bit of attention into making the Phobos more lazy and faster - sth. that probably won't happen otherwise. For many D libraries out there import <lib> is already a common pattern and offered by them with, see e.g. Vibe.d.

Anyhow I wrote a script to check the status quo of the import loading time of each module:

module import time
std.regex 1.03s
std.net.curl 0.28s
std.concurrency 0.23s
std.encoding 0.18s
std.zip 0.16s
std.path 0.14s
std.file 0.14s
std.mmfile 0.14s
std.datetime 0.14s
std.socket 0.12s
std.string 0.10s
std.uni 0.09s
std.process 0.05s
std.zlib 0.04s
std.getopt 0.04s
std.bigint 0.04s
std.numeric 0.04s
std.bitmanip 0.04s
std.algorithm 0.04s
std.uuid 0.03s
std.range 0.03s
std.parallelism 0.03s
std.utf 0.02s
std.math 0.02s
std.json 0.02s
std.conv 0.02s
std.stdio 0.02s
std.variant 0.02s
std.signals 0.02s
std.complex 0.02s
std.typecons 0.02s
std.container 0.02s
std.mathspecial 0.02s
std.uri 0.01s
std.csv 0.01s
std.meta 0.01s
std.array 0.01s
std.traits 0.01s
std.random 0.01s
std.format 0.01s
std.digest 0.01s
std.base64 0.01s
std.exception 0.01s
std.functional 0.01s
std.xml 0.00s
std.ascii 0.00s
std.system 0.00s
std.stdint 0.00s
std.demangle 0.00s
std.compiler 0.00s
std.outbuffer 0.00s

(the script did nothing more than echo "import std.<module>;" > test.d && dmd -c test.d)

@wilzbach
Copy link
Member Author

The only technical issue I could see from this is if two symbols in different modules have the same name, in which case they could add a static import and use the FQN.

Well that problem already exists today if you e.g. import std.stdio and std.file and want to use write, but AFAIK that's the only collision so far in Phobos and I thought that are/were attempts at fixing this mistake?
Anyhow, as you mentioned the same strategies to avoid collision that are used for std.(file|stdio).write can be used e.g. static imports or renamed imports.
Also as the import std is intended for quick one-off script, prototyping or REPLs, I doubt that this will be a bigger problem than the existing annoyance of std.(file|stdio).write in practice.

@GassaFM
Copy link
Contributor

GassaFM commented Dec 13, 2017

I'd like to have such option.

In GCC version of C++, there's #include <bits/stdc++.h> (include everything standard) and using namespace std; (put that everything into global namespace). Sure, this is considered bad practice in general, but in competitive programming, the area I'm in, it's in fact widely used. The reason is that a typical program in competitive programming is a solution written for a specific algorithmic problem: it's usually no longer than a hundred lines, it has no further use when the problem is solved, and seconds saved on carefully typing the #includes and the std::s everywhere can actually matter.

I use D for competitive programming solutions whenever I can, and I would like to have an option to just import std; instead of adding six imports when (1) my program is just readln.splitter.map!(to!int).array; and a few more lines of some trivial logic and (2) most likely no one would read it except the automated testing system.

I'd like to reiterate that std.regex adds a real slowdown to compiling - even more so in debug mode - to the point I tend to avoid it in smaller programs, just to have a faster REPL-like workflow. On the other hand, I'd also like to reiterate that, when the import std; option is there, people soon start to complain on D.learn and elsewhere, and fast Phobos compilation in general becomes more of a priority, which may be a net win.

@joakim-noah
Copy link
Contributor

No opinion on this pull, but doesn't rdmd already enable this?

@quickfur
Copy link
Member

@joakim-noah IIRC, rdmd hard-codes a bunch of "usual imports" from Phobos by default. I don't think it actually covers every Phobos module. But I could be wrong.

@wilzbach
Copy link
Member Author

Rdmd doesn't do this, but it has a --eval flag which allows to evaluate D code without needing a file by passing strings directly on the CLI:

https://github.com/dlang/tools/blob/master/rdmd.d#L823

For this most of the Phobos world is imported, but it's not really related to this PR as --eval is a poor man's REPL.
While it shows that even in the past people have been frustrated about adding a lot of imports, it can't be used instead of this PR (--eval is strings only and the entire point of having something like import std is that all regular tools still work without needing amy tweaks: linting, completion, ...)

@andralex
Copy link
Member

Cool, thanks for the info @GassaFM. @DmitryOlshansky is looking into the regex slowdown and @wilzbach into the others. I think we have a good chance to accelerate their compilation significantly.

BTW I just timed the inclusion of <bits/stdc++.h> and it compiled under 1 second - nice job gcc. Total lines after preprocessing add up to 133.5 KLOC. (According to a comment in <bits/stdc++.h>, precompiled headers might be used.)

@wilzbach
Copy link
Member Author

It's not something commonly done in languages.

As @andralex asked me a to look a bit at other languages and how they handle/support/recommend wildcard imports.
Hence, here's a quick overview of how languages handle wildcard imports:
(this is an incomplete list and will be improved over time)

Language wild card imports
Python Paradigm: keep imports tiny. More imports => more work for the interpreter => slower startup
Scala has wild card imports: import scalaz._ see e.g. scalaz
Java has wild card imports, but not very liked due to "code maintainability and willingness to deal with import ambiguities " (there's no static import in Java)
Rust Rust doc says: "The * is called a glob, and it will import all items visible inside the namespace. You should use globs sparingly: they are convenient, but this might also pull in more items than you expected and cause naming conflicts.". As of Rust 1.14 they even support use *
Go Go's dot imports import all symbols in the current scope: import ( . "fmt" ). However, it's not considered good practice: "Dot imports. If a program imports a standard package using import . "path", additional names defined in the imported package in future releases may conflict with other names defined in the program. We do not recommend the use of import . outside of tests, and using it may cause a program to fail to compile in future releases."
NodeJs require-all has 219k monthly downloads. There's no real wildcard import

For completeness, for C++ there's this StackOverflow post on why <bits/stdc++.h> should be avoided. The reasoning of the author is:

  • It will probably only work on that compiler (doesn't apply to D - LDC and GDC use the same module system and standard library)
  • You have no idea what it'll do when you use it, because its contents are not set by a standard (in D: you will get the entire standard library)
  • Every single standard header must be parsed and compiled along with your source code, which is slow and results in a bulky executable under certain compilation settings (_the later bit doesn't apply to D and hopefully we make the modules more lazily so that import std is not even noticeable. Also as mentioned before even for a hello world a huge chunk of the standard library gets parsed. _)
  • Even just upgrading your compiler to its own next version may break your program

Imho the last point is the only valid concern which affects D too. I don't see this as a big concern as the import std-feature is intended for experimentation, REPLs and one-off scripts. However, not many symbols are added to Phobos and currently the chances are a lot higher that Walter fixed a safety hole in the compiler which "breaks" your program.

Lastly, there's also the idea of putting this in sth. like std.experimental.all which should be a lot less controversial and allows us to collect feedback for this without putting it into stone.

@joakim-noah
Copy link
Contributor

@wilzbach, OK, I thought rdmd also enabled writing scripts without listing your imports, though I'd obviously never tried it, since it apparently doesn't.

I don't think speed is that big a deal, but that affording this convenience now means we'll see a bunch of D code that simply uses import std; instead of listing their imports. However, I'm about to start working on an import scoping tool that you can run on D files and have it automatically scope your imports- working on that tool finally hit the top of my queue- so we could always make it easy for them to automatically add the right imports, when needed. Looking forward to using your dmd frontend dub package for that eventually, dlang/dmd#7425, though I'm not waiting on it and will just hack up the frontend myself for now.

@wilzbach
Copy link
Member Author

However, I'm about to start working on an import scoping tool that you can run on D files and have it automatically scope your imports

Cool. Looking forward!
FYI: @CyberShadow has a very crude import fixer:
https://github.com/CyberShadow/AutoFix/blob/master/dautofix.d

Looking forward to using your dmd frontend dub package for that eventually, dlang/dmd#7425, though I'm not waiting on it and will just hack up the frontend myself for now.

Hehe that's good! I wouldn't count on the DUB package being "usable" in the next few weeks - there just seem to be too many outstanding issues with it.

@CyberShadow
Copy link
Member

FYI: @CyberShadow has a very crude import fixer:

It's not that crude...

It parses and caches JSON data emitted by DMD, and emits editor-agnostic edit instructions for each option. It also recognizes typos and such.

Here's the Emacs half: https://dump.thecybershadow.net/6adf07f0a11fde8ba11dedfa4309ecab/dfix.el

I'm waiting until the Flycheck guys finally implement flycheck/flycheck#530 so that it could be built into d-mode or something.

@andralex
Copy link
Member

Of the concerns mentioned by other languages, the one that new symbols in the library may break the program is the most important. See also https://github.com/dlang/DIPs/blob/master/DIPs/DIP1007.md.

One thing we could do from a naming perspective is to replace import std; (short, attractive, official-looking) with import std.scripting; (lowers expectations).

@JackStouffer
Copy link
Member

One thing we could do from a naming perspective is to replace import std; (short, attractive, official-looking) with import std.scripting;

This would also give room for us to add various useful scripting functions to the module in the future.

@wilzbach
Copy link
Member Author

is looking into the regex slowdown and @wilzbach into the others

Just a quick status update:
So the biggest slowing factor of std.net.curl is it's import of std.concurrency
In std.concurrency the problem is that we have Message which uses std.variant.Variant:

struct Message
{
    Variant data;
}

An example:

import std.variant;
semantic  bar
semantic2 bar
semantic3 bar
dmd -v -c -o- bar.d -I..  0.02s user 0.00s system 99% cpu 0.024 total

and now let's define a Variant variable:

import std.variant;
Variant d;
semantic  bar
import    std.algorithm.comparison	(../std/algorithm/comparison.d)
import    std.range.primitives	(../std/range/primitives.d)
import    std.algorithm.internal	(../std/algorithm/internal.d)
semantic2 bar
semantic3 bar
import    core.stdc.string	(/usr/include/dlang/dmd/core/stdc/string.d)
import    std.conv	(../std/conv.d)
import    std.ascii	(../std/ascii.d)
import    std.exception	(../std/exception.d)
import    std.format	(../std/format.d)
import    core.vararg	(/usr/include/dlang/dmd/core/vararg.d)
import    std.array	(../std/array.d)
import    std.algorithm.iteration	(../std/algorithm/iteration.d)
import    core.memory	(/usr/include/dlang/dmd/core/memory.d)
import    std.algorithm.searching	(../std/algorithm/searching.d)
import    std.bitmanip	(../std/bitmanip.d)
import    std.system	(../std/system.d)
import    core.internal.string	(/usr/include/dlang/dmd/core/internal/string.d)
import    core.bitop	(/usr/include/dlang/dmd/core/bitop.d)
import    std.utf	(../std/utf.d)
import    core.checkedint	(/usr/include/dlang/dmd/core/checkedint.d)
import    std.string	(../std/string.d)
import    std.uni	(../std/uni.d)
import    std.internal.unicode_tables	(../std/internal/unicode_tables.d)
import    std.range	(../std/range/package.d)
import    std.range.interfaces	(../std/range/interfaces.d)
import    std.algorithm.mutation	(../std/algorithm/mutation.d)
dmd -v -c -o- bar.d -I..  0.20s user 0.03s system 99% cpu 0.227 total

So yeah having a std.variant.Variant variable in a non-template cost us ~0.2s in compilation time.

@andralex
Copy link
Member

andralex commented Dec 15, 2017

@wilzbach great work on variant! I've investigated a bit more and found tuple to be the culprit, i.e. the cost of importing std.variant is almost 100% caused by the use of std.tuple. Look at this program:

import std.typecons;
Tuple!(int, double) t;

The variable definition adds a large compilation time.

@andralex
Copy link
Member

Further investigating the matter, it looks like most time is being spent in this function:

    // Generates named fields as follows:
    //    alias name_0 = Identity!(field[0]);
    //    alias name_1 = Identity!(field[1]);
    //      :
    // NOTE: field[k] is an expression (which yields a symbol of a
    //       variable) and can't be aliased directly.
    string injectNamedFields()
    {
        string decl = "";
        foreach (i, name; staticMap!(extractName, fieldSpecs))
        {
            import std.format : format;

            decl ~= format("alias _%s = Identity!(field[%s]);", i, i);
            if (name.length != 0)
            {
                decl ~= format("alias %s = _%s;", name, i);
            }
        }
        return decl;
    }

Replacing the function with return ""; accelerates compilation time 10x.

@wilzbach
Copy link
Member Author

Replacing the function with return ""; accelerates compilation time 10x.

Wow. Excellent catch!
A very dumb approach to making it faster already reduces the import time from 0.18s to 0.04s!!

static foreach (i, name; staticMap!(extractName, fieldSpecs))
{{
    import std.conv : to;
    const iStr = i.to!string;
    decl ~= "alias _" ~ iStr ~ " = Identity!(field[" ~ iStr ~"]);";
    if (name.length != 0)
        decl ~= "alias " ~ name ~ " = _"~ iStr ~";";
}}

CC @UplinkCoder

@andralex
Copy link
Member

#5931

@wilzbach
Copy link
Member Author

wilzbach commented Feb 4, 2018

That comes out to about 2.5 seconds right? I think that's reasonable.

0.42s on my system with debug dmd - as mentioned std.net.curl already imports the entire world.
When I build dmd with RELEASE=1 HOST_DC=ldmd it goes down to 0.25s, which is how we ship dmd on all distributions.

I would also ask that before there's any movement forward on this that you consider Andrei's std.scripting suggestion.

Ok. Even if I don't really like it, we should put it in experimental for a short while (Andrei suggest this).
I hope that it doesn't stay there too long though.

Anyhow experimental gives some exposure to this and hopefully enough motivation to move it to stable and/or make it even faster by cleaning up even more imports.

@CyberShadow
Copy link
Member

CyberShadow commented Feb 4, 2018

Anyhow I wrote a script to check the status quo of the import loading time of each module:

We have a much better tool for this: https://github.com/CyberShadow/DBuildStat

Note that it's not std.net.curl which takes so long to import, but all the modules it imports.

It doesn't look like it:

@CyberShadow
Copy link
Member

CyberShadow commented Feb 4, 2018

Here is a random sample stacktrace of DMD parsing std.net.curl:

Thread 1 (Thread 0x7f9b57891880 (LWP 20537)):
#0  0x00007f9b564743a4 in malloc () from /usr/lib/libc.so.6
#1  0x00000000007d3727 in Mem::xmalloc(unsigned long) ()
#2  0x00000000005d0912 in UnionExp::copy() ()
#3  0x00000000005682e5 in Interpreter::interpretCommon(BinExp*, UnionExp (*)(Loc, Type*, Expression*, Expression*)) ()
#4  0x0000000000568938 in Interpreter::visit(BinExp*) ()
#5  0x00000000006e6e32 in ParseTimeVisitor<ASTCodegen>::visit(MinExp*) ()
#6  0x00000000005df541 in MinExp::accept(Visitor*) ()
#7  0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#8  0x000000000056dcbd in Interpreter::resolveIndexing(IndexExp*, InterState*, Expression**, unsigned long*, bool) ()
#9  0x000000000056e2da in Interpreter::visit(IndexExp*) ()
#10 0x00000000005dea31 in IndexExp::accept(Visitor*) ()
#11 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#12 0x0000000000561551 in Interpreter::visit(ReturnStatement*) ()
#13 0x00000000006d6b89 in ReturnStatement::accept(Visitor*) ()
#14 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#15 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#16 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#17 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#18 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#19 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#20 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#21 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#22 0x0000000000569759 in Interpreter::interpretAssignCommon(BinExp*, UnionExp (*)(Loc, Type*, Expression*, Expression*), int) ()
#23 0x000000000056c021 in Interpreter::visit(AssignExp*) ()
#24 0x00000000006e7472 in Visitor::visit(ConstructExp*) ()
#25 0x00000000005ded81 in ConstructExp::accept(Visitor*) ()
#26 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#27 0x0000000000564462 in Interpreter::visit(DeclarationExp*) ()
#28 0x00000000005da8a9 in DeclarationExp::accept(Visitor*) ()
#29 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#30 0x0000000000560eca in Interpreter::visit(ExpStatement*) ()
#31 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#32 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#33 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#34 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#35 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#36 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#37 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#38 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#39 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#40 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#41 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#42 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#43 0x0000000000560eca in Interpreter::visit(ExpStatement*) ()
#44 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#45 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#46 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#47 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#48 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#49 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#50 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#51 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#52 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#53 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#54 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#55 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#56 0x0000000000560eca in Interpreter::visit(ExpStatement*) ()
#57 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#58 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#59 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#60 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#61 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#62 0x00000000005611e4 in Interpreter::visit(ScopeStatement*) ()
#63 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#64 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#65 0x000000000056116d in Interpreter::visit(IfStatement*) ()
#66 0x00000000006d59f9 in IfStatement::accept(Visitor*) ()
#67 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#68 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#69 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#70 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#71 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#72 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#73 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#74 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#75 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#76 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#77 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#78 0x0000000000569759 in Interpreter::interpretAssignCommon(BinExp*, UnionExp (*)(Loc, Type*, Expression*, Expression*), int) ()
#79 0x000000000056c021 in Interpreter::visit(AssignExp*) ()
#80 0x00000000005dec51 in AssignExp::accept(Visitor*) ()
#81 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#82 0x0000000000560eca in Interpreter::visit(ExpStatement*) ()
#83 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#84 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#85 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#86 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#87 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#88 0x0000000000561b47 in Interpreter::visit(ForStatement*) ()
#89 0x00000000006d5519 in ForStatement::accept(Visitor*) ()
#90 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#91 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#92 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#93 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#94 0x00000000005611e4 in Interpreter::visit(ScopeStatement*) ()
#95 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#96 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#97 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#98 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#99 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#100 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#101 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#102 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#103 0x000000000056246f in Interpreter::visit(TryFinallyStatement*) ()
#104 0x00000000006d74f1 in TryFinallyStatement::accept(Visitor*) ()
#105 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#106 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#107 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#108 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#109 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#110 0x00000000005614e2 in Interpreter::visit(ReturnStatement*) ()
#111 0x00000000006d6b89 in ReturnStatement::accept(Visitor*) ()
#112 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#113 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#114 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#115 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#116 0x000000000056246f in Interpreter::visit(TryFinallyStatement*) ()
#117 0x00000000006d74f1 in TryFinallyStatement::accept(Visitor*) ()
#118 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#119 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#120 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#121 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#122 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#123 0x0000000000560eca in Interpreter::visit(ExpStatement*) ()
#124 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#125 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#126 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#127 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#128 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#129 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#130 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#131 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#132 0x000000000056246f in Interpreter::visit(TryFinallyStatement*) ()
#133 0x00000000006d74f1 in TryFinallyStatement::accept(Visitor*) ()
#134 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#135 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#136 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#137 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#138 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#139 0x0000000000561551 in Interpreter::visit(ReturnStatement*) ()
#140 0x00000000006d6b89 in ReturnStatement::accept(Visitor*) ()
#141 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#142 0x000000000056116d in Interpreter::visit(IfStatement*) ()
#143 0x00000000006d59f9 in IfStatement::accept(Visitor*) ()
#144 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#145 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#146 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#147 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#148 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#149 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#150 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#151 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#152 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#153 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#154 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#155 0x0000000000561551 in Interpreter::visit(ReturnStatement*) ()
#156 0x00000000006d6b89 in ReturnStatement::accept(Visitor*) ()
#157 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#158 0x0000000000560f48 in Interpreter::visit(CompoundStatement*) ()
#159 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#160 0x000000000057166c in dmd.dinterpret.interpret(dmd.statement.Statement, dmd.dinterpret.InterState*) ()
#161 0x0000000000560963 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#162 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#163 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#164 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#165 0x0000000000560541 in dmd.dinterpret.interpret(dmd.func.FuncDeclaration, dmd.dinterpret.InterState*, dmd.root.array.Array!(dmd.expression.Expression).Array*, dmd.expression.Expression) ()
#166 0x000000000056d12b in Interpreter::visit(CallExp*) ()
#167 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#168 0x0000000000571605 in dmd.dinterpret.interpret(dmd.expression.Expression, dmd.dinterpret.InterState*, dmd.dinterpret.CtfeGoal) ()
#169 0x000000000055fcb8 in ctfeInterpret(Expression*) ()
#170 0x0000000000645e66 in InitializerSemanticVisitor::visit(ExpInitializer*) ()
#171 0x0000000000643d59 in ExpInitializer::accept(Visitor*) ()
#172 0x000000000059ca49 in DsymbolSemanticVisitor::visit(VarDeclaration*) ()
#173 0x00000000005594b1 in VarDeclaration::accept(Visitor*) ()
#174 0x00000000005ef8e9 in ExpressionSemanticVisitor::visit(DeclarationExp*) ()
#175 0x00000000005da8a9 in DeclarationExp::accept(Visitor*) ()
#176 0x00000000006ec66c in StatementSemanticVisitor::visit(ExpStatement*) ()
#177 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#178 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#179 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#180 0x000000000070ac0a in Semantic3Visitor::visit(FuncDeclaration*) ()
#181 0x000000000060e691 in FuncDeclaration::accept(Visitor*) ()
#182 0x0000000000708cfd in Semantic3Visitor::visit(TemplateInstance*) ()
#183 0x00000000005c6d01 in TemplateInstance::accept(Visitor*) ()
#184 0x00000000005c6cc5 in TemplateInstance::trySemantic3(Scope*) ()
#185 0x00000000005afe81 in dmd.dsymbolsem.templateInstanceSemantic(dmd.dtemplate.TemplateInstance, dmd.dscope.Scope*, dmd.root.array.Array!(dmd.expression.Expression).Array*) ()
#186 0x00000000005b9036 in dmd.dtemplate.functionResolve(dmd.declaration.Match*, dmd.dsymbol.Dsymbol, dmd.globals.Loc, dmd.dscope.Scope*, dmd.root.array.Array!(dmd.root.rootobject.RootObject).Array*, dmd.mtype.Type, dmd.root.array.Array!(dmd.expression.Expression).Array*) ()
#187 0x000000000060f3bb in resolveFuncCall(Loc, Scope*, Dsymbol*, Array<RootObject*>*, Type*, Array<Expression*>*, int) ()
#188 0x00000000005e289a in dmd.expressionsem.resolvePropertiesX(dmd.dscope.Scope*, dmd.expression.Expression, dmd.expression.Expression) ()
#189 0x000000000069b6fb in op_overload::OpOverload::visit(ArrayExp*) ()
#190 0x00000000005de1f9 in ArrayExp::accept(Visitor*) ()
#191 0x000000000069a719 in op_overload(Expression*, Scope*) ()
#192 0x00000000005f8833 in ExpressionSemanticVisitor::visit(ArrayExp*) ()
#193 0x00000000005de1f9 in ArrayExp::accept(Visitor*) ()
#194 0x0000000000602af9 in ExpressionSemanticVisitor::visit(LogicalExp*) ()
#195 0x00000000005dfad1 in LogicalExp::accept(Visitor*) ()
#196 0x00000000006f664a in StatementSemanticVisitor::visit(IfStatement*) ()
#197 0x00000000006d59f9 in IfStatement::accept(Visitor*) ()
#198 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#199 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#200 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#201 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#202 0x00000000006f9846 in StatementSemanticVisitor::visit(CaseStatement*) ()
#203 0x00000000006d6661 in CaseStatement::accept(Visitor*) ()
#204 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#205 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#206 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#207 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#208 0x00000000006f7ebe in StatementSemanticVisitor::visit(SwitchStatement*) ()
#209 0x00000000006d6511 in SwitchStatement::accept(Visitor*) ()
#210 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#211 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#212 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#213 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#214 0x00000000006eefc7 in StatementSemanticVisitor::visit(ForStatement*) ()
#215 0x00000000006d5519 in ForStatement::accept(Visitor*) ()
#216 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#217 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#218 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#219 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#220 0x0000000000700e77 in dmd.statementsem.semanticScope(dmd.statement.Statement, dmd.dscope.Scope*, dmd.statement.Statement, dmd.statement.Statement) ()
#221 0x00000000006f6850 in StatementSemanticVisitor::visit(IfStatement*) ()
#222 0x00000000006d59f9 in IfStatement::accept(Visitor*) ()
#223 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#224 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#225 0x000000000070ac0a in Semantic3Visitor::visit(FuncDeclaration*) ()
#226 0x000000000060e691 in FuncDeclaration::accept(Visitor*) ()
#227 0x0000000000609159 in FuncDeclaration::functionSemantic3() ()
#228 0x0000000000609093 in FuncDeclaration::functionSemantic() ()
#229 0x00000000005cd4b2 in dmd.expression.resolve(dmd.globals.Loc, dmd.dscope.Scope*, dmd.dsymbol.Dsymbol, bool) ()
#230 0x00000000005e615a in ExpressionSemanticVisitor::visit(IdentifierExp*) ()
#231 0x00000000005d5c81 in IdentifierExp::accept(Visitor*) ()
#232 0x0000000000605913 in dmd.expressionsem.unaSemantic(dmd.expression.UnaExp, dmd.dscope.Scope*) ()
#233 0x00000000005ebf57 in ExpressionSemanticVisitor::visit(CallExp*) ()
#234 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#235 0x0000000000646d34 in InferTypeVisitor::visit(ExpInitializer*) ()
#236 0x0000000000643d59 in ExpInitializer::accept(Visitor*) ()
#237 0x0000000000599cfd in DsymbolSemanticVisitor::visit(VarDeclaration*) ()
#238 0x00000000005594b1 in VarDeclaration::accept(Visitor*) ()
#239 0x00000000005ef8e9 in ExpressionSemanticVisitor::visit(DeclarationExp*) ()
#240 0x00000000005da8a9 in DeclarationExp::accept(Visitor*) ()
#241 0x00000000006ec66c in StatementSemanticVisitor::visit(ExpStatement*) ()
#242 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#243 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#244 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#245 0x000000000070ac0a in Semantic3Visitor::visit(FuncDeclaration*) ()
#246 0x000000000060e691 in FuncDeclaration::accept(Visitor*) ()
#247 0x000000000070e84a in Semantic3Visitor::visit(AttribDeclaration*) ()
#248 0x00000000006e664f in ParseTimeVisitor<ASTCodegen>::visit(StorageClassDeclaration*) ()
#249 0x00000000005128f9 in StorageClassDeclaration::accept(Visitor*) ()
#250 0x000000000070e98e in Semantic3Visitor::visit(AggregateDeclaration*) ()
#251 0x00000000006e66e2 in ParseTimeVisitor<ASTCodegen>::visit(ClassDeclaration*) ()
#252 0x0000000000555991 in ClassDeclaration::accept(Visitor*) ()
#253 0x0000000000708cfd in Semantic3Visitor::visit(TemplateInstance*) ()
#254 0x00000000005c6d01 in TemplateInstance::accept(Visitor*) ()
#255 0x00000000005afc40 in dmd.dsymbolsem.templateInstanceSemantic(dmd.dtemplate.TemplateInstance, dmd.dscope.Scope*, dmd.root.array.Array!(dmd.expression.Expression).Array*) ()
#256 0x00000000005b9036 in dmd.dtemplate.functionResolve(dmd.declaration.Match*, dmd.dsymbol.Dsymbol, dmd.globals.Loc, dmd.dscope.Scope*, dmd.root.array.Array!(dmd.root.rootobject.RootObject).Array*, dmd.mtype.Type, dmd.root.array.Array!(dmd.expression.Expression).Array*) ()
#257 0x000000000060f3bb in resolveFuncCall(Loc, Scope*, Dsymbol*, Array<RootObject*>*, Type*, Array<Expression*>*, int) ()
#258 0x00000000005ede31 in ExpressionSemanticVisitor::visit(CallExp*) ()
#259 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#260 0x00000000005cfad8 in arrayExpressionSemantic(Array<Expression*>*, Scope*, bool) ()
#261 0x00000000005ec319 in ExpressionSemanticVisitor::visit(CallExp*) ()
#262 0x00000000005dd021 in CallExp::accept(Visitor*) ()
#263 0x0000000000646d34 in InferTypeVisitor::visit(ExpInitializer*) ()
#264 0x0000000000643d59 in ExpInitializer::accept(Visitor*) ()
#265 0x0000000000599cfd in DsymbolSemanticVisitor::visit(VarDeclaration*) ()
#266 0x00000000005594b1 in VarDeclaration::accept(Visitor*) ()
#267 0x00000000005ef8e9 in ExpressionSemanticVisitor::visit(DeclarationExp*) ()
#268 0x00000000005da8a9 in DeclarationExp::accept(Visitor*) ()
#269 0x00000000006ec66c in StatementSemanticVisitor::visit(ExpStatement*) ()
#270 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#271 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#272 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#273 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#274 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#275 0x00000000006f67f2 in StatementSemanticVisitor::visit(IfStatement*) ()
#276 0x00000000006d59f9 in IfStatement::accept(Visitor*) ()
#277 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#278 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#279 0x00000000006ee127 in StatementSemanticVisitor::visit(ScopeStatement*) ()
#280 0x00000000006d4bf9 in ScopeStatement::accept(Visitor*) ()
#281 0x0000000000700e77 in dmd.statementsem.semanticScope(dmd.statement.Statement, dmd.dscope.Scope*, dmd.statement.Statement, dmd.statement.Statement) ()
#282 0x00000000006fe6cc in StatementSemanticVisitor::visit(TryCatchStatement*) ()
#283 0x00000000006d7251 in TryCatchStatement::accept(Visitor*) ()
#284 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#285 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#286 0x000000000070ac0a in Semantic3Visitor::visit(FuncDeclaration*) ()
#287 0x00000000006e6512 in ParseTimeVisitor<ASTCodegen>::visit(FuncLiteralDeclaration*) ()
#288 0x0000000000611b59 in FuncLiteralDeclaration::accept(Visitor*) ()
#289 0x00000000005eaea8 in ExpressionSemanticVisitor::visit(FuncExp*) ()
#290 0x00000000005da781 in FuncExp::accept(Visitor*) ()
#291 0x0000000000646d34 in InferTypeVisitor::visit(ExpInitializer*) ()
#292 0x0000000000643d59 in ExpInitializer::accept(Visitor*) ()
#293 0x0000000000599cfd in DsymbolSemanticVisitor::visit(VarDeclaration*) ()
#294 0x00000000005594b1 in VarDeclaration::accept(Visitor*) ()
#295 0x00000000005ef8e9 in ExpressionSemanticVisitor::visit(DeclarationExp*) ()
#296 0x00000000005da8a9 in DeclarationExp::accept(Visitor*) ()
#297 0x00000000006ec66c in StatementSemanticVisitor::visit(ExpStatement*) ()
#298 0x00000000006d38d1 in ExpStatement::accept(Visitor*) ()
#299 0x00000000006ecb04 in StatementSemanticVisitor::visit(CompoundStatement*) ()
#300 0x00000000006d4631 in CompoundStatement::accept(Visitor*) ()
#301 0x000000000070ac0a in Semantic3Visitor::visit(FuncDeclaration*) ()
#302 0x000000000060e691 in FuncDeclaration::accept(Visitor*) ()
#303 0x000000000070e98e in Semantic3Visitor::visit(AggregateDeclaration*) ()
#304 0x00000000006e66c2 in ParseTimeVisitor<ASTCodegen>::visit(StructDeclaration*) ()
#305 0x0000000000594299 in StructDeclaration::accept(Visitor*) ()
#306 0x000000000070e84a in Semantic3Visitor::visit(AttribDeclaration*) ()
#307 0x00000000006e662f in ParseTimeVisitor<ASTCodegen>::visit(ProtDeclaration*) ()
#308 0x0000000000513b61 in ProtDeclaration::accept(Visitor*) ()
#309 0x000000000070e98e in Semantic3Visitor::visit(AggregateDeclaration*) ()
#310 0x00000000006e66c2 in ParseTimeVisitor<ASTCodegen>::visit(StructDeclaration*) ()
#311 0x0000000000594299 in StructDeclaration::accept(Visitor*) ()
#312 0x00000000007092d1 in Semantic3Visitor::visit(Module*) ()
#313 0x0000000000580ba1 in Module::accept(Visitor*) ()
#314 0x0000000000677bff in dmd.mars.tryMain(ulong, const(char)**) ()
#315 0x00000000006794bb in D main ()

I'm not sure that stack trace really should be that deep. Could it be doing some redundant work for some reason?

Edit: Looking into this. I think we can improve our tooling for such situations as well.

@JackStouffer
Copy link
Member

How'd you do that collapsable section?

@CyberShadow
Copy link
Member

How'd you do that collapsable section?

HTML <details> tag, which you can also use in GFMD.

@CyberShadow
Copy link
Member

GDB Python script which walks DMD's stack and prints arguments' locations:

import gdb
import os.path


def get_loc(val):
    if (val.type.code == gdb.TYPE_CODE_PTR and
        val.type.target().name is not None and (
            val.type.target().name.endswith("Statement") or
            val.type.target().name.endswith("Expression"))):
        return get_loc(val.referenced_value())
    if val.type.name is None:
        return None
    if val.type.name.endswith("Statement"):
        return val.cast(gdb.lookup_type("dmd.statement.Statement"))["loc"]
    if val.type.name.endswith("Expression"):
        return val.cast(gdb.lookup_type("dmd.expression.Expression"))["loc"]
    return None


oldlocstr = ""
frame = gdb.newest_frame()
while frame:
    block = frame.block()
    while block:
        if not block.is_global:
            for symbol in block:
                if symbol.is_argument:
                    loc = get_loc(symbol.value(frame))
                    if loc is not None:
                        try:
                            filename = loc["filename"].string("utf-8")
                            line = int(loc["linnum"])
                            char = int(loc["charnum"])
                            locstr = "{}({},{})".format(filename, line, char)
                            if locstr != oldlocstr:
                                oldlocstr = locstr
                                if os.path.exists(filename):
                                    linestr = open(filename).readlines()[line-1]
                                    locstr += ": " + linestr.rstrip('\n')
                                print(locstr)
                        except (gdb.MemoryError):
                            pass
        block = block.superblock
    frame = frame.older()

Output:

/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(385,20):             target[idx] = source[idx];
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(385,19):             target[idx] = source[idx];
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(385,25):             target[idx] = source[idx];
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(385,13):             target[idx] = source[idx];
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(384,9):         foreach (idx; 0 .. slen)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(383,5):     {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(382,5):     if (overlaps)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/algorithm/mutation.d(372,1): {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1744,13):         copy(stuff, dest[from .. stuff_end]);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1744,9):         copy(stuff, dest[from .. stuff_end]);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1742,5):     {// replace decreases length by delta
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1737,10):     else if (stuff.length == delta)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1726,5):     if (stuff.length > delta)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(1722,1): {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3007,41):                     pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3007,25):                     pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3007,21):                     pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3004,17):                 {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3003,17):                 if (top == b)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(3000,13):             {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2992,13):             if (b_idx & 1)// b in positive
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2991,9):         {// a in positive
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2990,9):         if (a_idx & 1)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2945,5):     {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-6120(6120,22)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2582,32):             start = addInterval(i.a, i.b, start);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2582,19):             start = addInterval(i.a, i.b, start);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2582,13):             start = addInterval(i.a, i.b, start);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2580,9):         foreach (i; rhs.byInterval)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2578,5):     {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-6120(6120,22)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2328,32):                 return this.add(rhs);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2328,17):                 return this.add(rhs);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-6120(6120,22)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-2268(2268,5)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-2268(2268,1)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(2254,5):     {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d-mixin-6120(6120,1)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(6120,9):         return mixin(expr);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(6119,5):     if (__ctfe)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(6118,1): {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(6136,49):         | unicode.Me | unicode.Nd | unicode.Pc")();
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/uni.d(6135,5):     return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/ir.d(52,46):     static CharMatcher matcher = CharMatcher(wordCharacter);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/ir.d(52,45):     static CharMatcher matcher = CharMatcher(wordCharacter);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/ir.d(52,5):     static CharMatcher matcher = CharMatcher(wordCharacter);
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/ir.d(51,1): {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(379,36):                     if (atStart && wordMatcher[front])
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(379,47):                     if (atStart && wordMatcher[front])
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(379,25):                     if (atStart && wordMatcher[front])
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(379,21):                     if (atStart && wordMatcher[front])
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(375,17):                 case IR.Wordboundary:
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(334,17):                 {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(333,17):                 switch (re.ir[pc].code)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(328,13):             {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(327,13):             for (;;)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(318,9):         {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(312,9):         if (nativeFn)
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(311,5):     {
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(222,25):         immutable val = matchImpl();
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(222,34):         immutable val = matchImpl();
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(222,9):         immutable val = matchImpl();
/home/vladimir/work/extern/D/dmd/generated/linux/debug/64/../../../../../phobos/std/regex/internal/backtracking.d(220,5):     {
curl.d(2491,54):                         const m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$"));
curl.d(2491,40):                         const m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$"));
curl.d(2491,25):                         const m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$"));
curl.d(2488,21):                     {
curl.d(2487,21):                     if (header.startsWith("HTTP/"))
curl.d(2481,17):                 {
curl.d(2480,17):                 try
curl.d(2478,13):             {
curl.d(2477,23):             auto dg = (in char[] header)
curl.d(2477,13):             auto dg = (in char[] header)
curl.d(2468,9):         {

Looks like it's slow because pulling in (and actually using) std.regex is pulling in a lot of other stuff (std.uni, std.algorithm)...

This script might come in useful in the future.

@CyberShadow
Copy link
Member

CyberShadow commented Feb 4, 2018

Looks like it's slow because pulling in (and actually using) std.regex is pulling in a lot of other stuff (std.uni, std.algorithm)...

Sorry, that was wrong. std.regex isn't actually getting pulled when std.net.curl is merely imported... that's only the case when it's on DMD's command-line directly, even with -o-.

I decided to continue working and expanding on the Python script above and ended up with a sampling profiler for compilation time (i.e. it profiles which parts of your program take up most time being compiled). Here is the result for std.net.curl:

Click on the above image to get on-hover tooltips showing the contents of the source line in question, and clickable links to GitHub.

Some of the longer chains:

  • std.net.curl imports std.concurrency, so that it could do asynchronous requests.
  • std.concurrency imports std.variant to box messages passed between tasks.
  • std.variant imports std.conv, so that it could format meaningful error messages.
  • std.conv imports std.array to use Appender.
  • std.array goes back to std.conv to use emplaceRef.

Another:

  • std.net.curl imports std.format to implement StatusLine.toString().
  • std.format imports std.conv to parse format-string length specifiers.
  • std.conv goes back to std.format to use formatValue.
  • std.format imports std.range.primitives to use put.
  • std.range.primitives imports std.array to use Appender.put (indirectly, the type passed to it was an Appender).
  • std.array goes back to std.conv yet again to use emplaceRef.
  • std.conv imports std.traits to use hasElaborateAssign.

Another:

  • std.net.curl imports std.format to implement StatusLine.toString().
  • std.format imports std.uni for graphemeStride
  • std.uni imports std.conv to format some error messages
  • std.conv imports std.array to use Appender.
  • std.array imports std.traits to use isSomeChar.
  • std.traits imports std.meta to use staticIndexOf.
  • std.meta then goes back to std.traits to use isAggregateType.

@CyberShadow
Copy link
Member

@CyberShadow
Copy link
Member

std.net.curl 0.40s

Here's a patch that gets that down to 0.35... not sure if it's worthwhile:

https://github.com/dlang/phobos/compare/master...CyberShadow:std-net-curl-async-ns?w=1

@wilzbach
Copy link
Member Author

wilzbach commented Feb 4, 2018

We have a much better tool for this: https://github.com/CyberShadow/DBuildStat

I didn't know about this. I guess you have to write an article for the DBlog now ;-)
BTW I think @UplinkCoder was/is also working on sth. similar - dlang/dmd#7792

Click on the above image to get on-hover tooltips showing the contents of the source line in question, and clickable links to GitHub.

That's pretty amazing! How about integrating this into the Phobos Makefile (or maybe even DAutoTest)?

Anyhow one of the bigger costs comes from importing the huge unicode_table:

image

I already figured that one out without your fancy graph and have a PR to fix that #5945


Now back to the PR - anything else blocking this PR?
As long as it's in experimental we don't take any fixed responsibilities and can also choose to back off from this if it's not working out as expected. Also we have a bit more time to reduce the cost of imports from 0.25s to almost zero (though to be fair, it's more than fast enough for me already).

(I improved the changelog and added two examples to the std.experimental.scripting module)

@wilzbach
Copy link
Member Author

wilzbach commented Feb 4, 2018

Here's a patch that gets that down to 0.35... not sure if it's worthwhile:

Imho it is. Didn't we want to get deprecate these *Async functions anyhow?
I know that ideally we fix the compiler to make imports truly lazy, but I'm not sure how easy that is. Probably a lot more difficult that your patch.

@CyberShadow
Copy link
Member

Imho it is.

OK, PR sent: #6122

I know that ideally we fix the compiler to make imports truly lazy, but I'm not sure how easy that is. Probably a lot more difficult that your patch.

I think the main problem here was that top-level non-templated functions used types from imported modules (Tid).

@wilzbach
Copy link
Member Author

wilzbach commented Feb 9, 2018

Alrighty people, seeing that this has already spawned a lot of great work and there's still working going on (e.g. #6129 or #5945), I think we should move forward and give this a try in std.experimental.
As mentioned before only by trying/exposing this, people will work on reducing the default import cost to an even lower value.

Also there's yet another rant on the NG:

  • import ... really, we are 2018 and people are still wasting our time to have standard libraries as imports. Its even more fun when you split, only to need import the array library.

... and this time we can address it by a simple merge ;-)

@@ -0,0 +1,38 @@
`import std.experimental.scripting` as a global convenience import

$(MREF std,experimental,scripting) allows to conveniently use all Phobos modules
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either

allows/gives convenient use of

or

allows us/one to conveniently use

$(LINK2 $(ROOT)spec/module.html#renamed_imports, renamed imports) can be used
to uniquely select a specific symbol.

Importing entire Phobos costs less than half a second (varying on the system) and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify that you mean just importing and not using anything, as compile times will increase as soon as you instantiate a template from the module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reworded to:

The baseline cost for import std.experimental.scripting is less than half a second (varying from system to system) and work is in progress to reduce this overhead even further.

Less potentially confusing?

@@ -0,0 +1,83 @@
/++
Phobos is the D standard library, built on top of druntime.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems obvious.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally wanted that on the package.d to give a high level overview introduction for generated docs. It doesn't make much sense in a scripting.d file though...


Convenience file that allows to import entire Phobos in one command.
+/
module std.experimental.scripting;
Copy link
Contributor

@carun carun Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, scripting seems to be a context-keyword. I understand, naming is a hard part given that it'll mostly be used for scripting, but do you think it's possible to rename?

Let's say down the line, the import times get much sleeker and people start to use this by default in D, import std.scripting would look odd 😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Andrei suggested scripting. My personal favorite would be all.
Other opinions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also my hope is that if this gets accepted / popular, we can do import std (as initially proposed)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all is what I came up to my mind as well and looks appropriate. Other options in the order of sensibility can be: everything, completely, phobos, entirely, totally. Can't think of other synonyms of all. 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about std.experimental.allphobos? I.e., import all of Phobos. :-P

I don't think the name matters that much, since this is std.experimental. Once it becomes widely adopted, people will just change that to import std; anyway.

@andralex
Copy link
Member

andralex commented Feb 9, 2018

Let's give this a whirl! Thanks for the work.

@dlang-bot dlang-bot merged commit fcaf801 into dlang:master Feb 9, 2018
@wilzbach wilzbach deleted the global-convenience-package branch February 16, 2018 02:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants