compiler: disambiguate function-local named types (#5336)

* compiler: make getTypeCodeName a method on compilerContext

* testdata: add regression tests for function-local named types

* compiler: disambiguate function-local named types

* Some PR feedback
This commit is contained in:
Jake Bailey
2026-06-05 02:04:44 -07:00
committed by GitHub
parent 41f666c670
commit 1b9bb143bf
10 changed files with 644 additions and 19 deletions
+3
View File
@@ -0,0 +1,3 @@
module github.com/tinygo-org/tinygo/testdata/localtypes
go 1.21
+28
View File
@@ -0,0 +1,28 @@
package lib
type Checker = func(any) bool
func GenericWithLocals[T any]() (any, any, Checker, Checker) {
type UsesT struct{ V T }
type NoT struct{ V int }
var z T
return UsesT{V: z}, NoT{V: 42},
func(x any) bool { _, ok := x.(UsesT); return ok },
func(x any) bool { _, ok := x.(NoT); return ok }
}
func SiblingClosures() (any, any, Checker, Checker) {
var av, bv any
var ac, bc Checker
func() {
type Foo struct{ V int }
av = Foo{V: 1}
ac = func(x any) bool { _, ok := x.(Foo); return ok }
}()
func() {
type Foo struct{ V int }
bv = Foo{V: 2}
bc = func(x any) bool { _, ok := x.(Foo); return ok }
}()
return av, bv, ac, bc
}
+5
View File
@@ -0,0 +1,5 @@
package lib2
import "github.com/tinygo-org/tinygo/testdata/localtypes/lib"
func IntPair() (any, any, lib.Checker, lib.Checker) { return lib.GenericWithLocals[int]() }
+40
View File
@@ -0,0 +1,40 @@
package main
import (
"github.com/tinygo-org/tinygo/testdata/localtypes/lib"
"github.com/tinygo-org/tinygo/testdata/localtypes/lib2"
)
func expect(name string, ok bool) {
if ok {
println("ok:", name)
} else {
println("BUG:", name)
}
}
func main() {
mainU, mainN, mainUC, mainNC := lib.GenericWithLocals[int]()
libU, libN, libUC, libNC := lib2.IntPair()
// Same instance compiled in two packages: each package's checker
// must accept the other package's value.
expect("main GenericWithLocals[int].UsesT accepts lib2's value", mainUC(libU))
expect("main GenericWithLocals[int].NoT accepts lib2's value", mainNC(libN))
expect("lib2 GenericWithLocals[int].UsesT accepts main's value", libUC(mainU))
expect("lib2 GenericWithLocals[int].NoT accepts main's value", libNC(mainN))
// Different instantiations: distinct types.
stringU, stringN, stringUC, stringNC := lib.GenericWithLocals[string]()
expect("GenericWithLocals[int].UsesT rejects [string].UsesT", !mainUC(stringU))
expect("GenericWithLocals[int].NoT rejects [string].NoT", !mainNC(stringN))
expect("GenericWithLocals[string].UsesT rejects [int].UsesT", !stringUC(mainU))
expect("GenericWithLocals[string].NoT rejects [int].NoT", !stringNC(mainN))
// Sibling closures inside one function (declared in lib).
scA, scB, scAC, scBC := lib.SiblingClosures()
expect("lib.SiblingClosures.Foo#1 accepts own", scAC(scA))
expect("lib.SiblingClosures.Foo#2 accepts own", scBC(scB))
expect("lib.SiblingClosures.Foo#1 rejects Foo#2", !scAC(scB))
expect("lib.SiblingClosures.Foo#2 rejects Foo#1", !scBC(scA))
}
+12
View File
@@ -0,0 +1,12 @@
ok: main GenericWithLocals[int].UsesT accepts lib2's value
ok: main GenericWithLocals[int].NoT accepts lib2's value
ok: lib2 GenericWithLocals[int].UsesT accepts main's value
ok: lib2 GenericWithLocals[int].NoT accepts main's value
ok: GenericWithLocals[int].UsesT rejects [string].UsesT
ok: GenericWithLocals[int].NoT rejects [string].NoT
ok: GenericWithLocals[string].UsesT rejects [int].UsesT
ok: GenericWithLocals[string].NoT rejects [int].NoT
ok: lib.SiblingClosures.Foo#1 accepts own
ok: lib.SiblingClosures.Foo#2 accepts own
ok: lib.SiblingClosures.Foo#1 rejects Foo#2
ok: lib.SiblingClosures.Foo#2 rejects Foo#1