Go version
go version go1.24.1 linux/amd64
Output of go env
in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/andrew/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/andrew/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2188541391=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/andrew/git/src/github.com/meln5674/goj-compiler-bug/go.mod'
GOMODCACHE='/home/andrew/git/pkg/mod'
GONOPROXY='github.com/meln5675/*'
GONOSUMDB='github.com/meln5675/*'
GOOS='linux'
GOPATH='/home/andrew/git/'
GOPRIVATE='github.com/meln5675/*'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go-1.24.1'
GOSUMDB='sum.golang.org'
GOTELEMETRY='off'
GOTELEMETRYDIR='/home/andrew/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go-1.24.1/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.1'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
https://go.dev/play/p/oCCeMP7r2Jk
This is not the original source this defect was discovered in, which is not publicly releasable. The above was derived by removing source elements until the error no longer occurred and then anonymizing names.
A few previous versions of go that I happened to have installed were spot checked across 1.23 and 1.22 and they all produced internal errors, so this is likely not a regression in 1.24.1 .
What did you see happen?
Received the following error message:
# <REDACTED: root package of module>
<unknown line number>: internal compiler error: unexpected types2.Invalid
Please file a bug report including a short program that triggers the error.
https://go.dev/issue/new
This occurs identically locally and in the linked go playground.
What did you expect to see?
The command to either succeed or produce a usable error message.
Comment From: randall77
The command to either succeed or produce a usable error message.
Any idea which one? I cannot fathom whether this is legal Go code or not.
Comment From: meln5674
I am fairly certain this is legal, as the original code base compiled and executed correctly until a single new field was added, which, unsurprisingly, ended up as the "B" field in the anonymized example. go fmt
works without issue, so it is at very least syntactically legal.
Comment From: gabyhelp
Related Issues
- internal compiler error: bad type for ... argument: [2]string #45913 (closed)
- cmd/compile: internal compiler error when processing invalid recursive type #49276 (closed)
- cmd/compile: random compile error running tests #35760 (closed)
- cmd/compile: internal compiler error: unexpected type uint for 1 #32959 (closed)
- cmd/compile: internal compiler error: unexpected stmt: nil #50315 (closed)
- cmd/compile: internal compiler error with zero-size types #65808 (closed)
- [dev.typeparams] internal compiler error: cannot export = (22) node #47514 (closed)
- cmd/compile: internal compiler error: Type.Elem UNION #58345 (closed)
- cmd/compile: internal compiler error: bad Sym: have M, want foo #49017 (closed)
- cmd/compile: internal compiler error: unexpected dot in identifier: .anon0 #25101 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: meln5674
I have managed to simplify it further, along with a clearer naming scheme that disambiguates between types, fields, and type params: https://go.dev/play/p/bKryWGDZIep .
This makes it more obvious that there is a cycle in the type graph between B and D, which I originally believed to be the issue, but curiously, removing A fixes the error, as does making D a typedef for B directly or making B an alias for a pointer to D. It seems that it is this specific graph shape that triggers the bug.
Comment From: meln5674
Further progress: https://go.dev/play/p/5Pkrr97m2QT has an identical shape, but doesn't have the error. The only difference is that B is changed to a typedef of C, instead of an alias. I traced this back to what this represented in the original codebase, changed it from an alias to a typedef, and the original codebase compiles again. Something about that edge in the graph being an alias instead of a typedef must be the cause.
Comment From: ianlancetaylor
When using the alias these programs look invalid to me. They contain an invalid circular reference.
CC @griesemer @findleyr
Comment From: meln5674
Can you elaborate? B and D do not actually contain eachother in their representations, they are both empty structs by virtue of being an alias/typedef to C and E respectively, The reference is only in the type parameters. For example https://go.dev/play/p/yuQ_x9x_tA7 is valid, despite having the same circular reference in type parameters, and even having one (but not both) of the type edges be an alias.
Comment From: ianlancetaylor
A type alias means that you can substitute the name being defined with the right hand side. In the example https://go.dev/play/p/bKryWGDZIep after we substitute for TypeB
we have type TypeD TypeE[TypeC[*TypeD]]
. So TypeD
is being defined in terms of itself. That's not the case when not using an alias: without the alias, there are different types that refer to each other.
That said, there are some cases where circular type definitions are OK. I suppose I don't really know whether this is one of them.
Comment From: findleyr
This panic was added in https://go.dev/cl/338973, in response to #25838.
Panicking stack:
runtime/debug.Stack() runtime/debug/stack.go:26 +0x5e cmd/compile/internal/base.FatalfAt({0x139040?, 0xc0?}, {0xec4422, 0x19}, {0x0, 0x0, 0x0}) cmd/compile/internal/base/print.go:230 +0x1ea cmd/compile/internal/base.Fatalf(...) cmd/compile/internal/base/print.go:195 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b790, 0x15d9440}, 0xc000116500) cmd/compile/internal/noder/writer.go:533 +0x597 cmd/compile/internal/noder.(*writer).typ(0xc0003f5080, {0x102b790?, 0x15d9440?}) cmd/compile/internal/noder/writer.go:481 +0x2f cmd/compile/internal/noder.(*writer).doObj(0xc0003f5080, 0xc0003f5130, {0x10341e0, 0xc0004009c0}) cmd/compile/internal/noder/writer.go:883 +0x318 cmd/compile/internal/noder.(*pkgWriter).objIdx(0xc000139040, {0x10341e0, 0xc0004009c0}) cmd/compile/internal/noder/writer.go:815 +0x84b cmd/compile/internal/noder.(*pkgWriter).objInstIdx(0xc000139040, {0x10341e0, 0xc0004009c0}, 0x0, 0xc000116460) cmd/compile/internal/noder/writer.go:756 +0xf4 cmd/compile/internal/noder.(*writer).obj(0xc0003f4fd0, {0x10341e0?, 0xc0004009c0?}, 0xc0001350a0?) cmd/compile/internal/noder/writer.go:730 +0x33 cmd/compile/internal/noder.(*writer).namedType(0xc0003f4fd0, 0xc0004009c0, 0x0) cmd/compile/internal/noder/writer.go:631 +0x52 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b6c8, 0xc0001350a0}, 0xc000116460) cmd/compile/internal/noder/writer.go:550 +0x8cc cmd/compile/internal/noder.(*writer).typ(0xc0003f4f20, {0x102b6c8?, 0xc0001350a0?}) cmd/compile/internal/noder/writer.go:481 +0x2f cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b678, 0xc000036bb0}, 0xc000116460) cmd/compile/internal/noder/writer.go:578 +0x875 cmd/compile/internal/noder.(*pkgWriter).objInstIdx(0xc000139040, {0x10341e0, 0xc000400960}, 0xc0003fe3a8, 0xc000116460) cmd/compile/internal/noder/writer.go:754 +0x99 cmd/compile/internal/noder.(*writer).obj(0xc0003f4e70, {0x10341e0?, 0xc000400960?}, 0xc0001351f0?) cmd/compile/internal/noder/writer.go:730 +0x33 cmd/compile/internal/noder.(*writer).namedType(0xc0003f4e70, 0xc000400960, 0xc0003fe3a8) cmd/compile/internal/noder/writer.go:631 +0x52 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b6c8, 0xc0001351f0}, 0xc000116460) cmd/compile/internal/noder/writer.go:550 +0x8cc cmd/compile/internal/noder.(*writer).typ(0xc0003f4bb0, {0x102b6c8?, 0xc0001351f0?}) cmd/compile/internal/noder/writer.go:481 +0x2f cmd/compile/internal/noder.(*writer).doObj(0xc0003f4bb0, 0xc0003f4c60, {0x10341e0, 0xc000400900}) cmd/compile/internal/noder/writer.go:873 +0x4c5 cmd/compile/internal/noder.(*pkgWriter).objIdx(0xc000139040, {0x10341e0, 0xc000400900}) cmd/compile/internal/noder/writer.go:815 +0x84b cmd/compile/internal/noder.(*pkgWriter).objInstIdx(0xc000139040, {0x10341e0, 0xc000400900}, 0x0, 0xc0001163c0) cmd/compile/internal/noder/writer.go:756 +0xf4 cmd/compile/internal/noder.(*writer).obj(0xc0003f4b00, {0x10341e0?, 0xc000400900?}, 0x1?) cmd/compile/internal/noder/writer.go:730 +0x33 cmd/compile/internal/noder.(*writer).namedType(0xc0003f4b00, 0xc000400900, 0x0) cmd/compile/internal/noder/writer.go:631 +0x52 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b6f0, 0xc0003fcf00}, 0xc0001163c0) cmd/compile/internal/noder/writer.go:554 +0x3e5 cmd/compile/internal/noder.(*writer).typ(0xc0003f4a50, {0x102b6f0?, 0xc0003fcf00?}) cmd/compile/internal/noder/writer.go:481 +0x2f cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000139040, {0x102b678, 0xc000036b50}, 0xc0001163c0) cmd/compile/internal/noder/writer.go:578 +0x875 cmd/compile/internal/noder.(*writer).typ(0xc0003f46e0, {0x102b678?, 0xc000036b50?}) cmd/compile/internal/noder/writer.go:481 +0x2f cmd/compile/internal/noder.(*writer).doObj(0xc0003f46e0, 0xc0003f4790, {0x10341e0, 0xc0004008a0}) cmd/compile/internal/noder/writer.go:883 +0x318 cmd/compile/internal/noder.(*pkgWriter).objIdx(0xc000139040, {0x10341e0, 0xc0004008a0}) cmd/compile/internal/noder/writer.go:815 +0x84b cmd/compile/internal/noder.(*pkgWriter).objInstIdx(0xc000139040, {0x10341e0, 0xc0004008a0}, 0x0, 0x0) cmd/compile/internal/noder/writer.go:756 +0xf4 cmd/compile/internal/noder.(*writer).obj(0xc0003f44d0, {0x10341e0?, 0xc0004008a0?}, 0x0?) cmd/compile/internal/noder/writer.go:730 +0x33 cmd/compile/internal/noder.writePkgStub({0x0?, {0x0?, 0x0?}}, {0xc0000686c0, 0x1, 0x1}) cmd/compile/internal/noder/unified.go:343 +0x6fa cmd/compile/internal/noder.unified({0x0?, {0x0?, 0x0?}}, {0xc0000686c0?, 0xdd9ce0?, 0x0?}) cmd/compile/internal/noder/unified.go:195 +0xb3 cmd/compile/internal/noder.LoadPackage({0xc000020170, 0x1, 0x1}) cmd/compile/internal/noder/noder.go:77 +0x43a cmd/compile/internal/gc.Main(0xeeca88) cmd/compile/internal/gc/main.go:208 +0xcc5
Comment From: findleyr
Without digging too deeply, the problem seems to be that go/types creates a Typ[Invalid]
without reporting an actual error. My guess is there is some inaccurate "error reported elsewhere" assumption.
Comment From: griesemer
This is due to buggy cycle detection. For:
package main
type (
A B
B = C[D]
C[_ any] struct{}
D E[B]
E[_ any] struct{}
)
func main() {}
we get the following type-checking trace (excerpt, using go/types):
testdata/manual.go:4:2: -- checking type A (white, objPath = )
testdata/manual.go:4:11: . -- type B
testdata/manual.go:5:2: . . -- checking type B (white, objPath = A)
testdata/manual.go:5:13: . . . -- type C[D]
testdata/manual.go:5:13: . . . . -- instantiating type C with [D]
testdata/manual.go:5:13: . . . . . -- type C
testdata/manual.go:6:2: . . . . . . -- checking type C (white, objPath = A->B)
testdata/manual.go:6:6: . . . . . . . -- type any
testdata/manual.go:6:6: . . . . . . . => any (under = any) // *Alias
testdata/manual.go:6:11: . . . . . . . -- type struct{}
testdata/manual.go:6:11: . . . . . . . => struct{} // *Struct
testdata/manual.go:6:2: . . . . . . => type C[_ any] struct{} (black)
testdata/manual.go:5:13: . . . . . => C[_₁ any] (under = struct{}) // *Named
testdata/manual.go:5:15: . . . . . -- type D
testdata/manual.go:7:2: . . . . . . -- checking type D (white, objPath = A->B)
testdata/manual.go:7:11: . . . . . . . -- type E[B]
testdata/manual.go:7:11: . . . . . . . . -- instantiating type E with [B]
testdata/manual.go:7:11: . . . . . . . . . -- type E
testdata/manual.go:8:2: . . . . . . . . . . -- checking type E (white, objPath = A->B->D)
testdata/manual.go:8:6: . . . . . . . . . . . -- type any
testdata/manual.go:8:6: . . . . . . . . . . . => any (under = any) // *Alias
testdata/manual.go:8:11: . . . . . . . . . . . -- type struct{}
testdata/manual.go:8:11: . . . . . . . . . . . => struct{} // *Struct
testdata/manual.go:8:2: . . . . . . . . . . => type E[_ any] struct{} (black)
testdata/manual.go:7:11: . . . . . . . . . => E[_₂ any] (under = struct{}) // *Named
testdata/manual.go:7:13: . . . . . . . . . -- type B
testdata/manual.go:5:2: . . . . . . . . . . ## cycle detected: objPath = B->D->B (len = 2)
testdata/manual.go:5:2: . . . . . . . . . . ## cycle contains: 0 values, 1 type definitions
testdata/manual.go:5:2: . . . . . . . . . . => cycle is valid
testdata/manual.go:7:13: . . . . . . . . . => B (under = invalid type) // *Alias
testdata/manual.go:7:11: . . . . . . . . => invalid type
testdata/manual.go:7:11: . . . . . . . => invalid type // *Basic
testdata/manual.go:7:2: . . . . . . => type D invalid type (black)
testdata/manual.go:5:15: . . . . . => D (under = invalid type) // *Named
testdata/manual.go:5:13: . . . . => C[D]
testdata/manual.go:5:13: . . . => C[D] (under = <nil>) // *Named
testdata/manual.go:5:2: . . => type B = C[D] (black)
testdata/manual.go:4:11: . => B (under = <nil>) // *Alias
testdata/manual.go:5:13: -- Named.expandUnderlying C[D]
testdata/manual.go:5:13: => C[D] (tparams = [], under = <nil>)
testdata/manual.go:4:2: => type A struct{} (black)
A cycle is detected but deemed "Valid", however, the type for B
is invalid and (likely) never set later.
Possibly related issues: Status: #68162, #68025, #60130.
Not sure there's an easy fix that doesn't simply push the problem around. We need to revamp/rewrite cycle detection.
Comment From: thepudds
So apparently there was some sort of blog post on go.dev/blog a couple of days ago?
In one of the external discussions about the recent blog entry, one commenter complained about an internal compiler error. After some prodding from the other commenters, they eventually shared the error message and repository.
Using that info, I was able to reproduce the error on tip:
$ git clone https://github.com/codr7/shi-go
$ cd shi-go
$ gotip build ./src/shi
<unknown line number>: internal compiler error: unexpected types2.Invalid
I did a programmatic minimization (after throwing the whole repository into a txtar file via the very handy rogpeppe/go-internal/cmd/txtar-c), which yielded these 7 types. This pared-down version can be run on the playground (where it reproduces the unexpected types2.Invalid
error):
package s
type e interface{ l(Values) }
type E func(*Values)
type Stack[T any] struct{}
type T interface{ p(M) }
type V struct{ T }
type Values = Stack[V]
type M struct{ Stack[E] }
Note there is a type alias. It also seems sensitive to textual order.
A higher-level minimization is at this playground link, which includes identifier names that still match the original.
If I do gotip build -gcflags=-h
with the minimized version, I get this stack trace, which seems at least superficially similar to the one from @findleyr in https://github.com/golang/go/issues/72887#issuecomment-2729948538.
goroutine 1 [running]: cmd/compile/internal/gc.handlePanic() ../../../../sdk/gotip/src/cmd/compile/internal/gc/main.go:52 +0xa5 panic({0xe390c0?, 0x1084870?}) ../../../../sdk/gotip/src/runtime/panic.go:783 +0x132 cmd/compile/internal/base.hcrash() ../../../../sdk/gotip/src/cmd/compile/internal/base/print.go:267 +0x45 cmd/compile/internal/base.FatalfAt({0x166ea0?, 0xc0?}, {0xf1513b, 0x19}, {0x0, 0x0, 0x0}) ../../../../sdk/gotip/src/cmd/compile/internal/base/print.go:235 +0x22a cmd/compile/internal/base.Fatalf(...) ../../../../sdk/gotip/src/cmd/compile/internal/base/print.go:195 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000166ea0, {0x108b4f0, 0x166b120}, 0xc000478c80) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:533 +0x2e5 cmd/compile/internal/noder.(*writer).typ(0xc00049b1f0, {0x108b4f0?, 0x166b120?}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:481 +0x2c cmd/compile/internal/noder.(*writer).param(0xc00049b1f0, 0xc00012f5e0) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:719 +0x69 cmd/compile/internal/noder.(*writer).params(...) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:711 cmd/compile/internal/noder.(*writer).signature(0xc00049b1f0, 0xc00048e480) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:702 +0x98 cmd/compile/internal/noder.(*pkgWriter).typIdx(0xc000166ea0, {0x108b400, 0xc00048e480}, 0xc000478c80) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:583 +0x777 cmd/compile/internal/noder.(*writer).typ(0xc0004a00f0, {0x108b400?, 0xc00048e480?}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:481 +0x2c cmd/compile/internal/noder.(*writer).doObj(0xc0004a00f0, 0xc0004a0140, {0x1094300, 0xc000492480}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:883 +0x378 cmd/compile/internal/noder.(*pkgWriter).objIdx(...) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:815 cmd/compile/internal/noder.(*pkgWriter).objInstIdx(0xc000166ea0, {0x1094300, 0xc000492480}, 0x0, 0x0) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:756 +0x679 cmd/compile/internal/noder.(*writer).obj(0xc0004a0000, {0x1094300?, 0xc000492480?}, 0xc00012c4d0?) ../../../../sdk/gotip/src/cmd/compile/internal/noder/writer.go:730 +0x2c cmd/compile/internal/noder.writePkgStub({0x0?, {0x0?, 0x0?}}, {0xc00012c4d0, 0x1, 0x1}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/unified.go:343 +0x58d cmd/compile/internal/noder.unified({0x0?, {0x0?, 0x0?}}, {0xc00012c4d0?, 0xe232a0?, 0xc000111928?}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/unified.go:195 +0xb6 cmd/compile/internal/noder.LoadPackage({0xc000020270, 0x1, 0x1}) ../../../../sdk/gotip/src/cmd/compile/internal/noder/noder.go:77 +0x450 cmd/compile/internal/gc.Main(0xf3e390) ../../../../sdk/gotip/src/cmd/compile/internal/gc/main.go:208 +0xcc5 main.main() ../../../../sdk/gotip/src/cmd/compile/main.go:57 +0xf9
I didn't look closely. Sorry if this is a different underlying issue. (I can open a new issue if that is the case).
Comment From: griesemer
This pared-down code ~may or may not be~ looks related. The type-checker trace shows the same problem:
testdata/manual.go:10:6 : -- checking type e (white, objPath = )
testdata/manual.go:10:8 : . -- type interface{l(Values)}
testdata/manual.go:10:21 : . . -- type Values
testdata/manual.go:15:6 : . . . -- checking type Values (white, objPath = e)
testdata/manual.go:15:20 : . . . . -- type Stack[V]
testdata/manual.go:15:15 : . . . . . -- instantiating type Stack with [V]
testdata/manual.go:15:15 : . . . . . . -- type Stack
testdata/manual.go:12:6 : . . . . . . . -- checking type Stack (white, objPath = e->Values)
testdata/manual.go:12:14 : . . . . . . . . -- type any
testdata/manual.go:12:14 : . . . . . . . . => any (under = any) // *Alias
testdata/manual.go:12:19 : . . . . . . . . -- type struct{}
testdata/manual.go:12:19 : . . . . . . . . => struct{} // *Struct
testdata/manual.go:12:6 : . . . . . . . => type Stack[T any] struct{} (black)
testdata/manual.go:15:15 : . . . . . . => Stack[T₁ any] (under = struct{}) // *Named
testdata/manual.go:15:21 : . . . . . . -- type V
testdata/manual.go:14:6 : . . . . . . . -- checking type V (white, objPath = e->Values)
testdata/manual.go:14:8 : . . . . . . . . -- type struct{T}
testdata/manual.go:14:16 : . . . . . . . . . -- type T
testdata/manual.go:13:6 : . . . . . . . . . . -- checking type T (white, objPath = e->Values->V)
testdata/manual.go:13:8 : . . . . . . . . . . . -- type interface{p(M)}
testdata/manual.go:13:21 : . . . . . . . . . . . . -- type M
testdata/manual.go:16:6 : . . . . . . . . . . . . . -- checking type M (white, objPath = e->Values->V->T)
testdata/manual.go:16:8 : . . . . . . . . . . . . . . -- type struct{Stack[E]}
testdata/manual.go:16:21 : . . . . . . . . . . . . . . . -- type Stack[E]
testdata/manual.go:16:16 : . . . . . . . . . . . . . . . . -- instantiating type Stack with [E]
testdata/manual.go:16:16 : . . . . . . . . . . . . . . . . . -- type Stack
testdata/manual.go:16:16 : . . . . . . . . . . . . . . . . . => Stack[T₁ any] (under = struct{}) // *Named
testdata/manual.go:16:22 : . . . . . . . . . . . . . . . . . -- type E
testdata/manual.go:11:6 : . . . . . . . . . . . . . . . . . . -- checking type E (white, objPath = e->Values->V->T->M)
testdata/manual.go:11:12 : . . . . . . . . . . . . . . . . . . . -- type func(*Values)
testdata/manual.go:11:13 : . . . . . . . . . . . . . . . . . . . . -- type *Values
testdata/manual.go:11:14 : . . . . . . . . . . . . . . . . . . . . . -- type Values
testdata/manual.go:15:6 : . . . . . . . . . . . . . . . . . . . . . . ## cycle detected: objPath = Values->V->T->M->E->Values (len = 5)
testdata/manual.go:15:6 : . . . . . . . . . . . . . . . . . . . . . . ## cycle contains: 0 values, 4 type definitions
testdata/manual.go:15:6 : . . . . . . . . . . . . . . . . . . . . . . => cycle is valid
testdata/manual.go:11:14 : . . . . . . . . . . . . . . . . . . . . . => Values (under = invalid type) // *Alias
testdata/manual.go:11:13 : . . . . . . . . . . . . . . . . . . . . => invalid type // *Basic
testdata/manual.go:11:12 : . . . . . . . . . . . . . . . . . . . => func(invalid type) // *Signature
testdata/manual.go:11:6 : . . . . . . . . . . . . . . . . . . => type E func(invalid type) (black)
testdata/manual.go:16:22 : . . . . . . . . . . . . . . . . . => E (under = func(invalid type)) // *Named
testdata/manual.go:16:16 : . . . . . . . . . . . . . . . . => Stack[E]
testdata/manual.go:16:21 : . . . . . . . . . . . . . . . => Stack[E] (under = <nil>) // *Named
testdata/manual.go:16:8 : . . . . . . . . . . . . . . => struct{Stack[E]} // *Struct
testdata/manual.go:16:6 : . . . . . . . . . . . . . => type M struct{Stack[E]} (black)
testdata/manual.go:13:21 : . . . . . . . . . . . . => M (under = struct{Stack[E]}) // *Named
testdata/manual.go:13:8 : . . . . . . . . . . . => interface{p(M)} // *Interface
testdata/manual.go:13:6 : . . . . . . . . . . => type T interface{p(M)} (black)
testdata/manual.go:14:16 : . . . . . . . . . => T (under = interface{p(M)}) // *Named
testdata/manual.go:14:8 : . . . . . . . . => struct{T} // *Struct
testdata/manual.go:14:6 : . . . . . . . => type V struct{T} (black)
testdata/manual.go:15:21 : . . . . . . => V (under = struct{T}) // *Named
testdata/manual.go:15:15 : . . . . . => Stack[V]
testdata/manual.go:15:20 : . . . . => Stack[V] (under = <nil>) // *Named
testdata/manual.go:15:6 : . . . => type Values = Stack[V] (black)
testdata/manual.go:10:21 : . . => Values (under = <nil>) // *Alias
testdata/manual.go:10:8 : . => interface{l(Values)} // *Interface
testdata/manual.go:10:6 : => type e interface{l(Values)} (black)
A cycle is detected, considered "valid", but an invalid type is recorded.
The code causing a compiler panic is:
package p
type e interface{ l(Values) }
type E func(*Values)
type Stack[T any] struct{}
type T interface{ p(M) }
type V struct{ T }
type Values = Stack[V]
type M struct{ Stack[E] }
If the type definition of e
(first type definition in this code) is moved to the end, the issue disappears.
As mentioned earlier, this is a problem with cycle detection.
Comment From: findleyr
Possibly related to https://go.dev/issue/51244?
Comment From: mrkfrmn
Taking another look at this — I was able to distill this down to here:
type a *b
type b = t[*c]
type c t[b]
type t[_ any] struct{}
And thinking on this further, it sounded a bit similar to #74181 (ordering with a pointer to a generic alias). Interestingly, the error message shifts when I checkout my local changes:
<unknown line number>: internal compiler error: panic: nil
Which I suspect is from CL 683796. Seems we could be passing an alias as a type argument while it's still being set up and that is tripping up the instantiation logic.
Comment From: mrkfrmn
I think my assessment from yesterday was correct. Looking at the stack traces, we were seeing the instance
call panic here:
targs := check.typeList(xlist)
if targs == nil {
return Typ[Invalid]
}
ityp := check.instance(x.Pos(), gtyp, targs, nil, check.context())
Looking inside typeList
, it does check if the incoming type argument is a valid type:
func (check *Checker) typeList(list []syntax.Expr) []Type {
res := make([]Type, len(list))
for i, x := range list {
t := check.varType(x)
if !isValid(t) {
res = nil
}
if res != nil {
res[i] = t
}
}
return res
}
If we were using Invalid
to represent a stubbed Alias
, then this should return nil
. We can replicate that with something simple:
...
if !isValid(t) || Unalias(t) == nil {
res = nil
}
...
But there's probably a more structured way of approaching this. It's worth noting that with the above change (and those from #74181), I no longer see a compiler panic, at least when running the manual test suite.
Perhaps we implement the above as a short term solution, but we do need to rethink cycle detection.
Comment From: mrkfrmn
Just taking a look in the background again this week. On a second read of my above comments, it's worth pointing out those only get us back to the types2.Invalid
situation — it only handles drift from my other changes.
The main issue seems to be that instantiation does not account for partially set up alias types. In the below example (~same as prior):
type a b
type b = t[*c]
type c t[b]
type t[s any] struct{
f s
}
We fail on the t[b]
part because b
has a nil
(or Invalid
) type identity at that point (used for instance hashing). I think we need b
to at least have type identity before we can do t[b]
— but that's not how our current DFS type checking works.
That's also why removing the type a b
causes no error — as a policy, we try to type check aliases last (for such issues), but that line basically forces us to do it first.
I'm also not convinced that the issue here is entirely cycle detection. It seems possible (to me at least) to lay out the types, so I don't see why this would be an invalid cycle.
This sounds more like an issue of lifecycle — it seems to be an error to instantiate using a type argument that has no identity. But I am admittedly not too versed on how type hashing works.