
Go compiler internals: Adding a new statement to Go - soheilpro
https://eli.thegreenplace.net/2019/go-compiler-internals-adding-a-new-statement-to-go-part-1/
======
kstenerud
The go compiler is surprisingly easy to modify! I made a modification that
allows it to issue warnings instead of errors for unused imports, variables,
etc [1]. It was a fun little exercise :)

[1] [https://github.com/kstenerud/go](https://github.com/kstenerud/go)

~~~
saagarjha
Now if only this got merged into mailine ;)

~~~
dullgiulio
Why? The idea that warnings are errors is pretty important actually.

Now, unused imports are mostly harmless (alright, they are not because of
init), but they keep unrelated commits clean without needing ad-hoc linting or
IDE functionality.

~~~
kstenerud
It's explained in the use case: [https://github.com/kstenerud/go#use-
case](https://github.com/kstenerud/go#use-case)

Use Case

The use case for warnings is the exploratory development or debugging phases,
where you really don't care about leaving unused things lying around for the
time being (for example, temporarily commenting something out), and would
rather that the compiler just got out of your way until you've got something
ready to compile normally and commit.

Usage

    
    
        go build -gcflags=-warnunused somefile.go
        go test -gcflags=-warnunused
    

When you're done with your exploratory/debugging phase, simply build or test
without the flag:

    
    
        go build somefile.go
        go test
    

Compiling without the flags will fail on unused things as normal.

~~~
icxa
Other comments are missing out the other key point: go packages are sometimes
imported for side effects, and this pattern is common enough in the community
(although albeit slightly discouraged now). So unused imports can still impact
your code due to the init() functions

~~~
atombender
Unused imports that are imported solely for side effects should be imported
this way to avoid that problem:

    
    
      import (
        _ "github.com/some/package"
      )
    

Given that this is the _only_ way to have unused imports with mainline Go — in
other words, it's impossible to accidentally remove one once it's been
declared this way — I'm not sure I see what the argument is.

~~~
icxa
> I'm not sure I see what the argument is.

That there are other reasons why unused imports are a compilation error in go,
and it isn't as simple as turning them into warnings

------
eliben
Thanks for posting this :)

Part 2 is [http://eli.thegreenplace.net/2019/go-compiler-internals-
addi...](http://eli.thegreenplace.net/2019/go-compiler-internals-adding-a-new-
statement-to-go-part-2/)

------
dawkins
That was a fun read. Very interesting.

I have noticed that the AST nodes don't have a parent reference. I wonder how
it knows for example when encountering a "continue LABEL" that the code is
nested maybe deeply in a loop with that label. The only way I would think of
is traversing the tree up but I think there is no way of doing this. How do
they do it?

~~~
Crinus
Based on [1] (where continue is handled) and [2] (the branch statement type),
looks like the target is simply passed to the function. This is a recursive
descent parser, so passing the target down the statement handling stack is
trivial (and indeed, this is what happens for for[3]).

[1]
[https://github.com/golang/go/blob/master/src/cmd/compile/int...](https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/branches.go#L218)
("ctx" is a "targets" struct containing two entries, one for which statement
'break' targets and another for which statement 'continue' targets).

[2]
[https://github.com/golang/go/blob/master/src/cmd/compile/int...](https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/nodes.go#L360)

[3]
[https://github.com/golang/go/blob/master/src/cmd/compile/int...](https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/branches.go#L293)
(the targets{s, s} passed to innerBlock is then passed to blockBranches - s is
the for statement node itself).

As a sidenote, it is interesting how easy Go reads even though i never wrote a
single line of Go myself (though i do write a lot of C and Object Pascal and
the code patterns look similar).

~~~
dawkins
Now that you have pointed where it is, it really looks easy to understand how
it works. Thank you!

------
saagarjha
Question: does the Go compiler run meaningful optimization passes? I didn't
really see it mentioned here, and given that I hear that the compiler is super
fast I'm not sure if it does…

~~~
eliben
Check out part 2 ([http://eli.thegreenplace.net/2019/go-compiler-internals-
addi...](http://eli.thegreenplace.net/2019/go-compiler-internals-adding-a-new-
statement-to-go-part-2/)) which talks about the compiler backend - there are
many optimizations done on SSA

~~~
ernst_klim
>many optimizations

Not many, mostly simple peephole optimizations.

[https://github.com/golang/go/blob/master/src/cmd/compile/int...](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/generic.rules)

------
gldev3
What about generics?

------
person_of_color
Why didn't they use LLVM?

~~~
edflsafoiewq
LLVM is very slow.

~~~
kspp
It seems to be fast enough for SQL JIT-compiler in PostgreSQL 11+ and shader
compilation in the *nix OpenGL stack (mesa), both of which are sort-of-
realtime systems.

~~~
eatonphil
Another HN thread on projects that found LLVM to be slow.

[https://news.ycombinator.com/item?id=16956589](https://news.ycombinator.com/item?id=16956589)

~~~
kspp
That's the best way to learn — post a stupid thing on the internet and let the
internet prove you wrong. Thanks to everyone who replied!

