Skip to content

Commit 932a4a4

Browse files
committed
go/types: add Var.Kind() VarKind method
This CL adds an enum type, VarKind, that discriminates among the various kinds of Var, and adds setter/getter methods for Var's kind field. Beware: NewVar has a weaker postcondition: the Var objects it returns are not completely initialized and require a call to Var.SetKind. This should only affect importers. No changes are needed to the export data, since the kind can always be deduced from the context when decoding. See CL 645656 for the corresponding x/tools changes. + test, relnote, API Updates #70250 Change-Id: Icde86ad22a880cde6f50bc12bf38004a5c6a1025 Reviewed-on: https://go-review.googlesource.com/c/go/+/645115 Reviewed-by: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 2c16041 commit 932a4a4

33 files changed

+320
-90
lines changed

api/go1.25.txt

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pkg go/types, const FieldVar = 6 #70250
2+
pkg go/types, const FieldVar VarKind #70250
3+
pkg go/types, const LocalVar = 2 #70250
4+
pkg go/types, const LocalVar VarKind #70250
5+
pkg go/types, const PackageVar = 1 #70250
6+
pkg go/types, const PackageVar VarKind #70250
7+
pkg go/types, const ParamVar = 4 #70250
8+
pkg go/types, const ParamVar VarKind #70250
9+
pkg go/types, const RecvVar = 3 #70250
10+
pkg go/types, const RecvVar VarKind #70250
11+
pkg go/types, const ResultVar = 5 #70250
12+
pkg go/types, const ResultVar VarKind #70250
13+
pkg go/types, method (*Var) Kind() VarKind #70250
14+
pkg go/types, method (*Var) SetKind(VarKind) #70250
15+
pkg go/types, method (VarKind) String() string #70250
16+
pkg go/types, type VarKind uint8 #70250
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
### Minor changes to the library {#minor_library_changes}
22

3+
#### go/types
34

5+
The `Var.Kind` method returns an enumeration of type `VarKind` that
6+
classifies the variable (package-level, local, receiver, parameter,
7+
result, or struct field). See issue #70250.
8+
9+
Callers of `NewVar` or `NewParam` are encouraged to call `Var.SetKind`
10+
to ensure that this attribute is set correctly in all cases.

src/cmd/compile/internal/types2/api_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"internal/goversion"
1212
"internal/testenv"
1313
"slices"
14+
"sort"
1415
"strings"
1516
"sync"
1617
"testing"
@@ -3061,3 +3062,48 @@ func TestVersionWithoutPos(t *testing.T) {
30613062
t.Errorf("check error was %q, want substring %q", got, want)
30623063
}
30633064
}
3065+
3066+
func TestVarKind(t *testing.T) {
3067+
f := mustParse(`package p
3068+
3069+
var global int
3070+
3071+
type T struct { field int }
3072+
3073+
func (recv T) f(param int) (result int) {
3074+
var local int
3075+
local2 := 0
3076+
switch local3 := any(local).(type) {
3077+
default:
3078+
_ = local3
3079+
}
3080+
return local2
3081+
}
3082+
`)
3083+
3084+
pkg := NewPackage("p", "p")
3085+
info := &Info{Defs: make(map[*syntax.Name]Object)}
3086+
check := NewChecker(&Config{}, pkg, info)
3087+
if err := check.Files([]*syntax.File{f}); err != nil {
3088+
t.Fatal(err)
3089+
}
3090+
var got []string
3091+
for _, obj := range info.Defs {
3092+
if v, ok := obj.(*Var); ok {
3093+
got = append(got, fmt.Sprintf("%s: %v", v.Name(), v.Kind()))
3094+
}
3095+
}
3096+
sort.Strings(got)
3097+
want := []string{
3098+
"field: FieldVar",
3099+
"global: PackageVar",
3100+
"local2: LocalVar",
3101+
"local: LocalVar",
3102+
"param: ParamVar",
3103+
"recv: RecvVar",
3104+
"result: ResultVar",
3105+
}
3106+
if !slices.Equal(got, want) {
3107+
t.Errorf("got:\n%s\nwant:\n%s", got, want)
3108+
}
3109+
}

src/cmd/compile/internal/types2/assignments.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ func (check *Checker) shortVarDecl(pos poser, lhs, rhs []syntax.Expr) {
566566
}
567567

