Skip to content

Commit 892d764

Browse files
authored
Fixes (#148)
This PR contains various fixes: * underscore imports are now preserved * interfaces with embeddeds are now supported * `ast.Ellipsis` is now handled correctly when used in a closure struct * `ast.IndexListExpr` is now supported * generic type params are now translated correctly when they're present in an anonymous function return value * `reflect.Type` can now be serialized
2 parents 7013f86 + 4eca87e commit 892d764

File tree

7 files changed

+619
-9
lines changed

7 files changed

+619
-9
lines changed

compiler/compile.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ func (c *compiler) compilePackage(p *packages.Package, colors functionColors) er
372372
c.generateFunctypes(p, gen, colorsByFunc)
373373

374374
// Find all the required imports for this file.
375-
gen = addImports(p, gen)
375+
gen = addImports(p, f, gen)
376376

377377
outputPath := strings.TrimSuffix(p.GoFiles[i], ".go")
378378
outputPath += "_durable.go"
@@ -400,7 +400,7 @@ func containsColoredFuncLit(decl *ast.FuncDecl, colorsByFunc map[ast.Node]*types
400400
return
401401
}
402402

403-
func addImports(p *packages.Package, gen *ast.File) *ast.File {
403+
func addImports(p *packages.Package, f *ast.File, gen *ast.File) *ast.File {
404404
imports := map[string]string{}
405405

406406
ast.Inspect(gen, func(n ast.Node) bool {
@@ -438,6 +438,15 @@ func addImports(p *packages.Package, gen *ast.File) *ast.File {
438438
}
439439

440440
importspecs := make([]ast.Spec, 0, len(imports))
441+
442+
// Preserve underscore (side effect) imports.
443+
for _, imp := range f.Imports {
444+
if imp.Name != nil && imp.Name.Name == "_" {
445+
importspecs = append(importspecs, imp)
446+
}
447+
}
448+
449+
// Add imports for all packages used in the file.
441450
for name, path := range imports {
442451
importspecs = append(importspecs, &ast.ImportSpec{
443452
Name: ast.NewIdent(name),
@@ -526,6 +535,8 @@ func (scope *scope) compileFuncLit(p *packages.Package, fn *ast.FuncLit, color *
526535
Body: scope.compileFuncBody(p, fn.Type, fn.Body, nil, color),
527536
}
528537

538+
p.TypesInfo.Types[gen] = types.TypeAndValue{Type: p.TypesInfo.TypeOf(fn)}
539+
529540
if !isExpr(gen.Body) {
530541
scope.colors[gen] = color
531542
}

compiler/coroutine_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package compiler
22

33
import (
4+
"math"
5+
"reflect"
46
"slices"
57
"testing"
68

@@ -220,6 +222,11 @@ func TestCoroutineYield(t *testing.T) {
220222
coro: func() { StructClosure(3) },
221223
yields: []int{10, 100, 1000, 11, 101, 1000, 12, 102, 1000},
222224
},
225+
{
226+
name: "generic closure capturing receiver and param",
227+
coro: func() { StructGenericClosure(3) },
228+
yields: []int{10, 100, 1000, 11, 101, 1000, 12, 102, 1000},
229+
},
223230
{
224231
name: "generic function",
225232
coro: func() { IdentityGenericInt(11) },
@@ -255,6 +262,26 @@ func TestCoroutineYield(t *testing.T) {
255262
coro: func() { RangeOverInt(3) },
256263
yields: []int{0, 1, 2},
257264
},
265+
266+
{
267+
name: "reflect type",
268+
coro: func() {
269+
ReflectType(reflect.TypeFor[uint8](), reflect.TypeFor[uint16]())
270+
},
271+
yields: []int{math.MaxUint8, math.MaxUint16},
272+
},
273+
274+
{
275+
name: "ellipsis closure",
276+
coro: func() { EllipsisClosure(3) },
277+
yields: []int{-1, 0, 1, 2},
278+
},
279+
280+
{
281+
name: "interface embedded",
282+
coro: func() { InterfaceEmbedded() },
283+
yields: []int{1, 1, 1},
284+
},
258285
}
259286

260287
// This emulates the installation of function type information by the

compiler/function.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func collectFunctypes(p *packages.Package, name string, fn ast.Node, scope *func
9090
typeArg = g.typeArgOf
9191
}
9292

93-
signature := copyFunctionType(functionTypeOf(fn))
93+
signature := copyFunctionType(funcTypeWithNamedResults(p, fn))
9494
signature.TypeParams = nil
9595

9696
recv := copyFieldList(functionRecvOf(fn))
@@ -182,6 +182,11 @@ func collectFunctypes(p *packages.Package, name string, fn ast.Node, scope *func
182182
fieldName := ast.NewIdent(fmt.Sprintf("X%d", i))
183183
fieldType := freeVar.typ
184184

185+
// Convert ellipsis into slice (...X => []X).
186+
if e, ok := fieldType.(*ast.Ellipsis); ok {
187+
fieldType = &ast.ArrayType{Elt: e.Elt}
188+
}
189+
185190
// The Go compiler uses a more advanced mechanism to determine if a
186191
// free variable should be captured by pointer or by value: it looks
187192
// at whether the variable is reassigned, its address taken, and if

compiler/testdata/coroutine.go

+79
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
package testdata
44

55
import (
6+
"math"
7+
"reflect"
68
"time"
79
"unsafe"
810

@@ -586,6 +588,34 @@ func StructClosure(n int) {
586588
}
587589
}
588590

591+
type GenericBox[T integer] struct {
592+
x T
593+
}
594+
595+
func (b *GenericBox[T]) YieldAndInc() {
596+
coroutine.Yield[T, any](b.x)
597+
b.x++
598+
}
599+
600+
func (b *GenericBox[T]) Closure(y T) func(T) {
601+
return func(z T) {
602+
coroutine.Yield[T, any](b.x)
603+
coroutine.Yield[T, any](y)
604+
coroutine.Yield[T, any](z)
605+
b.x++
606+
y++
607+
z++ // mutation is lost
608+
}
609+
}
610+
611+
func StructGenericClosure(n int) {
612+
box := GenericBox[int]{10}
613+
fn := box.Closure(100)
614+
for i := 0; i < n; i++ {
615+
fn(1000)
616+
}
617+
}
618+
589619
func IdentityGeneric[T any](n T) {
590620
coroutine.Yield[T, any](n)
591621
}
@@ -662,3 +692,52 @@ func RangeOverInt(n int) {
662692
coroutine.Yield[int, any](i)
663693
}
664694
}
695+
696+
func ReflectType(types ...reflect.Type) {
697+
for _, t := range types {
698+
v := reflect.New(t).Elem()
699+
if !v.CanUint() {
700+
panic("expected uint type")
701+
}
702+
v.SetUint(math.MaxUint64)
703+
coroutine.Yield[int, any](int(v.Uint()))
704+
}
705+
}
706+
707+
func MakeEllipsisClosure(ints ...int) func() {
708+
return func() {
709+
x := ints
710+
for _, v := range x {
711+
coroutine.Yield[int, any](v)
712+
}
713+
}
714+
}
715+
716+
func EllipsisClosure(n int) {
717+
ints := make([]int, n)
718+
for i := range ints {
719+
ints[i] = i
720+
}
721+
c := MakeEllipsisClosure(ints...)
722+
coroutine.Yield[int, any](-1)
723+
c()
724+
}
725+
726+
type innerInterface interface {
727+
Value() int
728+
}
729+
730+
type innerInterfaceImpl int
731+
732+
func (i innerInterfaceImpl) Value() int { return int(i) }
733+
734+
type outerInterface interface {
735+
innerInterface
736+
}
737+
738+
func InterfaceEmbedded() {
739+
var x interface{ outerInterface } = innerInterfaceImpl(1)
740+
coroutine.Yield[int, any](x.Value())
741+
coroutine.Yield[int, any](x.Value())
742+
coroutine.Yield[int, any](x.Value())
743+
}

0 commit comments

Comments
 (0)