From 5bffc3abb610c99ac8f38fea6b4bc0880b056158 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 20 Jan 2026 08:43:14 +0800 Subject: [PATCH] pkg.NewTuple --- codebuild.go | 8 +++ type_tuple.go | 127 +++++++++++++++++++++++++++++++++++++++++++++ type_tuple_test.go | 53 +++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 type_tuple.go create mode 100644 type_tuple_test.go diff --git a/codebuild.go b/codebuild.go index e7370db..495c01c 100644 --- a/codebuild.go +++ b/codebuild.go @@ -159,6 +159,7 @@ type CodeBuilder struct { loadNamed LoadNamedFunc handleErr func(err error) closureParamInsts + vFieldsMgr iotav int commentOnce bool noSkipConst bool @@ -1572,6 +1573,10 @@ func (p *CodeBuilder) fieldRef(x ast.Expr, o *types.Struct, name string, src ast embed = append(embed, fld) } } + if p.refVField(o, name, x, src) { + return true + } + if visited == nil { visited = make(map[*types.Struct]none) } else if _, ok := visited[o]; ok { @@ -1934,6 +1939,9 @@ func (p *CodeBuilder) normalField( return MemberField } } + if p.findVField(o, name, arg, src) { + return MemberField + } return MemberInvalid } diff --git a/type_tuple.go b/type_tuple.go new file mode 100644 index 0000000..ca8eb9f --- /dev/null +++ b/type_tuple.go @@ -0,0 +1,127 @@ +/* + Copyright 2026 The XGo Authors (xgo.dev) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package gogen + +import ( + "go/ast" + "go/types" + "strconv" +) + +// ---------------------------------------------------------------------------- + +/* +// vFields defines the interface for virtual fields of a struct type. +type vFields interface { // virtual fields + FindField(cb *CodeBuilder, t *types.Struct, name string, arg *Element, src ast.Node) bool + FieldRef(cb *CodeBuilder, t *types.Struct, name string, x ast.Expr, src ast.Node) bool +} +*/ + +type vFields = *tupleFields + +type vFieldsMgr struct { + vfts map[*types.Struct]vFields +} + +func (p *CodeBuilder) refVField(t *types.Struct, name string, x ast.Expr, src ast.Node) bool { + if vft, ok := p.vfts[t]; ok { + return vft.FieldRef(p, t, name, x, src) + } + return false +} + +func (p *CodeBuilder) findVField(t *types.Struct, name string, arg *Element, src ast.Node) bool { + if vft, ok := p.vfts[t]; ok { + return vft.FindField(p, t, name, arg, src) + } + return false +} + +func (p *Package) setVFields(t *types.Struct, vft vFields) { + if p.cb.vfts == nil { + p.cb.vfts = make(map[*types.Struct]vFields) + } + p.cb.vfts[t] = vft +} + +/* +func (p *Package) vFields(t *types.Struct) (vft vFields, ok bool) { + vft, ok = p.cb.vfts[t] + return +} +*/ + +// ---------------------------------------------------------------------------- + +type tupleFields struct { + fields []*types.Var +} + +func (p *tupleFields) FindField(cb *CodeBuilder, t *types.Struct, name string, arg *Element, src ast.Node) bool { + for i, fld := range p.fields { + if fld.Name() == name { + cb.stk.Ret(1, &Element{ + Val: selector(arg, tupleFieldName(i)), + Type: fld.Type(), + Src: src, + }) + if cb.rec != nil { + cb.rec.Member(src, fld) + } + return true + } + } + return false +} + +func (p *tupleFields) FieldRef(cb *CodeBuilder, t *types.Struct, name string, x ast.Expr, src ast.Node) bool { + for i, fld := range p.fields { + if fld.Name() == name { + if cb.rec != nil { + cb.rec.Member(src, fld) + } + ordName := tupleFieldName(i) + cb.stk.Ret(1, &Element{ + Val: &ast.SelectorExpr{X: x, Sel: ident(ordName)}, + Type: &refType{typ: fld.Type()}, + }) + return true + } + } + return false +} + +// NewTuple creates a tuple type with the given fields. +// The fields are named as _0, _1, ... +// If withName is true, the original fields can also be accessed through +// virtual fields mechanism. +func (p *Package) NewTuple(withName bool, fields ...*types.Var) *types.Struct { + ordinals := make([]*types.Var, len(fields)) + for i, fld := range fields { + name := tupleFieldName(i) + ordinals[i] = types.NewVar(fld.Pos(), fld.Pkg(), name, fld.Type()) + } + ret := types.NewStruct(ordinals, nil) + if withName { + p.setVFields(ret, &tupleFields{fields}) + } + return ret +} + +func tupleFieldName(i int) string { + return "_" + strconv.Itoa(i) +} + +// ---------------------------------------------------------------------------- diff --git a/type_tuple_test.go b/type_tuple_test.go new file mode 100644 index 0000000..7f41e99 --- /dev/null +++ b/type_tuple_test.go @@ -0,0 +1,53 @@ +/* + Copyright 2026 The XGo Authors (xgo.dev) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package gogen_test + +import ( + "go/token" + "go/types" + "testing" + + "github.com/goplus/gogen" +) + +func TestTupleMember(t *testing.T) { + pkg := newMainPackage() + x := types.NewField(token.NoPos, pkg.Types, "x", types.Typ[types.Int], false) + y := types.NewField(token.NoPos, pkg.Types, "y", types.Typ[types.Int], false) + typ := pkg.NewTuple(true, x, y) + a := types.NewParam(token.NoPos, pkg.Types, "a", typ) + pkg.NewFunc(nil, "foo", types.NewTuple(a), nil, false).BodyStart(pkg). + Val(ctxRef(pkg, "a")). + MemberRef("x"). + Val(ctxRef(pkg, "a")). + MemberVal("y"). + Assign(1). + EndStmt(). + Debug(func(cb *gogen.CodeBuilder) { + cb.Val(ctxRef(pkg, "a")) + cb.Member("unknown", gogen.MemberFlagRef) + cb.Member("unknown", gogen.MemberFlagVal) + cb.ResetStmt() + }). + End() + domTest(t, pkg, `package main + +func foo(a struct { + _0 int + _1 int +}) { + a._0 = a._1 +} +`) +}