From 41fb587948487d8fdd1ef5af4d7f082f172292cf Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 26 Jul 2013 13:55:14 -0700 Subject: [PATCH] go.tools/go/types: implement Info.Selections Should make it easier for clients to deal with selector expressions. Removed Field type: The respective information is now reported in Info.Selections. R=adonovan CC=golang-dev https://golang.org/cl/11942043 --- go/types/api.go | 6 ++++ go/types/builtins.go | 4 +-- go/types/call.go | 10 +++--- go/types/check.go | 9 ++++++ go/types/lookup.go | 20 ------------ go/types/methodset.go | 74 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 95 insertions(+), 28 deletions(-) diff --git a/go/types/api.go b/go/types/api.go index 4c8a3a43..7b4b8c99 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -115,6 +115,12 @@ type Info struct { // *ast.Field anonymous struct field or parameter *Var // Implicits map[ast.Node]Object + + // If Selections != nil, it records the selector expression and corresponding + // selection, i.e., package object (qualified identifier), struct field (field + // selector), or method (method expression or value) for each selector expression + // that is type-checked. + Selections map[*ast.SelectorExpr]*Selection } // Check type-checks a package and returns the resulting package object, the first diff --git a/go/types/builtins.go b/go/types/builtins.go index 787b33dc..560b8b26 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -356,8 +356,8 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) { goto Error } - // TODO(gri) don't create a lookupResult if no Objects map exists - check.recordObject(arg.Sel, lookupResult(base, obj, index, indirect)) + // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)? + check.recordSelection(arg, FieldVal, base, obj, index, false) offs := check.conf.offsetof(base, index) x.mode = constant diff --git a/go/types/call.go b/go/types/call.go index d714de0b..8d3a8de6 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -182,7 +182,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) goto Error } - check.recordObject(e.Sel, exp) + check.recordSelection(e, PackageObj, nil, exp, nil, false) // Simplified version of the code for *ast.Idents: // - imported packages use types.Scope and types.Objects // - imported objects are always fully initialized @@ -225,9 +225,6 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { goto Error } - // TODO(gri) don't create a lookupResult if no Objects map exists - check.recordObject(e.Sel, lookupResult(x.typ, obj, index, indirect)) - if x.mode == typexpr { // method expression m, _ := obj.(*Func) @@ -242,6 +239,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { goto Error } + check.recordSelection(e, MethodExpr, x.typ, m, index, indirect) + // the receiver type becomes the type of the first function // argument of the method expression's function type var params []*Var @@ -260,6 +259,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { // regular selector switch obj := obj.(type) { case *Var: + check.recordSelection(e, FieldVal, x.typ, obj, index, indirect) x.mode = variable x.typ = obj.typ @@ -276,6 +276,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { goto Error } + check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) + if debug { // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. typ := x.typ diff --git a/go/types/check.go b/go/types/check.go index 6001d308..e363a939 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -96,6 +96,15 @@ func (check *checker) recordImplicit(node ast.Node, obj Object) { } } +func (check *checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { + assert(obj != nil && (recv == nil || len(index) > 0)) + check.recordObject(x.Sel, obj) + // TODO(gri) Should we also call recordTypeAndValue? + if m := check.Selections; m != nil { + m[x] = &Selection{kind, obj, selectorPath{recv, index, indirect}} + } +} + // A bailout panic is raised to indicate early termination. type bailout struct{} diff --git a/go/types/lookup.go b/go/types/lookup.go index 696d7590..a293895f 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -11,26 +11,6 @@ package types // types always have only one representation (even when imported // indirectly via different packages.) -// TODO(gri) Move Field to objects.go? - -// A Field represents a struct field x.f and corresponding path. -type Field struct { - *Var - selectorPath -} - -func lookupResult(typ Type, obj Object, index []int, indirect bool) Object { - switch obj := obj.(type) { - case nil: - return nil - case *Var: - return &Field{obj, selectorPath{typ, index, indirect}} - case *Func: - return &Method{obj, selectorPath{typ, index, indirect}} - } - panic("unreachable") -} - // LookupFieldOrMethod looks up a field or method with given package and name // in typ and returns the corresponding *Var or *Func, an index sequence, // and a bool indicating if there were any pointer indirections on the path diff --git a/go/types/methodset.go b/go/types/methodset.go index 005d3a60..288fc7e3 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -13,8 +13,78 @@ import ( "sync" ) -// TODO(gri) Move Method to objects.go? -// TODO(gri) Method.Type() returns the wrong receiver type. +// SelectorKind describes the kind of a selector expression recv.obj. +type SelectionKind int + +const ( + FieldVal SelectionKind = iota // recv.obj is a struct field selector + MethodVal // recv.obj is a method selector + MethodExpr // recv.obj is a method expression + PackageObj // recv.obj is a qualified identifier +) + +// A Selection describes a selector expression recv.obj. +type Selection struct { + kind SelectionKind + obj Object + selectorPath // not valid for PackageObj +} + +func (s *Selection) Kind() SelectionKind { return s.kind } +func (s *Selection) Obj() Object { return s.obj } + +// Type returns the type of the selector recv.obj which may be different +// from the type of the selected object obj. +// +// Given the example declarations: +// +// type T struct{ x int; E } +// type E struct{} +// func (e E) m() {} +// var p *T +// +// the following relationships exist: +// +// Kind Selector Recv type Selection type +// +// FieldVal p.x T int (type of x) +// MethodVal p.m *T func (e *T) m() +// MethodExpr T.m T func m(_ T) +// PackageObj math.Pi nil untyped numeric constant (type of Pi) +// +func (s *Selection) Type() Type { + switch s.kind { + case MethodVal: + // The type of recv.obj is a method with its receiver type + // changed to s.recv. + // TODO(gri) Similar code is already in Method.Type below. + // Method may go away in favor of Selection. + sig := *s.obj.(*Func).typ.(*Signature) + recv := *sig.recv + recv.typ = s.recv + sig.recv = &recv + return &sig + + case MethodExpr: + // The type of recv.obj is a function (without receiver) + // and an additional first argument of type recv. + // TODO(gri) Similar code is already in call.go - factor! + sig := *s.obj.(*Func).typ.(*Signature) + arg0 := *sig.recv + arg0.typ = s.recv + var params []*Var + if sig.params != nil { + params = sig.params.vars + } + sig.params = NewTuple(append([]*Var{&arg0}, params...)...) + return &sig + } + + // In all other cases, the type of recv.obj is the type of obj. + return s.obj.Type() +} + +// TODO(gri) Replace Method in favor of Selection (make Method an interface?). // A Method represents a concrete or abstract (interface) method x.m // and corresponding path. The method belongs to the method set of x.