568568
// declare new variable
569-
obj := NewVar(ident.Pos(), check.pkg, name, nil)
569+
obj := newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
570570
lhsVars[i] = obj
571571
if name != "_" {
572572
newVars = append(newVars, obj)
@@ -577,7 +577,7 @@ func (check *Checker) shortVarDecl(pos poser, lhs, rhs []syntax.Expr) {
577577
// create dummy variables where the lhs is invalid
578578
for i, obj := range lhsVars {
579579
if obj == nil {
580-
lhsVars[i] = NewVar(lhs[i].Pos(), check.pkg, "_", nil)
580+
lhsVars[i] = newVar(LocalVar, lhs[i].Pos(), check.pkg, "_", nil)
581581
}
582582
}
583583

src/cmd/compile/internal/types2/builtins.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1032,13 +1032,13 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x *operand, id builtinId)
10321032
func makeSig(res Type, args ...Type) *Signature {
10331033
list := make([]*Var, len(args))
10341034
for i, param := range args {
1035-
list[i] = NewVar(nopos, nil, "", Default(param))
1035+
list[i] = NewParam(nopos, nil, "", Default(param))
10361036
}
10371037
params := NewTuple(list...)
10381038
var result *Tuple
10391039
if res != nil {
10401040
assert(!isUntyped(res))
1041-
result = NewTuple(NewVar(nopos, nil, "", res))
1041+
result = NewTuple(newVar(ResultVar, nopos, nil, "", res))
10421042
}
10431043
return &Signature{params: params, results: result}
10441044
}

src/cmd/compile/internal/types2/call.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *synt
9494
}
9595
}
9696
gsig := NewSignatureType(nil, nil, nil, sig.params, sig.results, sig.variadic)
97-
params = []*Var{NewVar(x.Pos(), check.pkg, "", gsig)}
97+
params = []*Var{NewParam(x.Pos(), check.pkg, "", gsig)}
9898
// The type of the argument operand is tsig, which is the type of the LHS in an assignment
9999
// or the result type in a return statement. Create a pseudo-expression for that operand
100100
// that makes sense when reported in error messages from infer, below.
@@ -875,7 +875,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *TypeName
875875
name = "_"
876876
}
877877
}
878-
params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
878+
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
879879
x.mode = value
880880
x.typ = &Signature{
881881
tparams: sig.tparams,

src/cmd/compile/internal/types2/check.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,8 @@ func (check *Checker) recordCommaOkTypesInSyntax(x syntax.Expr, t0, t1 Type) {
568568
assert(tv.Type != nil) // should have been recorded already
569569
pos := x.Pos()
570570
tv.Type = NewTuple(
571-
NewVar(pos, check.pkg, "", t0),
572-
NewVar(pos, check.pkg, "", t1),
571+
NewParam(pos, check.pkg, "", t0),
572+
NewParam(pos, check.pkg, "", t1),
573573
)
574574
x.SetTypeInfo(tv)
575575
p, _ := x.(*syntax.ParenExpr)

src/cmd/compile/internal/types2/context_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestContextHashCollisions(t *testing.T) {
3838
{
3939
// type unaryP = func[P any](_ P)
4040
tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface)
41-
params := NewTuple(NewVar(nopos, nil, "_", tparam))
41+
params := NewTuple(NewParam(nopos, nil, "_", tparam))
4242
unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false)
4343
}
4444

src/cmd/compile/internal/types2/decl.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ func (check *Checker) declStmt(list []syntax.Decl) {
844844

845845
lhs0 := make([]*Var, len(s.NameList))
846846
for i, name := range s.NameList {
847-
lhs0[i] = NewVar(name.Pos(), pkg, name.Value, nil)
847+
lhs0[i] = newVar(LocalVar, name.Pos(), pkg, name.Value, nil)
848848
}
849849

850850
// initialize all variables

src/cmd/compile/internal/types2/interface.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
4242
typ := (*Checker)(nil).newInterface()
4343
for _, m := range methods {
4444
if sig := m.typ.(*Signature); sig.recv == nil {
45-
sig.recv = NewVar(m.pos, m.pkg, "", typ)
45+
sig.recv = newVar(RecvVar, m.pos, m.pkg, "", typ)
4646
}
4747
}
4848

@@ -158,7 +158,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
158158
recvTyp = named
159159
}
160160
}
161-
sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
161+
sig.recv = newVar(RecvVar, f.Name.Pos(), check.pkg, "", recvTyp)
162162

163163
m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
164164
check.recordDef(f.Name, m)

