Skip to content

Commit 5b379e0

Browse files
committed
cl: preserve named rangefunc yield closure types
1 parent 4f08740 commit 5b379e0

3 files changed

Lines changed: 95 additions & 5 deletions

File tree

cl/instr.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2119,12 +2119,34 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
21192119
}
21202120
default:
21212121
fn := p.compileValue(b, cv)
2122-
args := p.compileValues(b, args, kind)
2122+
args := p.compileDynamicCallValues(b, call, kind)
21232123
ret = p.emitDo(b, act, ds, fn, llssa.Builder.Call, args...)
21242124
}
21252125
return
21262126
}
21272127

2128+
func (p *context) compileDynamicCallValues(b llssa.Builder, call *ssa.CallCommon, hasVArg int) []llssa.Expr {
2129+
args := p.compileValues(b, call.Args, hasVArg)
2130+
params := call.Signature().Params()
2131+
n := min(len(call.Args)-hasVArg, params.Len())
2132+
for i, arg := range call.Args[:n] {
2133+
want := params.At(i).Type()
2134+
if needsNamedClosureChange(arg.Type(), want) {
2135+
args[i] = b.ChangeType(p.type_(want, llssa.InGo), args[i])
2136+
}
2137+
}
2138+
return args
2139+
}
2140+
2141+
func needsNamedClosureChange(got, want types.Type) bool {
2142+
if types.Identical(got, want) {
2143+
return false
2144+
}
2145+
_, gotIsFunc := got.Underlying().(*types.Signature)
2146+
_, wantIsFunc := want.Underlying().(*types.Signature)
2147+
return gotIsFunc && wantIsFunc && types.Identical(got.Underlying(), want.Underlying())
2148+
}
2149+
21282150
func (p *context) reflectTypeMethodCheck(call *ssa.CallCommon, method *types.Func) (check llssa.ReflectMethodCheck) {
21292151
if !isReflectType(call.Value.Type()) {
21302152
return

cl/named_closure_internal_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//go:build !llgo
2+
3+
package cl
4+
5+
import (
6+
"go/token"
7+
"go/types"
8+
"testing"
9+
10+
"github.com/xgo-dev/llvm"
11+
)
12+
13+
func TestNeedsNamedClosureChange(t *testing.T) {
14+
pkg := types.NewPackage("example.com/p", "p")
15+
params := types.NewTuple(types.NewParam(token.NoPos, pkg, "value", types.Typ[types.Int]))
16+
results := types.NewTuple(types.NewParam(token.NoPos, pkg, "", types.Typ[types.Bool]))
17+
sig := types.NewSignatureType(nil, nil, nil, params, results, false)
18+
named := types.NewNamed(types.NewTypeName(token.NoPos, pkg, "Func", nil), sig, nil)
19+
different := types.NewSignatureType(nil, nil, nil, nil, results, false)
20+
21+
tests := []struct {
22+
name string
23+
got types.Type
24+
want types.Type
25+
ok bool
26+
}{
27+
{name: "anonymous to named", got: sig, want: named, ok: true},
28+
{name: "named to anonymous", got: named, want: sig, ok: true},
29+
{name: "identical named", got: named, want: named},
30+
{name: "different signatures", got: different, want: named},
31+
{name: "non functions", got: types.Typ[types.Int], want: types.Typ[types.Int64]},
32+
}
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
if got := needsNamedClosureChange(tt.got, tt.want); got != tt.ok {
36+
t.Fatalf("needsNamedClosureChange(%v, %v) = %v, want %v", tt.got, tt.want, got, tt.ok)
37+
}
38+
})
39+
}
40+
}
41+
42+
func TestNamedClosureValuesKeepTheirDeclaredType(t *testing.T) {
43+
const source = `package main
44+
45+
type Func func(int) bool
46+
47+
func call(fn Func) bool { return fn(1) }
48+
49+
func direct() bool {
50+
want := 1
51+
return call(func(got int) bool { return got == want })
52+
}
53+
54+
func iterator() func(Func) {
55+
return func(yield Func) { _ = yield(1) }
56+
}
57+
58+
func ranged() int {
59+
n := 0
60+
for range iterator() { n++ }
61+
return n
62+
}
63+
64+
func main() {
65+
if !direct() || ranged() != 1 { panic("named closure conversion failed") }
66+
}
67+
`
68+
_, module := mustCompileLLPkgFromSrc(t, source)
69+
if err := llvm.VerifyModule(module, llvm.ReturnStatusAction); err != nil {
70+
t.Fatalf("named closure module is invalid: %v\n%s", err, module.String())
71+
}
72+
}

test/goroot/xfail.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,10 +2444,6 @@ xfails:
24442444
directive: run
24452445
case: nilptr2.go
24462446
reason: address-of a dereferenced nil pointer does not panic and the run hangs on darwin/arm64
2447-
- platform: darwin/arm64
2448-
directive: run
2449-
case: range4.go
2450-
reason: range-over-function build exits successfully without producing a binary on darwin/arm64
24512447
- platform: darwin/arm64
24522448
directive: run
24532449
case: fixedbugs/issue31546.go

0 commit comments

Comments
 (0)