go.tools/go/types: cleanups
Objects: - provide IsExported, SameName, uniqueName methods - clean up a lot of dependent code Scopes: - don't add children to Universe scope (!) - document Node, WriteTo Types: - remove Deref in favor of internal function deref ssa, ssa/interp: - introduced local deref, adjusted code - fixed some "Underlying" bugs (pun intended) R=adonovan CC=golang-dev https://golang.org/cl/11232043
This commit is contained in:
parent
b042505490
commit
f1a889124d
|
|
@ -340,7 +340,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
base := x.typ.Deref()
|
base, _ := deref(x.typ)
|
||||||
sel := arg.Sel.Name
|
sel := arg.Sel.Name
|
||||||
obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name)
|
obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name)
|
||||||
switch obj.(type) {
|
switch obj.(type) {
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
if exp == nil {
|
if exp == nil {
|
||||||
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
|
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
|
||||||
goto Error
|
goto Error
|
||||||
} else if !ast.IsExported(exp.Name()) {
|
} else if !exp.IsExported() {
|
||||||
// gcimported package scopes contain non-exported
|
// gcimported package scopes contain non-exported
|
||||||
// objects such as types used in partially exported
|
// objects such as types used in partially exported
|
||||||
// objects - do not accept them
|
// objects - do not accept them
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,6 @@ import (
|
||||||
"code.google.com/p/go.tools/go/exact"
|
"code.google.com/p/go.tools/go/exact"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(gri) Calling IsIdentity on struct or interface types created
|
|
||||||
// with New or Eval in the Universe context will crash if the types
|
|
||||||
// contain non-exported fields or methods because those require a non-
|
|
||||||
// nil package. The type checker should disallow non-exported methods
|
|
||||||
// and fields in types in Universe scope.
|
|
||||||
|
|
||||||
// New is a convenience function to create a new type from a given
|
// New is a convenience function to create a new type from a given
|
||||||
// expression or type literal string evaluated in Universe scope.
|
// expression or type literal string evaluated in Universe scope.
|
||||||
// New(str) is shorthand for Eval(str, nil, nil), but only returns
|
// New(str) is shorthand for Eval(str, nil, nil), but only returns
|
||||||
|
|
|
||||||
|
|
@ -880,7 +880,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
switch utyp := typ.Deref().Underlying().(type) {
|
switch typ, _ := deref(typ); utyp := typ.Underlying().(type) {
|
||||||
case *Struct:
|
case *Struct:
|
||||||
if len(e.Elts) == 0 {
|
if len(e.Elts) == 0 {
|
||||||
break
|
break
|
||||||
|
|
@ -991,7 +991,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
check.errorf(e.Pos(), "%s is not a valid composite literal type", typ)
|
check.errorf(e.Pos(), "invalid composite literal type %s", typ)
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ func (p *gcParser) parseField() (*Field, string) {
|
||||||
anonymous := false
|
anonymous := false
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// anonymous field - typ must be T or *T and T must be a type name
|
// anonymous field - typ must be T or *T and T must be a type name
|
||||||
switch typ := typ.Deref().(type) {
|
switch typ, _ := deref(typ); typ := typ.(type) {
|
||||||
case *Basic: // basic types are named types
|
case *Basic: // basic types are named types
|
||||||
pkg = nil
|
pkg = nil
|
||||||
name = typ.name
|
name = typ.name
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "go/ast"
|
|
||||||
|
|
||||||
// TODO(gri) The named type consolidation and seen maps below must be
|
// TODO(gri) The named type consolidation and seen maps below must be
|
||||||
// indexed by unique keys for a given type. Verify that named
|
// indexed by unique keys for a given type. Verify that named
|
||||||
// types always have only one representation (even when imported
|
// types always have only one representation (even when imported
|
||||||
|
|
@ -129,7 +127,7 @@ func lookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
||||||
case *Struct:
|
case *Struct:
|
||||||
// look for a matching field and collect embedded types
|
// look for a matching field and collect embedded types
|
||||||
for i, f := range t.fields {
|
for i, f := range t.fields {
|
||||||
if f.isMatch(pkg, name) {
|
if f.SameName(pkg, name) {
|
||||||
assert(f.typ != nil)
|
assert(f.typ != nil)
|
||||||
index = concat(e.index, i)
|
index = concat(e.index, i)
|
||||||
if obj != nil || e.multiples {
|
if obj != nil || e.multiples {
|
||||||
|
|
@ -274,8 +272,6 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
||||||
|
|
||||||
// Deref dereferences typ if it is a pointer and returns its base and true.
|
// Deref dereferences typ if it is a pointer and returns its base and true.
|
||||||
// Otherwise it returns (typ, false).
|
// Otherwise it returns (typ, false).
|
||||||
// In contrast, Type.Deref also dereferenciates if type is a named type
|
|
||||||
// that is a pointer.
|
|
||||||
func deref(typ Type) (Type, bool) {
|
func deref(typ Type) (Type, bool) {
|
||||||
if p, _ := typ.(*Pointer); p != nil {
|
if p, _ := typ.(*Pointer); p != nil {
|
||||||
return p.base, true
|
return p.base, true
|
||||||
|
|
@ -297,11 +293,7 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int {
|
||||||
return -1 // blank identifiers are never found
|
return -1 // blank identifiers are never found
|
||||||
}
|
}
|
||||||
for i, f := range fields {
|
for i, f := range fields {
|
||||||
// spec:
|
if f.SameName(pkg, name) {
|
||||||
// "Two identifiers are different if they are spelled differently,
|
|
||||||
// or if they appear in different packages and are not exported.
|
|
||||||
// Otherwise, they are the same."
|
|
||||||
if f.name == name && (ast.IsExported(name) || f.pkg.path == pkg.path) {
|
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,11 +304,7 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int {
|
||||||
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
||||||
assert(name != "_")
|
assert(name != "_")
|
||||||
for i, m := range methods {
|
for i, m := range methods {
|
||||||
// spec:
|
if m.SameName(pkg, name) {
|
||||||
// "Two identifiers are different if they are spelled differently,
|
|
||||||
// or if they appear in different packages and are not exported.
|
|
||||||
// Otherwise, they are the same."
|
|
||||||
if m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) {
|
|
||||||
return i, m
|
return i, m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ package types
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -25,7 +24,7 @@ func (s *MethodSet) String() string {
|
||||||
fmt.Fprintln(&buf)
|
fmt.Fprintln(&buf)
|
||||||
}
|
}
|
||||||
for _, m := range s.list {
|
for _, m := range s.list {
|
||||||
fmt.Fprintf(&buf, "\t%s\n", key(m.pkg, m.name))
|
fmt.Fprintf(&buf, "\t%s\n", m.uniqueName())
|
||||||
}
|
}
|
||||||
fmt.Fprintln(&buf, "}")
|
fmt.Fprintln(&buf, "}")
|
||||||
return buf.String()
|
return buf.String()
|
||||||
|
|
@ -39,16 +38,16 @@ func (s *MethodSet) At(i int) *Func { return s.list[i] }
|
||||||
|
|
||||||
// Lookup returns the method with matching package and name, or nil if not found.
|
// Lookup returns the method with matching package and name, or nil if not found.
|
||||||
func (s *MethodSet) Lookup(pkg *Package, name string) *Func {
|
func (s *MethodSet) Lookup(pkg *Package, name string) *Func {
|
||||||
k := key(pkg, name)
|
key := (&object{pkg: pkg, name: name}).uniqueName()
|
||||||
|
|
||||||
i := sort.Search(len(s.list), func(i int) bool {
|
i := sort.Search(len(s.list), func(i int) bool {
|
||||||
m := s.list[i]
|
m := s.list[i]
|
||||||
return key(m.pkg, m.name) >= k
|
return m.uniqueName() >= key
|
||||||
})
|
})
|
||||||
|
|
||||||
if i < len(s.list) {
|
if i < len(s.list) {
|
||||||
m := s.list[i]
|
m := s.list[i]
|
||||||
if key(m.pkg, m.name) == k {
|
if m.uniqueName() == key {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,22 +156,11 @@ func NewMethodSet(typ Type) *MethodSet {
|
||||||
list = append(list, m)
|
list = append(list, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Sort(byKey(list))
|
sort.Sort(byUniqueName(list))
|
||||||
|
|
||||||
return &MethodSet{list}
|
return &MethodSet{list}
|
||||||
}
|
}
|
||||||
|
|
||||||
// key computes a unique (lookup and sort) key given a package and name.
|
|
||||||
func key(pkg *Package, name string) string {
|
|
||||||
if ast.IsExported(name) {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
if pkg == nil {
|
|
||||||
panic("unexported object without package information: " + name)
|
|
||||||
}
|
|
||||||
return pkg.path + "." + name
|
|
||||||
}
|
|
||||||
|
|
||||||
// A fieldSet is a set of fields and name collisions.
|
// A fieldSet is a set of fields and name collisions.
|
||||||
// A conflict indicates that multiple fields with the same package and name appeared.
|
// A conflict indicates that multiple fields with the same package and name appeared.
|
||||||
type fieldSet map[string]*Field // a nil entry indicates a name collision
|
type fieldSet map[string]*Field // a nil entry indicates a name collision
|
||||||
|
|
@ -181,15 +169,15 @@ type fieldSet map[string]*Field // a nil entry indicates a name collision
|
||||||
// If multiples is set, f appears multiple times
|
// If multiples is set, f appears multiple times
|
||||||
// and is treated as a collision at this level.
|
// and is treated as a collision at this level.
|
||||||
func (s fieldSet) add(f *Field, multiples bool) {
|
func (s fieldSet) add(f *Field, multiples bool) {
|
||||||
k := key(f.pkg, f.name)
|
key := f.uniqueName()
|
||||||
// if f is not in the set, add it
|
// if f is not in the set, add it
|
||||||
if !multiples {
|
if !multiples {
|
||||||
if _, found := s[k]; !found {
|
if _, found := s[key]; !found {
|
||||||
s[k] = f
|
s[key] = f
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s[k] = nil // collision
|
s[key] = nil // collision
|
||||||
}
|
}
|
||||||
|
|
||||||
// A methodSet is a set of methods and name collisions.
|
// A methodSet is a set of methods and name collisions.
|
||||||
|
|
@ -201,25 +189,21 @@ type methodSet map[string]*Func // a nil entry indicates a name collision
|
||||||
// and is treated as a collision at this level.
|
// and is treated as a collision at this level.
|
||||||
func (s methodSet) add(list []*Func, multiples bool) {
|
func (s methodSet) add(list []*Func, multiples bool) {
|
||||||
for _, m := range list {
|
for _, m := range list {
|
||||||
k := key(m.pkg, m.name)
|
key := m.uniqueName()
|
||||||
// if m is not in the set, add it
|
// if m is not in the set, add it
|
||||||
if !multiples {
|
if !multiples {
|
||||||
if _, found := s[k]; !found {
|
if _, found := s[key]; !found {
|
||||||
s[k] = m
|
s[key] = m
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s[k] = nil // collision
|
s[key] = nil // collision
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// byKey function lists can be sorted by key(pkg, name).
|
// byUniqueName function lists can be sorted by their unique names.
|
||||||
type byKey []*Func
|
type byUniqueName []*Func
|
||||||
|
|
||||||
func (a byKey) Len() int { return len(a) }
|
func (a byUniqueName) Len() int { return len(a) }
|
||||||
func (a byKey) Less(i, j int) bool {
|
func (a byUniqueName) Less(i, j int) bool { return a[i].uniqueName() < a[j].uniqueName() }
|
||||||
x := a[i]
|
func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
y := a[j]
|
|
||||||
return key(x.pkg, x.name) < key(y.pkg, y.name)
|
|
||||||
}
|
|
||||||
func (a byKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,21 @@ import (
|
||||||
// All objects implement the Object interface.
|
// All objects implement the Object interface.
|
||||||
//
|
//
|
||||||
type Object interface {
|
type Object interface {
|
||||||
Parent() *Scope // scope in which this object is declared
|
Parent() *Scope // scope in which this object is declared
|
||||||
Pos() token.Pos // position of object identifier in declaration
|
Pos() token.Pos // position of object identifier in declaration
|
||||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||||
Name() string // package local object name
|
Name() string // package local object name
|
||||||
Type() Type // object type
|
Type() Type // object type
|
||||||
|
IsExported() bool // reports whether the name starts with a capital letter
|
||||||
|
|
||||||
|
// SameName reports whether the object's name is the same as some
|
||||||
|
// other qualified name, per the rules of Uniqueness of identifiers.
|
||||||
|
SameName(pkg *Package, name string) bool
|
||||||
|
|
||||||
|
// String returns a human-readable string of the object.
|
||||||
String() string
|
String() string
|
||||||
|
|
||||||
|
// setParent sets the parent scope of the object.
|
||||||
setParent(*Scope)
|
setParent(*Scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,11 +47,49 @@ type object struct {
|
||||||
typ Type
|
typ Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *object) Parent() *Scope { return obj.parent }
|
func (obj *object) Parent() *Scope { return obj.parent }
|
||||||
func (obj *object) Pos() token.Pos { return obj.pos }
|
func (obj *object) Pos() token.Pos { return obj.pos }
|
||||||
func (obj *object) Pkg() *Package { return obj.pkg }
|
func (obj *object) Pkg() *Package { return obj.pkg }
|
||||||
func (obj *object) Name() string { return obj.name }
|
func (obj *object) Name() string { return obj.name }
|
||||||
func (obj *object) Type() Type { return obj.typ }
|
func (obj *object) Type() Type { return obj.typ }
|
||||||
|
func (obj *object) IsExported() bool { return ast.IsExported(obj.name) }
|
||||||
|
|
||||||
|
func (obj *object) SameName(pkg *Package, name string) bool {
|
||||||
|
// spec:
|
||||||
|
// "Two identifiers are different if they are spelled differently,
|
||||||
|
// or if they appear in different packages and are not exported.
|
||||||
|
// Otherwise, they are the same."
|
||||||
|
if name != obj.name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// obj.Name == name
|
||||||
|
if obj.IsExported() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// not exported, so packages must be the same (pkg == nil for
|
||||||
|
// fields in Universe scope; this can only happen for types
|
||||||
|
// introduced via Eval)
|
||||||
|
if pkg == nil || obj.pkg == nil {
|
||||||
|
return pkg == obj.pkg
|
||||||
|
}
|
||||||
|
// pkg != nil && obj.pkg != nil
|
||||||
|
return pkg.path == obj.pkg.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *object) uniqueName() string {
|
||||||
|
if obj.IsExported() {
|
||||||
|
return obj.name
|
||||||
|
}
|
||||||
|
// unexported names need the package path for differentiation
|
||||||
|
path := ""
|
||||||
|
if obj.pkg != nil {
|
||||||
|
path = obj.pkg.path
|
||||||
|
if path == "" {
|
||||||
|
path = "?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path + "." + obj.name
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *object) toString(kind string, typ Type) string {
|
func (obj *object) toString(kind string, typ Type) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -136,18 +182,6 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool
|
||||||
func (obj *Field) String() string { return obj.toString("field", obj.typ) }
|
func (obj *Field) String() string { return obj.toString("field", obj.typ) }
|
||||||
func (obj *Field) Anonymous() bool { return obj.anonymous }
|
func (obj *Field) Anonymous() bool { return obj.anonymous }
|
||||||
|
|
||||||
func (f *Field) isMatch(pkg *Package, name string) bool {
|
|
||||||
// spec:
|
|
||||||
// "Two identifiers are different if they are spelled differently,
|
|
||||||
// or if they appear in different packages and are not exported.
|
|
||||||
// Otherwise, they are the same."
|
|
||||||
if name != f.name {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// f.Name == name
|
|
||||||
return ast.IsExported(name) || pkg.path == f.pkg.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Func represents a declared function.
|
// A Func represents a declared function.
|
||||||
type Func struct {
|
type Func struct {
|
||||||
object
|
object
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "go/ast"
|
|
||||||
|
|
||||||
func isNamed(typ Type) bool {
|
func isNamed(typ Type) bool {
|
||||||
if _, ok := typ.(*Basic); ok {
|
if _, ok := typ.(*Basic); ok {
|
||||||
return ok
|
return ok
|
||||||
|
|
@ -133,7 +131,7 @@ func IsIdentical(x, y Type) bool {
|
||||||
g := y.fields[i]
|
g := y.fields[i]
|
||||||
if f.anonymous != g.anonymous ||
|
if f.anonymous != g.anonymous ||
|
||||||
x.Tag(i) != y.Tag(i) ||
|
x.Tag(i) != y.Tag(i) ||
|
||||||
!f.isMatch(g.pkg, g.name) ||
|
!f.SameName(g.pkg, g.name) ||
|
||||||
!IsIdentical(f.typ, g.typ) {
|
!IsIdentical(f.typ, g.typ) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -208,19 +206,6 @@ func identicalTypes(a, b *Tuple) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// qname computes the "qualified name" of a function.
|
|
||||||
// TODO(gri) This is similar in functionality to Field.isMatch.
|
|
||||||
// Try to consolidate.
|
|
||||||
func qname(f *Func) string {
|
|
||||||
if ast.IsExported(f.name) {
|
|
||||||
return f.name
|
|
||||||
}
|
|
||||||
if f.pkg == nil {
|
|
||||||
panic("unexported function without package information")
|
|
||||||
}
|
|
||||||
return f.pkg.path + "." + f.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// identicalMethods returns true if both slices a and b have the
|
// identicalMethods returns true if both slices a and b have the
|
||||||
// same length and corresponding entries have identical types.
|
// same length and corresponding entries have identical types.
|
||||||
// TODO(gri) make this more efficient (e.g., sort them on completion)
|
// TODO(gri) make this more efficient (e.g., sort them on completion)
|
||||||
|
|
@ -231,14 +216,14 @@ func identicalMethods(a, b []*Func) bool {
|
||||||
|
|
||||||
m := make(map[string]*Func)
|
m := make(map[string]*Func)
|
||||||
for _, x := range a {
|
for _, x := range a {
|
||||||
k := qname(x)
|
key := x.uniqueName()
|
||||||
assert(m[k] == nil) // method list must not have duplicate entries
|
assert(m[key] == nil) // method list must not have duplicate entries
|
||||||
m[k] = x
|
m[key] = x
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, y := range b {
|
for _, y := range b {
|
||||||
k := qname(y)
|
key := y.uniqueName()
|
||||||
if x := m[k]; x == nil || !IsIdentical(x.typ, y.typ) {
|
if x := m[key]; x == nil || !IsIdentical(x.typ, y.typ) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
||||||
// gcimported package scopes contain non-exported
|
// gcimported package scopes contain non-exported
|
||||||
// objects such as types used in partially exported
|
// objects such as types used in partially exported
|
||||||
// objects - do not accept them
|
// objects - do not accept them
|
||||||
if ast.IsExported(obj.Name()) {
|
if obj.IsExported() {
|
||||||
// Note: This will change each imported object's scope!
|
// Note: This will change each imported object's scope!
|
||||||
// May be an issue for types aliases.
|
// May be an issue for types aliases.
|
||||||
check.declare(fileScope, nil, obj)
|
check.declare(fileScope, nil, obj)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ type Scope struct {
|
||||||
// scope, if any.
|
// scope, if any.
|
||||||
func NewScope(parent *Scope) *Scope {
|
func NewScope(parent *Scope) *Scope {
|
||||||
scope := &Scope{parent: parent}
|
scope := &Scope{parent: parent}
|
||||||
if parent != nil {
|
// don't add children to Universe scope!
|
||||||
|
if parent != nil && parent != Universe {
|
||||||
parent.children = append(parent.children, scope)
|
parent.children = append(parent.children, scope)
|
||||||
}
|
}
|
||||||
return scope
|
return scope
|
||||||
|
|
@ -42,10 +43,22 @@ func (s *Scope) Parent() *Scope {
|
||||||
return s.parent
|
return s.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node returns the ast.Node responsible for this scope.
|
// Node returns the ast.Node responsible for this scope,
|
||||||
|
// which may be one of the following:
|
||||||
|
//
|
||||||
|
// ast.File
|
||||||
|
// ast.FuncType
|
||||||
|
// ast.BlockStmt
|
||||||
|
// ast.IfStmt
|
||||||
|
// ast.SwitchStmt
|
||||||
|
// ast.TypeSwitchStmt
|
||||||
|
// ast.CaseClause
|
||||||
|
// ast.CommClause
|
||||||
|
// ast.ForStmt
|
||||||
|
// ast.RangeStmt
|
||||||
|
//
|
||||||
// The result is nil if there is no corresponding node
|
// The result is nil if there is no corresponding node
|
||||||
// (e.g., for the universe scope, package scope, or
|
// (universe and package scopes).
|
||||||
// imported packages).
|
|
||||||
func (s *Scope) Node() ast.Node {
|
func (s *Scope) Node() ast.Node {
|
||||||
return s.node
|
return s.node
|
||||||
}
|
}
|
||||||
|
|
@ -107,13 +120,8 @@ func (s *Scope) Lookup(pkg *Package, name string) Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
// slow path: both pkg path and name must match
|
// slow path: both pkg path and name must match
|
||||||
// TODO(gri) if packages were canonicalized, we could just compare the packages
|
|
||||||
for _, obj := range s.entries {
|
for _, obj := range s.entries {
|
||||||
// spec:
|
if obj.SameName(pkg, name) {
|
||||||
// "Two identifiers are different if they are spelled differently,
|
|
||||||
// or if they appear in different packages and are not exported.
|
|
||||||
// Otherwise, they are the same."
|
|
||||||
if obj.Name() == name && (ast.IsExported(name) || obj.Pkg().path == pkg.path) {
|
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +146,8 @@ func (s *Scope) LookupParent(name string) Object {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(gri): Should Insert not be exported?
|
||||||
|
|
||||||
// Insert attempts to insert an object obj into scope s.
|
// Insert attempts to insert an object obj into scope s.
|
||||||
// If s already contains an object with the same package path
|
// If s already contains an object with the same package path
|
||||||
// and name, Insert leaves s unchanged and returns that object.
|
// and name, Insert leaves s unchanged and returns that object.
|
||||||
|
|
@ -155,6 +165,10 @@ func (s *Scope) Insert(obj Object) Object {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteTo writes a string representation of the scope to w.
|
||||||
|
// The level of indentation is controlled by n >= 0, with
|
||||||
|
// n == 0 for no indentation.
|
||||||
|
// If recurse is set, it also prints nested (children) scopes.
|
||||||
func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
|
func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
|
||||||
const ind = ". "
|
const ind = ". "
|
||||||
indn := strings.Repeat(ind, n)
|
indn := strings.Repeat(ind, n)
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ const (
|
||||||
_b1 = assert(iota + iota2 == 5)
|
_b1 = assert(iota + iota2 == 5)
|
||||||
_b2 = len([iota]int{}) // iota may appear in a type!
|
_b2 = len([iota]int{}) // iota may appear in a type!
|
||||||
_b3 = assert(_b2 == 2)
|
_b3 = assert(_b2 == 2)
|
||||||
_b4 = len(A /* ERROR "not a valid composite literal" */ {})
|
_b4 = len(A /* ERROR "invalid composite literal" */ {})
|
||||||
)
|
)
|
||||||
|
|
||||||
type A [iota /* ERROR "cannot use iota" */ ]int
|
type A [iota /* ERROR "cannot use iota" */ ]int
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,12 @@ func struct_literals() {
|
||||||
_ = T0{1, 2} /* ERROR "too few values" */
|
_ = T0{1, 2} /* ERROR "too few values" */
|
||||||
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
|
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
|
||||||
_ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "overflows" */}
|
_ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "overflows" */}
|
||||||
|
|
||||||
|
// invalid type
|
||||||
|
type P *struct{
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
_ = P /* ERROR "invalid composite literal type" */ {}
|
||||||
}
|
}
|
||||||
|
|
||||||
func array_literals() {
|
func array_literals() {
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,8 @@ type Type interface {
|
||||||
// Underlying returns the underlying type of a type.
|
// Underlying returns the underlying type of a type.
|
||||||
Underlying() Type
|
Underlying() Type
|
||||||
|
|
||||||
// For a pointer type (or a named type denoting a pointer type),
|
|
||||||
// Deref returns the pointer's element type. For all other types,
|
|
||||||
// Deref returns the receiver.
|
|
||||||
Deref() Type
|
|
||||||
|
|
||||||
// String returns a string representation of a type.
|
// String returns a string representation of a type.
|
||||||
String() string
|
String() string
|
||||||
|
|
||||||
// TODO(gri) Which other functionality should move here?
|
|
||||||
// Candidates are all predicates (IsIdentical(), etc.),
|
|
||||||
// and some others. What is the design principle?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicKind describes the kind of basic type.
|
// BasicKind describes the kind of basic type.
|
||||||
|
|
@ -371,25 +362,6 @@ func (t *Map) Underlying() Type { return t }
|
||||||
func (t *Chan) Underlying() Type { return t }
|
func (t *Chan) Underlying() Type { return t }
|
||||||
func (t *Named) Underlying() Type { return t.underlying }
|
func (t *Named) Underlying() Type { return t.underlying }
|
||||||
|
|
||||||
func (t *Basic) Deref() Type { return t }
|
|
||||||
func (t *Array) Deref() Type { return t }
|
|
||||||
func (t *Slice) Deref() Type { return t }
|
|
||||||
func (t *Struct) Deref() Type { return t }
|
|
||||||
func (t *Pointer) Deref() Type { return t.base }
|
|
||||||
func (t *Tuple) Deref() Type { return t }
|
|
||||||
func (t *Signature) Deref() Type { return t }
|
|
||||||
func (t *Builtin) Deref() Type { return t }
|
|
||||||
func (t *Interface) Deref() Type { return t }
|
|
||||||
func (t *Map) Deref() Type { return t }
|
|
||||||
func (t *Chan) Deref() Type { return t }
|
|
||||||
func (t *Named) Deref() Type {
|
|
||||||
// TODO(gri) Is this the right operation here given how Deref is used?
|
|
||||||
if p, ok := t.underlying.(*Pointer); ok {
|
|
||||||
return p.base
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Basic) String() string { return typeString(t) }
|
func (t *Basic) String() string { return typeString(t) }
|
||||||
func (t *Array) String() string { return typeString(t) }
|
func (t *Array) String() string { return typeString(t) }
|
||||||
func (t *Slice) String() string { return typeString(t) }
|
func (t *Slice) String() string { return typeString(t) }
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -132,7 +131,7 @@ func def(obj Object) {
|
||||||
}
|
}
|
||||||
// exported identifiers go into package unsafe
|
// exported identifiers go into package unsafe
|
||||||
scope := Universe
|
scope := Universe
|
||||||
if ast.IsExported(name) {
|
if obj.IsExported() {
|
||||||
scope = Unsafe.scope
|
scope = Unsafe.scope
|
||||||
// set Pkg field
|
// set Pkg field
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
|
||||||
}
|
}
|
||||||
|
|
||||||
case "new":
|
case "new":
|
||||||
return emitNew(fn, typ.Underlying().Deref(), pos)
|
return emitNew(fn, deref(typ), pos)
|
||||||
|
|
||||||
case "len", "cap":
|
case "len", "cap":
|
||||||
// Special case: len or cap of an array or *array is
|
// Special case: len or cap of an array or *array is
|
||||||
|
|
@ -310,7 +310,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
|
||||||
// We must still evaluate the value, though. (If it
|
// We must still evaluate the value, though. (If it
|
||||||
// was side-effect free, the whole call would have
|
// was side-effect free, the whole call would have
|
||||||
// been constant-folded.)
|
// been constant-folded.)
|
||||||
t := fn.Pkg.typeOf(args[0]).Deref().Underlying()
|
t := deref(fn.Pkg.typeOf(args[0])).Underlying()
|
||||||
if at, ok := t.(*types.Array); ok {
|
if at, ok := t.(*types.Array); ok {
|
||||||
b.expr(fn, args[0]) // for effects only
|
b.expr(fn, args[0]) // for effects only
|
||||||
return intLiteral(at.Len())
|
return intLiteral(at.Len())
|
||||||
|
|
@ -357,7 +357,7 @@ func (b *builder) selectField(fn *Function, e *ast.SelectorExpr, wantAddr, escap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply field selections.
|
// Apply field selections.
|
||||||
st := tx.Deref().Underlying().(*types.Struct)
|
st := deref(tx).Underlying().(*types.Struct)
|
||||||
for i, index := range indices {
|
for i, index := range indices {
|
||||||
f := st.Field(index)
|
f := st.Field(index)
|
||||||
ft := f.Type()
|
ft := f.Type()
|
||||||
|
|
@ -407,7 +407,7 @@ func (b *builder) selectField(fn *Function, e *ast.SelectorExpr, wantAddr, escap
|
||||||
}
|
}
|
||||||
|
|
||||||
// May be nil at end of last iteration:
|
// May be nil at end of last iteration:
|
||||||
st, _ = ft.Deref().Underlying().(*types.Struct)
|
st, _ = deref(ft).Underlying().(*types.Struct)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v
|
return v
|
||||||
|
|
@ -447,7 +447,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
||||||
return address{addr: v}
|
return address{addr: v}
|
||||||
|
|
||||||
case *ast.CompositeLit:
|
case *ast.CompositeLit:
|
||||||
t := fn.Pkg.typeOf(e).Deref()
|
t := deref(fn.Pkg.typeOf(e))
|
||||||
var v Value
|
var v Value
|
||||||
if escaping {
|
if escaping {
|
||||||
v = emitNew(fn, t, e.Lbrace)
|
v = emitNew(fn, t, e.Lbrace)
|
||||||
|
|
@ -1639,7 +1639,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
||||||
fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
|
fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
|
||||||
}
|
}
|
||||||
ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
|
ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
|
||||||
ok.store(fn, emitExtract(fn, sel, 1, ok.typ().Deref()))
|
ok.store(fn, emitExtract(fn, sel, 1, deref(ok.typ())))
|
||||||
}
|
}
|
||||||
r++
|
r++
|
||||||
}
|
}
|
||||||
|
|
@ -1736,7 +1736,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
||||||
|
|
||||||
// Determine number of iterations.
|
// Determine number of iterations.
|
||||||
var length Value
|
var length Value
|
||||||
if arr, ok := x.Type().Deref().(*types.Array); ok {
|
if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok {
|
||||||
// For array or *array, the number of iterations is
|
// For array or *array, the number of iterations is
|
||||||
// known statically thanks to the type. We avoid a
|
// known statically thanks to the type. We avoid a
|
||||||
// data dependence upon x, permitting later dead-code
|
// data dependence upon x, permitting later dead-code
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
||||||
} else {
|
} else {
|
||||||
// Method declaration.
|
// Method declaration.
|
||||||
_, method := namedTypeMethodIndex(
|
_, method := namedTypeMethodIndex(
|
||||||
recv.Type().Deref().(*types.Named),
|
deref(recv.Type()).(*types.Named),
|
||||||
MakeId(name, pkg.Object))
|
MakeId(name, pkg.Object))
|
||||||
pkg.Prog.concreteMethods[method] = fn
|
pkg.Prog.concreteMethods[method] = fn
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value {
|
||||||
//
|
//
|
||||||
func emitLoad(f *Function, addr Value) *UnOp {
|
func emitLoad(f *Function, addr Value) *UnOp {
|
||||||
v := &UnOp{Op: token.MUL, X: addr}
|
v := &UnOp{Op: token.MUL, X: addr}
|
||||||
v.setType(addr.Type().Deref())
|
v.setType(deref(addr.Type()))
|
||||||
f.emit(v)
|
f.emit(v)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
||||||
func emitStore(f *Function, addr, val Value) *Store {
|
func emitStore(f *Function, addr, val Value) *Store {
|
||||||
s := &Store{
|
s := &Store{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Val: emitConv(f, val, addr.Type().Deref()),
|
Val: emitConv(f, val, deref(addr.Type())),
|
||||||
}
|
}
|
||||||
f.emit(s)
|
f.emit(s)
|
||||||
return s
|
return s
|
||||||
|
|
|
||||||
|
|
@ -557,7 +557,7 @@ func (f *Function) DumpTo(w io.Writer) {
|
||||||
if len(f.Locals) > 0 {
|
if len(f.Locals) > 0 {
|
||||||
io.WriteString(w, "# Locals:\n")
|
io.WriteString(w, "# Locals:\n")
|
||||||
for i, l := range f.Locals {
|
for i, l := range f.Locals {
|
||||||
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), l.Type().Deref())
|
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), deref(l.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
|
||||||
// local
|
// local
|
||||||
addr = fr.env[instr].(*value)
|
addr = fr.env[instr].(*value)
|
||||||
}
|
}
|
||||||
*addr = zero(instr.Type().Deref())
|
*addr = zero(deref(instr.Type()))
|
||||||
|
|
||||||
case *ssa.MakeSlice:
|
case *ssa.MakeSlice:
|
||||||
slice := make([]value, asInt(fr.get(instr.Cap)))
|
slice := make([]value, asInt(fr.get(instr.Cap)))
|
||||||
|
|
@ -464,7 +464,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
|
||||||
locals: make([]value, len(fn.Locals)),
|
locals: make([]value, len(fn.Locals)),
|
||||||
}
|
}
|
||||||
for i, l := range fn.Locals {
|
for i, l := range fn.Locals {
|
||||||
fr.locals[i] = zero(l.Type().Deref())
|
fr.locals[i] = zero(deref(l.Type()))
|
||||||
fr.env[l] = &fr.locals[i]
|
fr.env[l] = &fr.locals[i]
|
||||||
}
|
}
|
||||||
for i, p := range fn.Params {
|
for i, p := range fn.Params {
|
||||||
|
|
@ -549,7 +549,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string)
|
||||||
for _, m := range pkg.Members {
|
for _, m := range pkg.Members {
|
||||||
switch v := m.(type) {
|
switch v := m.(type) {
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
cell := zero(v.Type().Deref())
|
cell := zero(deref(v.Type()))
|
||||||
i.globals[v] = &cell
|
i.globals[v] = &cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -619,3 +619,12 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deref returns a pointer's element type; otherwise it returns typ.
|
||||||
|
// TODO(adonovan): Import from ssa?
|
||||||
|
func deref(typ types.Type) types.Type {
|
||||||
|
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||||||
|
return p.Elem()
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
||||||
// important optimisation for pointer analysis because the
|
// important optimisation for pointer analysis because the
|
||||||
// extra indirection really hurts precision under Das's
|
// extra indirection really hurts precision under Das's
|
||||||
// algorithm.
|
// algorithm.
|
||||||
switch alloc.Type().Deref().Underlying().(type) {
|
switch deref(alloc.Type()).Underlying().(type) {
|
||||||
case *types.Array, *types.Struct:
|
case *types.Array, *types.Struct:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -379,7 +379,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
||||||
Comment: alloc.Name(),
|
Comment: alloc.Name(),
|
||||||
}
|
}
|
||||||
phi.pos = alloc.Pos()
|
phi.pos = alloc.Pos()
|
||||||
phi.setType(alloc.Type().Deref())
|
phi.setType(deref(alloc.Type()))
|
||||||
phi.block = v
|
phi.block = v
|
||||||
if debugLifting {
|
if debugLifting {
|
||||||
fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v)
|
fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v)
|
||||||
|
|
@ -426,7 +426,7 @@ func replaceAll(x, y Value) {
|
||||||
func renamed(renaming []Value, alloc *Alloc) Value {
|
func renamed(renaming []Value, alloc *Alloc) Value {
|
||||||
v := renaming[alloc.index]
|
v := renaming[alloc.index]
|
||||||
if v == nil {
|
if v == nil {
|
||||||
v = zeroLiteral(alloc.Type().Deref())
|
v = zeroLiteral(deref(alloc.Type()))
|
||||||
renaming[alloc.index] = v
|
renaming[alloc.index] = v
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func (a address) store(fn *Function, v Value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a address) typ() types.Type {
|
func (a address) typ() types.Type {
|
||||||
return a.addr.Type().Deref()
|
return deref(a.addr.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
// An element is an lvalue represented by m[k], the location of an
|
// An element is an lvalue represented by m[k], the location of an
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func (v *Alloc) String() string {
|
||||||
if v.Heap {
|
if v.Heap {
|
||||||
op = "new"
|
op = "new"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s %s", op, v.Type().Deref())
|
return fmt.Sprintf("%s %s", op, deref(v.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Phi) String() string {
|
func (v *Phi) String() string {
|
||||||
|
|
@ -225,7 +225,7 @@ func (v *MakeChan) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *FieldAddr) String() string {
|
func (v *FieldAddr) String() string {
|
||||||
st := v.X.Type().Deref().Underlying().(*types.Struct)
|
st := deref(v.X.Type()).Underlying().(*types.Struct)
|
||||||
// Be robust against a bad index.
|
// Be robust against a bad index.
|
||||||
name := "?"
|
name := "?"
|
||||||
if 0 <= v.Field && v.Field < st.NumFields() {
|
if 0 <= v.Field && v.Field < st.NumFields() {
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||||
if node != nil {
|
if node != nil {
|
||||||
t = node.field.Type()
|
t = node.field.Type()
|
||||||
}
|
}
|
||||||
t = t.Deref()
|
t = deref(t)
|
||||||
|
|
||||||
if nt, ok := t.(*types.Named); ok {
|
if nt, ok := t.(*types.Named); ok {
|
||||||
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
for i, n := 0, nt.NumMethods(); i < n; i++ {
|
||||||
|
|
@ -309,7 +309,7 @@ func promotionWrapper(prog *Program, typ types.Type, cand *candidate) *Function
|
||||||
// Iterate over selections e.A.B.C.f in the natural order [A,B,C].
|
// Iterate over selections e.A.B.C.f in the natural order [A,B,C].
|
||||||
for _, p := range cand.path.reverse() {
|
for _, p := range cand.path.reverse() {
|
||||||
// Loop invariant: v holds a pointer to a struct.
|
// Loop invariant: v holds a pointer to a struct.
|
||||||
if _, ok := v.Type().Deref().Underlying().(*types.Struct); !ok {
|
if _, ok := deref(v.Type()).Underlying().(*types.Struct); !ok {
|
||||||
panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type))
|
panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type))
|
||||||
}
|
}
|
||||||
sel := &FieldAddr{
|
sel := &FieldAddr{
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,14 @@ func pointer(typ types.Type) *types.Pointer {
|
||||||
return types.NewPointer(typ)
|
return types.NewPointer(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deref returns a pointer's element type; otherwise it returns typ.
|
||||||
|
func deref(typ types.Type) types.Type {
|
||||||
|
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||||||
|
return p.Elem()
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
// namedTypeMethodIndex returns the method (and its index) named id
|
// namedTypeMethodIndex returns the method (and its index) named id
|
||||||
// within the set of explicitly declared concrete methods of named
|
// within the set of explicitly declared concrete methods of named
|
||||||
// type typ. If not found, panic ensues.
|
// type typ. If not found, panic ensues.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue