From 5ce4c05093970381aac4a333e6a39064d44b98fd Mon Sep 17 00:00:00 2001 From: visualfc Date: Fri, 3 Apr 2026 10:00:17 +0800 Subject: [PATCH] func SetVarKind/VarKind --- func.go | 18 ++++++++++++ package_test.go | 21 ++++++++++++++ param_go125.go | 14 ++++++++-- param_legacy.go | 13 ++++++++- stmt.go | 6 +++- type_var_and_const.go | 12 ++++++-- varkind.go | 47 +++++++++++++++++++++++++++++++ varkind_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 varkind.go create mode 100644 varkind_test.go diff --git a/func.go b/func.go index 0c5995cb..25037d4b 100644 --- a/func.go +++ b/func.go @@ -35,6 +35,24 @@ func (p *Package) NewParam(pos token.Pos, name string, typ types.Type, optional return param } +// NewResult returns a new variable representing a function result. +func (p *Package) NewResult(pos token.Pos, name string, typ types.Type) *types.Var { + param := types.NewParam(pos, p.Types, name, typ) + if HasVarKind { + p.SetVarKind(param, ResultVar) + } + return param +} + +// NewRecv returns a new variable representing a method receiver. +func (p *Package) NewRecv(pos token.Pos, name string, typ types.Type) *types.Var { + param := types.NewParam(pos, p.Types, name, typ) + if HasVarKind { + p.SetVarKind(param, RecvVar) + } + return param +} + // ---------------------------------------------------------------------------- // Func type diff --git a/package_test.go b/package_test.go index c57894c0..958e6025 100644 --- a/package_test.go +++ b/package_test.go @@ -4719,4 +4719,25 @@ func main() { `) } +func TestFuncVarKind(t *testing.T) { + pkg := newMainPackage() + x := pkg.NewParam(token.NoPos, "x", types.Typ[types.Int], false) + pkg.SetVarKind(x, gogen.ParamVar) + pkg.VarKind(x) + y := pkg.NewParam(token.NoPos, "y", types.Typ[types.Int], false) + r := pkg.NewResult(token.NoPos, "r", types.Typ[types.Int]) + typ := pkg.NewType("T").InitType(pkg, types.Typ[types.Int]) + recv := pkg.NewRecv(token.NoPos, "this", typ) + pkg.NewFunc(recv, "add", types.NewTuple(x, y), types.NewTuple(r), false). + BodyStart(pkg).Val(1).Return(1).End() + domTest(t, pkg, `package main + +type T int + +func (this T) add(x int, y int) (r int) { + return 1 +} +`) +} + // ---------------------------------------------------------------------------- diff --git a/param_go125.go b/param_go125.go index f9d56607..ab3f3ea5 100644 --- a/param_go125.go +++ b/param_go125.go @@ -20,10 +20,20 @@ type optionalVars struct{} // setParamOptional marks a parameter as optional using types.Var.SetKind (Go 1.25+). func (o *optionalVars) setParamOptional(param *types.Var) { - param.SetKind(0xff) + param.SetKind(types.VarKind(ParamOptionalVar)) } // isParamOptional checks if a parameter is marked as optional using types.Var.Kind (Go 1.25+). func (o *optionalVars) isParamOptional(param *types.Var) bool { - return param.Kind() == 0xff + return param.Kind() == types.VarKind(ParamOptionalVar) } + +func (o *optionalVars) SetVarKind(v *types.Var, kind VarKind) { + v.SetKind(types.VarKind(kind)) +} + +func (o *optionalVars) VarKind(v *types.Var) VarKind { + return VarKind(v.Kind()) +} + +const HasVarKind = true diff --git a/param_legacy.go b/param_legacy.go index 89a4b59a..b7c8b30a 100644 --- a/param_legacy.go +++ b/param_legacy.go @@ -13,7 +13,9 @@ package gogen -import "go/types" +import ( + "go/types" +) // optionalVars manages optional parameter metadata for Go < 1.25. type optionalVars struct { @@ -32,3 +34,12 @@ func (o *optionalVars) setParamOptional(param *types.Var) { func (o *optionalVars) isParamOptional(param *types.Var) bool { return o.paramsMeta[param] } + +func (o *optionalVars) SetVarKind(v *types.Var, kind VarKind) { +} + +func (o *optionalVars) VarKind(v *types.Var) VarKind { + return 0 +} + +const HasVarKind = false diff --git a/stmt.go b/stmt.go index 85e29853..7a5a813c 100644 --- a/stmt.go +++ b/stmt.go @@ -527,7 +527,11 @@ func (p *forRangeStmt) RangeAssignThen(cb *CodeBuilder, pos token.Pos) { if name == "_" { continue } - if scope.Insert(types.NewVar(token.NoPos, pkg.Types, name, typs[i])) != nil { + newVar := types.NewVar(token.NoPos, pkg.Types, name, typs[i]) + if HasVarKind { + pkg.SetVarKind(newVar, LocalVar) + } + if scope.Insert(newVar) != nil { log.Panicln("TODO: variable already defined -", name) } } diff --git a/type_var_and_const.go b/type_var_and_const.go index 15634045..b2bd37f3 100644 --- a/type_var_and_const.go +++ b/type_var_and_const.go @@ -413,7 +413,11 @@ func (p *ValueDecl) endInit(cb *CodeBuilder, arity int) *ValueDecl { values[i] = parg.Val } p.setType(i, name, retType) - if old := p.scope.Insert(types.NewVar(p.pos, pkg.Types, name, retType)); old != nil { + newVar := types.NewVar(p.pos, pkg.Types, name, retType) + if HasVarKind && pkg.Types.Scope() != p.scope { + pkg.SetVarKind(newVar, LocalVar) + } + if old := p.scope.Insert(newVar); old != nil { if p.tok != token.DEFINE { oldpos := cb.fset.Position(old.Pos()) cb.panicCodeErrorf( @@ -461,7 +465,11 @@ func (p *Package) newValueDecl( continue } if typ != nil && tok == token.VAR { - if old := scope.Insert(types.NewVar(pos, p.Types, name, typ)); old != nil { + newVar := types.NewVar(pos, p.Types, name, typ) + if HasVarKind && p.Types.Scope() != scope { + p.SetVarKind(newVar, LocalVar) + } + if old := scope.Insert(newVar); old != nil { allowRedecl := p.allowRedecl && scope == p.Types.Scope() if !(allowRedecl && types.Identical(old.Type(), typ)) { // for c2go oldpos := p.cb.fset.Position(old.Pos()) diff --git a/varkind.go b/varkind.go new file mode 100644 index 00000000..b7a3b7a4 --- /dev/null +++ b/varkind.go @@ -0,0 +1,47 @@ +// Copyright 2021 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 "fmt" + +// A VarKind discriminates the various kinds of variables. +type VarKind uint8 + +const ( + _ VarKind = iota // (not meaningful) + PackageVar // a package-level variable + LocalVar // a local variable + RecvVar // a method receiver variable + ParamVar // a function parameter variable + ResultVar // a function result variable + FieldVar // a struct field + + ParamOptionalVar VarKind = 0xff +) + +var varKindNames = map[VarKind]string{ + 0: "VarKind(0)", + PackageVar: "PackageVar", + LocalVar: "LocalVar", + RecvVar: "RecvVar", + ParamVar: "ParamVar", + ResultVar: "ResultVar", + FieldVar: "FieldVar", + ParamOptionalVar: "ParamOptional", +} + +func (kind VarKind) String() string { + if s, ok := varKindNames[kind]; ok { + return s + } + return fmt.Sprintf("VarKind(%d)", kind) +} diff --git a/varkind_test.go b/varkind_test.go new file mode 100644 index 00000000..258ddf9b --- /dev/null +++ b/varkind_test.go @@ -0,0 +1,64 @@ +// Copyright 2021 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. + +//go:build go1.25 + +package gogen_test + +import ( + "go/types" + "testing" + + "github.com/goplus/gogen" +) + +func TestVarKind(t *testing.T) { + pkg := newPackage("foo") + kinds := []gogen.VarKind{ + gogen.PackageVar, + gogen.LocalVar, + gogen.RecvVar, + gogen.ParamVar, + gogen.FieldVar, + gogen.ParamOptionalVar, + } + v := pkg.NewParam(0, "v", types.Typ[types.Int], false) + for _, kind := range kinds { + pkg.SetVarKind(v, kind) + if pkg.VarKind(v).String() != kind.String() { + t.Fatal("bad kind string") + } + if types.VarKind(pkg.VarKind(v)) != v.Kind() { + t.Fatal("bad kind") + } + } + if gogen.VarKind(gogen.FieldVar+1).String() != types.VarKind(gogen.FieldVar+1).String() { + t.Fatal("bad string") + } +} + +func TestRecvVarKind(t *testing.T) { + pkg := newPackage("foo") + typ := pkg.NewType("T").InitType(pkg, types.Typ[types.Int]) + recv := pkg.NewRecv(0, "this", typ) + if recv.Kind() != types.RecvVar { + t.Fatal("error", recv.Kind()) + } +} + +func TestResultVarKind(t *testing.T) { + pkg := newPackage("foo") + typ := pkg.NewType("T").InitType(pkg, types.Typ[types.Int]) + recv := pkg.NewResult(0, "this", typ) + if recv.Kind() != types.ResultVar { + t.Fatal("error", recv.Kind()) + } +}