go で「*」をたくさんつけるとコンパイルに30時間以上かかることがある

# これは何？
先日

https://qiita.com/Nabetani/items/3ddd37cf987102640c57

という記事を書いたんだけど、そういえば go もたくさん `*` をつけられそうだなと思って試した。

# `*` の付け方

こんな感じ。

```go:go
package main

import (
  "log"
  "unsafe"
)

func main() {
  var p ********************************uintptr
  x := (uintptr)((unsafe.Pointer)(&p))
  *(*uintptr)((unsafe.Pointer)(&p)) = x
  log.Println(********************************p)
}
```

x の初期化と `*****(略)*****p` の間に GC が入ると死ぬような気がするけど気にしない。

実行するとこんな出力が得られる。

```text
2021/08/10 18:33:55 824634449616
```

# 限界に挑戦

ruby でこんなコードを書いた。

```ruby:ruby
# CC="gcc-7"
# CC="gcc-11"
CC="clang"

def test_aster(n)
  aster = "*"*n
  src=<<~"SRC"
    package main

    import (
      "log"
      "unsafe"
    )
    
    func main() {
      var p #{aster}uintptr
      x := (uintptr)((unsafe.Pointer)(&p))
      *(*uintptr)((unsafe.Pointer)(&p)) = x
      log.Println(#{aster}p)
    }
    SRC

  Dir.chdir("go") do
    File.open( "main.go", "w" ){ |f| f.puts(src) }
    puts %x(time go run main.go)
    puts("n=#{n}  err = #{$?}")
  end
  $?==0
end

n=1
loop do
  break unless test_aster(n)
  n*=2
end

a = [*(n/2)..n]
p(a.bsearch do |n|
  !test_aster(n)
end)
```

`go version go1.16.5 darwin/amd64` で試したらこんな結果になった。

```text
2021/08/08 18:28:39 824634449760
        0.48 real         0.21 user         0.20 sys

n=1  err = pid 53457 exit 0
︙
略
︙
2021/08/08 19:02:32 824634834896
     1773.83 real      1761.87 user         9.20 sys

n=8192  err = pid 53747 exit 0
2021/08/08 23:15:01 824634900432
    15149.74 real     15074.27 user        60.22 sys

n=16384  err = pid 54106 exit 0

2021/08/10 14:35:05 824635031504
   139271.45 real    139042.02 user       438.17 sys

n=32768  err = pid 56707 exit 0
# command-line-arguments
./main.go:12:14: internal compiler error: name too long: **********(略)**********uintptr

Please file a bug report including a short program that triggers the error.
https://golang.org/issue/new
     1864.84 real      1874.95 user        21.12 sys

n=65536  err = pid 79661 exit 2
```

ここで ctrl+C を押して止めた。

n=32768 の場合、139042秒 = 38.6時間を要した。これをあと数回やらないと結果が出ないと思われるので諦めた。

n=65536 だと失敗するので、32768 と 65536 の間のどこかに境界があるのだろう。

失敗する場合のメッセージは上記の通り

```
# command-line-arguments
./main.go:12:14: internal compiler error: name too long: **********(略)**********uintptr

Please file a bug report including a short program that triggers the error.
https://golang.org/issue/new
```

という感じ。本当は (略) の部分に六万個を超える `*` がある。
internal compiler error なので、実装者の予期せぬ事態が起こっている模様。

チャレンジした身としては、internal compiler error が起こることより、`*` が倍になるとコンパイル時間が 10倍ぐらいになるのがつらかった。


# 補足: `*************` の意味

見出し詐欺だけど、 `*************` の意味だと説明しにくいので、 `***` ぐらいにしておく。

コードの上では

```go:go
var p ***uintptr
x := (uintptr)((unsafe.Pointer)(&p))
*(*uintptr)((unsafe.Pointer)(&p)) = x
log.Println(***p)
```

ということになる。

普通、

1. `p` の値は xxxx。`p` は `***uintptr` なので、 xxxx を `**uintptr` の値が書いてあるアドレスとみなす。
2. アドレス xxxx には yyyy と書いてある。yyyy は `**uintptr` 型なので、 yyyy を `*uintptr` の値が書いてあるアドレスとみなす。
3. アドレス yyyy には zzzz と書いてある。zzzz は `*uintptr` 型なので、 zzzz を `uintptr` の値が書いてあるアドレスとみなす。
4. というわけで、 `***p` の値はアドレス zzzz に書いてある値になる。

という具合に間接参照演算子（`*`）は機能する。

今回は、 `unsafe.Pointer` と邪悪なキャストを使って、 `p` 自身のアドレスを `p` に書き込んでいる。

例えば `&p` を `uintptr` にキャストした値が `824634449760` の場合を考える。
この場合の `***p` は

1. `p` の値は `824634449760`。`p` は `***uintptr` なので、`824634449760` を `**uintptr` の値が書いてあるアドレスとみなす。
2. アドレス `824634449760` には `824634449760` と書いてある。この文脈では `824634449760` は `**uintptr` 型なので、 `824634449760` を `*uintptr` の値が書いてあるアドレスとみなす。
3. アドレス `824634449760` には `824634449760` と書いてある。この文脈では `824634449760` は `*uintptr` 型なので、 `824634449760` を `uintptr` の値が書いてあるアドレスとみなす。
4. というわけで、 `***p` の値はアドレス `824634449760` に書いてある値 `824634449760` になる。

という具合に解釈される。

これは `*` が三個しかない例だけど、見て分かる通り、何個になってもうまくいくはず。

とはいえ、`p` のアドレスを取っている箇所とその先にある `log.Println` の間で GC が発生すると、 `p` の値と `p` のアドレスが合わなくなって死ぬかもしれないと思うけどよくわからなない。死なないかな。どうだろう。