src/cmd/compile/internal/types2/issues_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ func TestIssue50646(t *testing.T) {
628628
func TestIssue55030(t *testing.T) {
629629
// makeSig makes the signature func(typ...)
630630
makeSig := func(typ Type) {
631-
par := NewVar(nopos, nil, "", typ)
631+
par := NewParam(nopos, nil, "", typ)
632632
params := NewTuple(par)
633633
NewSignatureType(nil, nil, nil, params, nil, true)
634634
}

src/cmd/compile/internal/types2/object.go

+59-6
Original file line numberDiff line numberDiff line change
@@ -331,28 +331,81 @@ func (obj *TypeName) IsAlias() bool {
331331
// A Variable represents a declared variable (including function parameters and results, and struct fields).
332332
type Var struct {
333333
object
334+
kind VarKind
334335
embedded bool // if set, the variable is an embedded struct field, and name is the type name
335-
isField bool // var is struct field
336336
used bool // set if the variable was used
337337
origin *Var // if non-nil, the Var from which this one was instantiated
338338
}
339339

340+
// A VarKind discriminates the various kinds of variables.
341+
type VarKind uint8
342+
343+
const (
344+
_ VarKind = iota // (not meaningful)
345+
PackageVar // a package-level variable
346+
LocalVar // a local variable
347+
RecvVar // a method receiver variable
348+
ParamVar // a function parameter variable
349+
ResultVar // a function result variable
350+
FieldVar // a struct field
351+
)
352+
353+
var varKindNames = [...]string{
354+
0: "VarKind(0)",
355+
PackageVar: "PackageVar",
356+
LocalVar: "LocalVar",
357+
RecvVar: "RecvVar",
358+
ParamVar: "ParamVar",
359+
ResultVar: "ResultVar",
360+
FieldVar: "FieldVar",
361+
}
362+
363+
func (kind VarKind) String() string {
364+
if 0 <= kind && int(kind) < len(varKindNames) {
365+
return varKindNames[kind]
366+
}
367+
return fmt.Sprintf("VarKind(%d)", kind)
368+
}
369+
370+
// Kind reports what kind of variable v is.
371+
func (v *Var) Kind() VarKind { return v.kind }
372+
373+
// SetKind sets the kind of the variable.
374+
// It should be used only immediately after [NewVar] or [NewParam].
375+
func (v *Var) SetKind(kind VarKind) { v.kind = kind }
376+
340377
// NewVar returns a new variable.
341378
// The arguments set the attributes found with all Objects.
379+
//
380+
// The caller must subsequently call [Var.SetKind]
381+
// if the desired Var is not of kind [PackageVar].
342382
func NewVar(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
343-
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
383+
return newVar(PackageVar, pos, pkg, name, typ)
344384
}
345385

346386
// NewParam returns a new variable representing a function parameter.
387+
//
388+
// The caller must subsequently call [Var.SetKind] if the desired Var
389+
// is not of kind [ParamVar]: for example, [RecvVar] or [ResultVar].
347390
func NewParam(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
348-
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, used: true} // parameters are always 'used'
391+
return newVar(ParamVar, pos, pkg, name, typ)
349392
}
350393

351394
// NewField returns a new variable representing a struct field.
352395
// For embedded fields, the name is the unqualified type name
353396
// under which the field is accessible.
354397
func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var {
355-
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true}
398+
v := newVar(FieldVar, pos, pkg, name, typ)
399+
v.embedded = embedded
400+
return v
401+
}
402+
403+
// newVar returns a new variable.
404+
// The arguments set the attributes found with all Objects.
405+
func newVar(kind VarKind, pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
406+
// Function parameters are always 'used'.
407+
used := kind == RecvVar || kind == ParamVar || kind == ResultVar
408+
return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, kind: kind, used: used}
356409
}
357410

358411
// Anonymous reports whether the variable is an embedded field.
@@ -363,7 +416,7 @@ func (obj *Var) Anonymous() bool { return obj.embedded }
363416
func (obj *Var) Embedded() bool { return obj.embedded }
364417

365418
// IsField reports whether the variable is a struct field.
366-
func (obj *Var) IsField() bool { return obj.isField }
419+
func (obj *Var) IsField() bool { return obj.kind == FieldVar }
367420

368421
// Origin returns the canonical Var for its receiver, i.e. the Var object
369422
// recorded in Info.Defs.
@@ -526,7 +579,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
526579
}
527580

