package main import ( "reflect" "unsafe" ) type checker = func(any) bool func siblingA() (any, checker) { type Foo struct{ V int } return Foo{V: 1}, func(x any) bool { _, ok := x.(Foo); return ok } } func siblingB() (any, checker) { type Foo struct{ V int } return Foo{V: 2}, func(x any) bool { _, ok := x.(Foo); return ok } } func nestedScopes() (any, any, any, checker, checker, checker) { var av, bv, cv any var ac, bc, cc checker { type Bar struct{ X int } av = Bar{X: 10} ac = func(x any) bool { _, ok := x.(Bar); return ok } } { type Bar struct{ X int } bv = Bar{X: 20} bc = func(x any) bool { _, ok := x.(Bar); return ok } } { type Bar struct{ X int } cv = Bar{X: 30} cc = func(x any) bool { _, ok := x.(Bar); return ok } } return av, bv, cv, ac, bc, cc } 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 C struct{ V int } av = C{V: 1} ac = func(x any) bool { _, ok := x.(C); return ok } }() func() { type C struct{ V int } bv = C{V: 2} bc = func(x any) bool { _, ok := x.(C); return ok } }() return av, bv, ac, bc } func closureInGenericUsesT[T any]() (any, checker) { var v any var c checker func() { type X struct{ V T } var z T v = X{V: z} c = func(x any) bool { _, ok := x.(X); return ok } }() return v, c } func closureInGenericNoT[T any]() (any, checker) { var v any var c checker func() { type X struct{ V int } v = X{V: 7} c = func(x any) bool { _, ok := x.(X); return ok } }() return v, c } func siblingClosuresInGeneric[T any]() (any, any, checker, checker) { var av, bv any var ac, bc checker func() { type C struct{ V T } var z T av = C{V: z} ac = func(x any) bool { _, ok := x.(C); return ok } }() func() { type C struct{ V T } var z T bv = C{V: z} bc = func(x any) bool { _, ok := x.(C); return ok } }() return av, bv, ac, bc } func doublyNestedInGeneric[T any]() (any, checker) { var v any var c checker func() { func() { type Y struct{ V T } var z T v = Y{V: z} c = func(x any) bool { _, ok := x.(Y); return ok } }() }() return v, c } // issue4931Item's layout depends on T, so sharing generic function // bodies across different local T declarations is observable. type issue4931Item[T any] struct { _ T Labels []int } func issue4931Run[T any](input <-chan issue4931Item[T]) int { item := <-input return len(item.Labels) } func issue4931A() int { type T struct{ _ [1]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Run(items) } func issue4931B() int { type T struct{ _ [0]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Run(items) } func issue4931Pair[A, B any](input <-chan issue4931Item[A]) int { item := <-input return len(item.Labels) } func issue4931PairA() int { type T struct{ _ [1]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Pair[T, int](items) } func issue4931PairB() int { type T struct{ _ [0]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Pair[T, int](items) } type issue4931Runner[T any] struct{} func (issue4931Runner[T]) run(input <-chan issue4931Item[T]) int { item := <-input return len(item.Labels) } func issue4931MethodA() int { type T struct{ _ [1]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Runner[T]{}.run(items) } func issue4931MethodB() int { type T struct{ _ [0]any } items := make(chan issue4931Item[T], 1) items <- issue4931Item[T]{Labels: nil} return issue4931Runner[T]{}.run(items) } func expect(name string, ok bool) { if ok { println("ok:", name) } else { println("BUG:", name) } } // issue5180Copy1 and issue5180CopyIgnoreNilMembers are the original // repro from https://github.com/tinygo-org/tinygo/issues/5180. Each // declares its own local Foo with a different field type, then uses // reflect.New to construct a value via the runtime type and asserts it // back. Without distinct local-type names the two Foos collide and the // second assertion panics. func issue5180Copy1() bool { type Foo struct{ A int } f1 := &Foo{} dst := reflect.New(reflect.TypeOf(f1).Elem()).Interface() _, ok := dst.(*Foo) return ok } func issue5180CopyIgnoreNilMembers() (ok bool) { type Foo struct{ A *int } defer func() { if r := recover(); r != nil { ok = false } }() f1 := &Foo{} dst := reflect.New(reflect.TypeOf(f1).Elem()).Interface() _, ok = dst.(*Foo) return ok } func aliasSize[F float32 | float64]() int { return 8 * int(unsafe.Sizeof(F(0))) } func aliasSizeCaller32() int { type F = float32 return aliasSize[F]() } func aliasSizeCaller64() int { type F = float64 return aliasSize[F]() } func aliasLocal[F float32 | float64]() (any, checker) { type Local struct{ Value F } return Local{}, func(x any) bool { _, ok := x.(Local) return ok } } func aliasLocalCaller32() (any, checker) { type F = float32 return aliasLocal[F]() } func aliasLocalCaller64() (any, checker) { type F = float64 return aliasLocal[F]() } type aliasBox[F float32 | float64] struct { Value F } func (b aliasBox[F]) Get() F { return b.Value } func aliasBoxCaller32() bool { type F = float32 var box any = aliasBox[F]{Value: 32} _, ok := box.(interface{ Get() float32 }) return ok } func aliasBoxCaller64() bool { type F = float64 var box any = aliasBox[F]{Value: 64} _, ok := box.(interface{ Get() float64 }) return ok } type aliasMethodResult[F float32 | float64] struct { Value F } type aliasMethodBox[F float32 | float64] struct { Value F } func (b aliasMethodBox[F]) Get() aliasMethodResult[F] { return aliasMethodResult[F]{Value: b.Value} } func aliasMethodCaller32() (any, checker) { type F = float32 return aliasMethodBox[F]{Value: 32}, func(x any) bool { _, ok := x.(interface { Get() aliasMethodResult[F] }) return ok } } func aliasMethodCaller64() (any, checker) { type F = float64 return aliasMethodBox[F]{Value: 64}, func(x any) bool { _, ok := x.(interface { Get() aliasMethodResult[F] }) return ok } } func main() { expect("issue5180 TestCopy1", issue5180Copy1()) expect("issue5180 TestCopyIgnoreNilMembers", issue5180CopyIgnoreNilMembers()) // Two siblings with same-named local types are distinct. aV, aC := siblingA() bV, bC := siblingB() expect("siblingA.Foo accepts own", aC(aV)) expect("siblingB.Foo accepts own", bC(bV)) expect("siblingA.Foo rejects siblingB.Foo", !aC(bV)) expect("siblingB.Foo rejects siblingA.Foo", !bC(aV)) // Three sibling-scope locals in one function are mutually distinct. n1V, n2V, n3V, n1C, n2C, n3C := nestedScopes() expect("nestedScopes.Bar#1 accepts own", n1C(n1V)) expect("nestedScopes.Bar#2 accepts own", n2C(n2V)) expect("nestedScopes.Bar#3 accepts own", n3C(n3V)) expect("nestedScopes.Bar#1 rejects #2", !n1C(n2V)) expect("nestedScopes.Bar#2 rejects #3", !n2C(n3V)) expect("nestedScopes.Bar#1 rejects #3", !n1C(n3V)) // Generic instantiations are distinct from each other but match // themselves across calls. iU, iN, iUC, iNC := genericWithLocals[int]() sU, sN, sUC, sNC := genericWithLocals[string]() iU2, iN2, _, _ := genericWithLocals[int]() expect("genericWithLocals[int].UsesT accepts own", iUC(iU)) expect("genericWithLocals[int].NoT accepts own", iNC(iN)) expect("genericWithLocals[string].UsesT accepts own", sUC(sU)) expect("genericWithLocals[string].NoT accepts own", sNC(sN)) expect("genericWithLocals[int].UsesT rejects [string].UsesT", !iUC(sU)) expect("genericWithLocals[int].NoT rejects [string].NoT", !iNC(sN)) expect("genericWithLocals[string].UsesT rejects [int].UsesT", !sUC(iU)) expect("genericWithLocals[string].NoT rejects [int].NoT", !sNC(iN)) expect("genericWithLocals[int].UsesT matches across calls", iUC(iU2)) expect("genericWithLocals[int].NoT matches across calls", iNC(iN2)) // Sibling closures in a non-generic function. scA, scB, scAC, scBC := siblingClosures() expect("siblingClosures.C#1 accepts own", scAC(scA)) expect("siblingClosures.C#2 accepts own", scBC(scB)) expect("siblingClosures.C#1 rejects C#2", !scAC(scB)) expect("siblingClosures.C#2 rejects C#1", !scBC(scA)) // Closure inside generic function, type uses T. cgi, cgiC := closureInGenericUsesT[int]() cgs, cgsC := closureInGenericUsesT[string]() expect("closureInGenericUsesT[int].X accepts own", cgiC(cgi)) expect("closureInGenericUsesT[string].X accepts own", cgsC(cgs)) expect("closureInGenericUsesT[int].X rejects [string].X", !cgiC(cgs)) expect("closureInGenericUsesT[string].X rejects [int].X", !cgsC(cgi)) // Closure inside generic function, type does not use T. cni, cniC := closureInGenericNoT[int]() cns, cnsC := closureInGenericNoT[string]() expect("closureInGenericNoT[int].X accepts own", cniC(cni)) expect("closureInGenericNoT[string].X accepts own", cnsC(cns)) expect("closureInGenericNoT[int].X rejects [string].X", !cniC(cns)) expect("closureInGenericNoT[string].X rejects [int].X", !cnsC(cni)) // Sibling closures inside a generic function. sgIA, sgIB, sgIAC, sgIBC := siblingClosuresInGeneric[int]() sgSA, _, sgSAC, _ := siblingClosuresInGeneric[string]() expect("siblingClosuresInGeneric[int].C#1 accepts own", sgIAC(sgIA)) expect("siblingClosuresInGeneric[int].C#2 accepts own", sgIBC(sgIB)) expect("siblingClosuresInGeneric[int].C#1 rejects C#2", !sgIAC(sgIB)) expect("siblingClosuresInGeneric[int].C#1 rejects [string].C#1", !sgIAC(sgSA)) expect("siblingClosuresInGeneric[string].C#1 rejects [int].C#1", !sgSAC(sgIA)) // Doubly-nested closure inside a generic function. dni, dniC := doublyNestedInGeneric[int]() dns, dnsC := doublyNestedInGeneric[string]() expect("doublyNestedInGeneric[int].Y accepts own", dniC(dni)) expect("doublyNestedInGeneric[string].Y accepts own", dnsC(dns)) expect("doublyNestedInGeneric[int].Y rejects [string].Y", !dniC(dns)) expect("doublyNestedInGeneric[string].Y rejects [int].Y", !dnsC(dni)) // Issue 4931: generic instances with local type args must not share bodies. println("issue4931A labels:", issue4931A()) println("issue4931B labels:", issue4931B()) println("issue4931PairA labels:", issue4931PairA()) println("issue4931PairB labels:", issue4931PairB()) println("issue4931MethodA labels:", issue4931MethodA()) println("issue4931MethodB labels:", issue4931MethodB()) expect("aliasSize[float32]==32", aliasSizeCaller32() == 32) expect("aliasSize[float64]==64", aliasSizeCaller64() == 64) aliasLocal32, aliasLocalCheck32 := aliasLocalCaller32() aliasLocal64, aliasLocalCheck64 := aliasLocalCaller64() expect("aliasLocal[float32] accepts own", aliasLocalCheck32(aliasLocal32)) expect("aliasLocal[float64] accepts own", aliasLocalCheck64(aliasLocal64)) expect("aliasLocal[float32] rejects [float64]", !aliasLocalCheck32(aliasLocal64)) expect("aliasLocal[float64] rejects [float32]", !aliasLocalCheck64(aliasLocal32)) expect("aliasBox[float32] implements Get() float32", aliasBoxCaller32()) expect("aliasBox[float64] implements Get() float64", aliasBoxCaller64()) aliasMethod32, aliasMethodCheck32 := aliasMethodCaller32() aliasMethod64, aliasMethodCheck64 := aliasMethodCaller64() expect("aliasMethod[float32] accepts own", aliasMethodCheck32(aliasMethod32)) expect("aliasMethod[float64] accepts own", aliasMethodCheck64(aliasMethod64)) expect("aliasMethod[float32] rejects [float64]", !aliasMethodCheck32(aliasMethod64)) expect("aliasMethod[float64] rejects [float32]", !aliasMethodCheck64(aliasMethod32)) }