Skip to content

Commit dbaaa15

Browse files
authored
Merge pull request #570 from xushiwei/q
tuple type: cb.tryUnpackTuple(auto unpacking)
2 parents f75b698 + 53ab8b1 commit dbaaa15

4 files changed

Lines changed: 72 additions & 5 deletions

File tree

ast.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ func matchFuncType(
10991099
}
11001100
var t *types.Tuple
11011101
n := len(args)
1102-
if len(args) == 1 && checkTuple(&t, args[0].Type) {
1102+
if len(args) == 1 && checkTuple(&t, args[0].Type) { // TODO(xsw): tuple type
11031103
n = t.Len()
11041104
args = make([]*internal.Elem, n)
11051105
for i := 0; i < n; i++ {

type_tuple.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package gogen
1515

1616
import (
1717
"go/ast"
18+
"go/token"
1819
"go/types"
1920
"strconv"
2021
)
@@ -103,6 +104,55 @@ func (p *tupleFields) FieldRef(cb *CodeBuilder, t *types.Struct, name string, x
103104
return false
104105
}
105106

107+
func (p *CodeBuilder) checkTupleType(typ types.Type) (*tupleFields, bool) {
108+
switch typ := typ.(type) {
109+
case *types.Struct:
110+
if vft, ok := p.vfts[typ]; ok {
111+
return vft, true
112+
}
113+
case *types.Named:
114+
if u, ok := typ.Underlying().(*types.Struct); ok {
115+
if vft, ok := p.vfts[u]; ok {
116+
return vft, true
117+
}
118+
}
119+
}
120+
return nil, false
121+
}
122+
123+
func (p *CodeBuilder) tryUnpackTuple() int {
124+
e := p.stk.Get(-1)
125+
tuple := e.Type
126+
if t, ok := p.checkTupleType(tuple); ok {
127+
n := len(t.fields)
128+
p.stk.PopN(1)
129+
val := e.Val
130+
if _, ok := val.(*ast.Ident); ok {
131+
for i := 0; i < n; i++ {
132+
p.stk.Push(e)
133+
p.MemberVal(tupleFieldName(i))
134+
}
135+
return n
136+
} else {
137+
pkg := p.pkg
138+
pkgType := pkg.Types
139+
arg := types.NewParam(token.NoPos, pkgType, "v", tuple)
140+
result := make([]*types.Var, n)
141+
for i, fld := range t.fields {
142+
result[i] = types.NewParam(token.NoPos, pkgType, "", fld.Type())
143+
}
144+
p.NewClosure(types.NewTuple(arg), types.NewTuple(result...), false).BodyStart(pkg)
145+
for i := 0; i < n; i++ {
146+
p.Val(arg).MemberVal(tupleFieldName(i))
147+
}
148+
p.Return(n).End()
149+
p.stk.Push(e)
150+
p.Call(1)
151+
}
152+
}
153+
return 1
154+
}
155+
106156
// LookupField looks up a field by name in the given struct type t.
107157
// It returns the field index if found, or -1 if not found.
108158
// It checks both the original fields and the virtual fields (e.g. tuple

type_tuple_test.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,24 @@ func TestTupleMember(t *testing.T) {
2626
x := types.NewField(token.NoPos, pkg.Types, "x", types.Typ[types.Int], false)
2727
y := types.NewField(token.NoPos, pkg.Types, "y", types.Typ[types.Int], false)
2828
typ := pkg.NewTuple(true, x, y)
29+
pt := types.NewNamed(types.NewTypeName(token.NoPos, pkg.Types, "Point", typ), typ, nil)
2930
a := types.NewParam(token.NoPos, pkg.Types, "a", typ)
30-
pkg.NewFunc(nil, "foo", types.NewTuple(a), nil, false).BodyStart(pkg).
31+
b := types.NewParam(token.NoPos, pkg.Types, "b", pt)
32+
typf := types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(b), false)
33+
f := types.NewParam(token.NoPos, pkg.Types, "f", typf)
34+
pkg.NewFunc(nil, "foo", types.NewTuple(a, f), nil, false).BodyStart(pkg).
3135
Val(ctxRef(pkg, "a")).
3236
MemberRef("x").
3337
Val(ctxRef(pkg, "a")).
3438
MemberVal("y").
3539
Assign(1).
3640
EndStmt().
41+
DefineVarStart(token.NoPos, "x", "y").
42+
Val(ctxRef(pkg, "a")).
43+
EndInit(1).
44+
DefineVarStart(token.NoPos, "x2", "y2").
45+
Val(ctxRef(pkg, "f")).Call(0).
46+
EndInit(1).
3747
Debug(func(cb *gogen.CodeBuilder) {
3848
cb.Val(ctxRef(pkg, "a"))
3949
cb.Member("unknown", gogen.MemberFlagRef)
@@ -46,8 +56,12 @@ func TestTupleMember(t *testing.T) {
4656
func foo(a struct {
4757
X_0 int
4858
X_1 int
49-
}) {
59+
}, f func() (b Point)) {
5060
a.X_0 = a.X_1
61+
x, y := a.X_0, a.X_1
62+
x2, y2 := func(v Point) (int, int) {
63+
return v.X_0, v.X_1
64+
}(f())
5165
}
5266
`)
5367
}

type_var_and_const.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,10 @@ func checkTuple(t **types.Tuple, typ types.Type) (ok bool) {
329329
}
330330

331331
func (p *ValueDecl) endInit(cb *CodeBuilder, arity int) *ValueDecl {
332-
var t *types.Tuple
333-
var values []ast.Expr
334332
n := len(p.names)
333+
if arity == 1 && n > 1 {
334+
arity = cb.tryUnpackTuple()
335+
}
335336
rets := cb.stk.GetArgs(arity)
336337
defer func() {
337338
cb.stk.PopN(arity)
@@ -340,6 +341,8 @@ func (p *ValueDecl) endInit(cb *CodeBuilder, arity int) *ValueDecl {
340341
cb.commitStmt(p.at) // to support inline call, we must emitStmt at EndInit stage
341342
}
342343
}()
344+
var t *types.Tuple
345+
var values []ast.Expr
343346
if arity == 1 && checkTuple(&t, rets[0].Type) {
344347
if n != t.Len() {
345348
caller := getCaller(rets[0])

0 commit comments

Comments
 (0)