528581
case *Var:
529-
if obj.isField {
582+
if obj.IsField() {
530583
buf.WriteString("field")
531584
} else {
532585
buf.WriteString("var")

src/cmd/compile/internal/types2/recording.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a []*operand) {
105105
assert(tv.Type != nil) // should have been recorded already
106106
pos := x.Pos()
107107
tv.Type = NewTuple(
108-
NewVar(pos, check.pkg, "", t0),
109-
NewVar(pos, check.pkg, "", t1),
108+
newVar(LocalVar, pos, check.pkg, "", t0),
109+
newVar(LocalVar, pos, check.pkg, "", t1),
110110
)
111111
m[x] = tv
112112
// if x is a parenthesized expression (p.X), update p.X

src/cmd/compile/internal/types2/resolver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ func (check *Checker) collectObjects() {
388388
// declare all variables
389389
values := syntax.UnpackListExpr(s.Values)
390390
for i, name := range s.NameList {
391-
obj := NewVar(name.Pos(), pkg, name.Value, nil)
391+
obj := newVar(PackageVar, name.Pos(), pkg, name.Value, nil)
392392
lhs[i] = obj
393393

394394
d := d1

src/cmd/compile/internal/types2/signature.go

+12-11
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
117117
}
118118

119119
// collect ordinary and result parameters
120-
pnames, params, variadic := check.collectParams(ftyp.ParamList, true)
121-
rnames, results, _ := check.collectParams(ftyp.ResultList, false)
120+
pnames, params, variadic := check.collectParams(ParamVar, ftyp.ParamList)
121+
rnames, results, _ := check.collectParams(ResultVar, ftyp.ResultList)
122122

123123
// declare named receiver, ordinary, and result parameters
124124
scopePos := syntax.EndPos(ftyp) // all parameter's scopes start after the signature
@@ -258,13 +258,13 @@ func (check *Checker) collectRecv(rparam *syntax.Field, scopePos syntax.Pos) (*V
258258
var recv *Var
259259
if rname := rparam.Name; rname != nil && rname.Value != "" {
260260
// named receiver
261-
recv = NewParam(rname.Pos(), check.pkg, rname.Value, recvType)
261+
recv = newVar(RecvVar, rname.Pos(), check.pkg, rname.Value, recvType)
262262
// In this case, the receiver is declared by the caller
263263
// because it must be declared after any type parameters
264264
// (otherwise it might shadow one of them).
265265
} else {
266266
// anonymous receiver
267-
recv = NewParam(rparam.Pos(), check.pkg, "", recvType)
267+
recv = newVar(RecvVar, rparam.Pos(), check.pkg, "", recvType)
268268
check.recordImplicit(rparam, recv)
269269
}
270270

@@ -322,10 +322,11 @@ func (check *Checker) recordParenthesizedRecvTypes(expr syntax.Expr, typ Type) {
322322
}
323323
}
324324

325-
// collectParams collects (but does not declare) all parameters of list and returns
326-
// the list of parameter names, corresponding parameter variables, and whether the
327-
// parameter list is variadic. Anonymous parameters are recorded with nil names.
328-
func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (names []*syntax.Name, params []*Var, variadic bool) {
325+
// collectParams collects (but does not declare) all parameter/result
326+
// variables of list and returns the list of names and corresponding
327+
// variables, and whether the (parameter) list is variadic.
328+
// Anonymous parameters are recorded with nil names.
329+
func (check *Checker) collectParams(kind VarKind, list []*syntax.Field) (names []*syntax.Name, params []*Var, variadic bool) {
329330
if list == nil {
330331
return
331332
}
@@ -341,7 +342,7 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name
341342
prev = ftype
342343
if t, _ := ftype.(*syntax.DotsType); t != nil {
343344
ftype = t.Elem
344-
if variadicOk && i == len(list)-1 {
345+
if kind == ParamVar && i == len(list)-1 {
345346
variadic = true
346347
} else {
347348
check.error(t, InvalidSyntaxTree, "invalid use of ...")
@@ -359,14 +360,14 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name
359360
check.error(field.Name, InvalidSyntaxTree, "anonymous parameter")
360361
// ok to continue
361362
}
362-
par := NewParam(field.Name.Pos(), check.pkg, name, typ)
363+
par := newVar(kind, field.Name.Pos(), check.pkg, name, typ)
363364
// named parameter is declared by caller
364365
names = append(names, field.Name)
365366
params = append(params, par)
366367
named = true
367368
} else {
368369
// anonymous parameter
369-
par := NewParam(field.Pos(), check.pkg, "", typ)
370+
par := newVar(kind, field.Pos(), check.pkg, "", typ)
370371
check.recordImplicit(field, par)
371372
names = append(names, nil)
372373
params = append(params, par)

0 commit comments

Comments
 (0)