Open
Description
Go version
go version go1.24.4 darwin/arm64
Output of go env
in your module/workspace:
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='*************************'
GOCACHEPROG=''
GODEBUG=''
GOENV='*************************'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/34/__4d1gf55nx2m34qjyjc3f1m0000gn/T/go-build4095632605=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='*************************'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='*************************'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='*************************'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.4'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
Link: https://go.dev/play/p/AzuM0eY_vGH
An internal compiler error is emitted when using a pointer to a recursive type alias.
Note: the non-pointer version compiles successfully. Likewise, the pointer version works if moved into the alias definition (type AB = *A[B]
).
To reproduce:
go run main.go
package main
type _ interface {
Value() AB
}
type A[T any] struct{}
type B struct {
Value *AB
}
type AB = A[B]
func main() {
}
What did you see happen?
% go run main.go
# command-line-arguments
<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
%
What did you expect to see?
% go run main.go
%
Sorry if this is a duplicate issue! There were some related entires, though I get this on the latest version, 1.24.4
.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Milestone
Relationships
Development
No branches or pull requests
Activity
gabyhelp commentedon Jun 14, 2025
Related Issues
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
thepudds commentedon Jun 14, 2025
Possibly related to #72887 (or maybe not).
It seems to reproduce with tip on the playground: https://go.dev/play/p/AzuM0eY_vGH?v=gotip
@adamlouis, what do you get if you add
-gcflags=-h
flag togo run
orgo build
, and does it seem at least superficially similar to the call stacks in #72887? (The-h
causes the compiler to panic on the first compile error encountered, which lets you see the call stack).CC @griesemer, @findleyr
thepudds commentedon Jun 14, 2025
@adamlouis, also, I should add -- thanks for filing this, and especially thanks for including a nice minimal reproducer, which should be enough for others to investigate further (and no worries of course if you don't have time to try
-gcflags=-h
or look at this more yourself).JunyangShao commentedon Jun 16, 2025
@golang/compiler @mrkfrmn
mrkfrmn commentedon Jun 18, 2025
I've done a bit of poking — somewhat interesting to me is that the below panics:
While the below does not:
I added in some one-off logging to get more detail on where the panic is coming from and it looks like the issue is with
B
— specifically its fieldf
becomes invalid:And a brief cycle trace:
While in the second example it resolves fine:
And a cycle trace:
I took a deeper trace of the first case and got:
The interesting part to me is that
*AB
resolves toBasic
(due to the invalid type) rather thanPointer
. Seems a bit odd.mrkfrmn commentedon Jun 18, 2025
I suspect the below (from
typInternal
) is related:Specifically the
!isValid(typ.base) { ... }
introduced by CL 356449. FWIW, reverting that CL locally does seem to resolve this issue (I have not ran expansive tests). Though, I imagine we would be reintroducing #49005 — will think about how we can avoid opening that again.Also cc @griesemer who worked on the related issue.
findleyr commentedon Jun 18, 2025
@mrkfrmn I suppose the question then becomes: if
!isValid(typ.base)
, why were there no type checking errors?mrkfrmn commentedon Jun 18, 2025
@findleyr Oftentimes, when we assign a type to
Invalid
, we emit an error then and there and continue type checking. However, that does not always happen. In particular, aliases useInvalid
to mean that the right hand side of the alias is not yet fully set up — it's a sort of stub.I think that
!isValid(typ.base)
is assuming an error reported elsewhere and tries to just eliminate a follow-along error. The alias use case violates this assumption and results in no error at all while leaving behind anInvalid
struct field (that will never be set).I think this is also why altering declaration order "fixes" the above — it's a coincidence that the alias is fully set up when we type the struct field.
2 considerations come to mind:
Invalid
is the optimal way to mark an unfinished alias. I think @adonovan may have thoughts.Invalid
. It's unfortunate that this gets down to noding before panicking.griesemer commentedon Jun 18, 2025
I agree that using
Invalid
to mark a type as incomplete is problematic because it implies that an error was reported somewhere when it wasn't. Now that we have an actualAlias
type we can probably use a flag. I actually have a TODO (in my personal log) about this: "Use bool to mark Alias as initialized". See also the TODO inChecker.typeDecl
(decl.go).I also agree that upon successful completion of type checking, there shouldn't be any
Invalid
types anywhere. That's a harder invariant to establish.mrkfrmn commentedon Jun 23, 2025
It seems that doing this via the
Alias
type is appropriate. I checkedgo/types
and the issue replicates there as expected. Interestingly, withgotypesalias=0
, the check added in CL 379916 is reported:It's likely fine to leave the infrastructure without explicit alias types alone. Also considered removing
gotypesalias
prior to this work, but we must support that until at least 1.27 (thanks @findleyr for the pointer).I'll look at handling this via flag where
gotypesalias=1
.gopherbot commentedon Jun 25, 2025
Change https://go.dev/cl/683796 mentions this issue:
go/types, types2: use nil to represent incomplete explicit aliases
mrkfrmn commentedon Jun 27, 2025
After some slight adjustment to handle the case of partial type checking, things seem to be in order. I'm going to consider this all but submitted.