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 {
|
||||
goto Error
|
||||
}
|
||||
base := x.typ.Deref()
|
||||
base, _ := deref(x.typ)
|
||||
sel := arg.Sel.Name
|
||||
obj, index, indirect := LookupFieldOrMethod(base, check.pkg, arg.Sel.Name)
|
||||
switch obj.(type) {
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
if exp == nil {
|
||||
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
|
||||
goto Error
|
||||
} else if !ast.IsExported(exp.Name()) {
|
||||
} else if !exp.IsExported() {
|
||||
// gcimported package scopes contain non-exported
|
||||
// objects such as types used in partially exported
|
||||
// objects - do not accept them
|
||||
|
|
|
|||
|
|
@ -15,12 +15,6 @@ import (
|
|||
"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
|
||||
// expression or type literal string evaluated in Universe scope.
|
||||
// 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
|
||||
}
|
||||
|
||||
switch utyp := typ.Deref().Underlying().(type) {
|
||||
switch typ, _ := deref(typ); utyp := typ.Underlying().(type) {
|
||||
case *Struct:
|
||||
if len(e.Elts) == 0 {
|
||||
break
|
||||
|
|
@ -991,7 +991,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@ func (p *gcParser) parseField() (*Field, string) {
|
|||
anonymous := false
|
||||
if 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
|
||||
pkg = nil
|
||||
name = typ.name
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
package types
|
||||
|
||||
import "go/ast"
|
||||
|
||||
// TODO(gri) The named type consolidation and seen maps below must be
|
||||
// indexed by unique keys for a given type. Verify that named
|
||||
// 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:
|
||||
// look for a matching field and collect embedded types
|
||||
for i, f := range t.fields {
|
||||
if f.isMatch(pkg, name) {
|
||||
if f.SameName(pkg, name) {
|
||||
assert(f.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
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.
|
||||
// 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) {
|
||||
if p, _ := typ.(*Pointer); p != nil {
|
||||
return p.base, true
|
||||
|
|
@ -297,11 +293,7 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int {
|
|||
return -1 // blank identifiers are never found
|
||||
}
|
||||
for i, f := range fields {
|
||||
// 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 f.name == name && (ast.IsExported(name) || f.pkg.path == pkg.path) {
|
||||
if f.SameName(pkg, name) {
|
||||
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) {
|
||||
assert(name != "_")
|
||||
for i, m := range methods {
|
||||
// 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 m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) {
|
||||
if m.SameName(pkg, name) {
|
||||
return i, m
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ package types
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
|
@ -25,7 +24,7 @@ func (s *MethodSet) String() string {
|
|||
fmt.Fprintln(&buf)
|
||||
}
|
||||
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, "}")
|
||||
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.
|
||||
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 {
|
||||
m := s.list[i]
|
||||
return key(m.pkg, m.name) >= k
|
||||
return m.uniqueName() >= key
|
||||
})
|
||||
|
||||
if i < len(s.list) {
|
||||
m := s.list[i]
|
||||
if key(m.pkg, m.name) == k {
|
||||
if m.uniqueName() == key {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
|
@ -157,22 +156,11 @@ func NewMethodSet(typ Type) *MethodSet {
|
|||
list = append(list, m)
|
||||
}
|
||||
}
|
||||
sort.Sort(byKey(list))
|
||||
sort.Sort(byUniqueName(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 conflict indicates that multiple fields with the same package and name appeared.
|
||||
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
|
||||
// and is treated as a collision at this level.
|
||||
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 !multiples {
|
||||
if _, found := s[k]; !found {
|
||||
s[k] = f
|
||||
if _, found := s[key]; !found {
|
||||
s[key] = f
|
||||
return
|
||||
}
|
||||
}
|
||||
s[k] = nil // collision
|
||||
s[key] = nil // collision
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (s methodSet) add(list []*Func, multiples bool) {
|
||||
for _, m := range list {
|
||||
k := key(m.pkg, m.name)
|
||||
key := m.uniqueName()
|
||||
// if m is not in the set, add it
|
||||
if !multiples {
|
||||
if _, found := s[k]; !found {
|
||||
s[k] = m
|
||||
if _, found := s[key]; !found {
|
||||
s[key] = m
|
||||
continue
|
||||
}
|
||||
}
|
||||
s[k] = nil // collision
|
||||
s[key] = nil // collision
|
||||
}
|
||||
}
|
||||
|
||||
// byKey function lists can be sorted by key(pkg, name).
|
||||
type byKey []*Func
|
||||
// byUniqueName function lists can be sorted by their unique names.
|
||||
type byUniqueName []*Func
|
||||
|
||||
func (a byKey) Len() int { return len(a) }
|
||||
func (a byKey) Less(i, j int) bool {
|
||||
x := 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] }
|
||||
func (a byUniqueName) Len() int { return len(a) }
|
||||
func (a byUniqueName) Less(i, j int) bool { return a[i].uniqueName() < a[j].uniqueName() }
|
||||
func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
|
|
|||
|
|
@ -20,13 +20,21 @@ import (
|
|||
// All objects implement the Object interface.
|
||||
//
|
||||
type Object interface {
|
||||
Parent() *Scope // scope in which this object is declared
|
||||
Pos() token.Pos // position of object identifier in declaration
|
||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||
Name() string // package local object name
|
||||
Type() Type // object type
|
||||
Parent() *Scope // scope in which this object is declared
|
||||
Pos() token.Pos // position of object identifier in declaration
|
||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||
Name() string // package local object name
|
||||
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
|
||||
|
||||
// setParent sets the parent scope of the object.
|
||||
setParent(*Scope)
|
||||
}
|
||||
|
||||
|
|
@ -39,11 +47,49 @@ type object struct {
|
|||
typ Type
|
||||
}
|
||||
|
||||
func (obj *object) Parent() *Scope { return obj.parent }
|
||||
func (obj *object) Pos() token.Pos { return obj.pos }
|
||||
func (obj *object) Pkg() *Package { return obj.pkg }
|
||||
func (obj *object) Name() string { return obj.name }
|
||||
func (obj *object) Type() Type { return obj.typ }
|
||||
func (obj *object) Parent() *Scope { return obj.parent }
|
||||
func (obj *object) Pos() token.Pos { return obj.pos }
|
||||
func (obj *object) Pkg() *Package { return obj.pkg }
|
||||
func (obj *object) Name() string { return obj.name }
|
||||
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 {
|
||||
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) 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.
|
||||
type Func struct {
|
||||
object
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
package types
|
||||
|
||||
import "go/ast"
|
||||
|
||||
func isNamed(typ Type) bool {
|
||||
if _, ok := typ.(*Basic); ok {
|
||||
return ok
|
||||
|
|
@ -133,7 +131,7 @@ func IsIdentical(x, y Type) bool {
|
|||
g := y.fields[i]
|
||||
if f.anonymous != g.anonymous ||
|
||||
x.Tag(i) != y.Tag(i) ||
|
||||
!f.isMatch(g.pkg, g.name) ||
|
||||
!f.SameName(g.pkg, g.name) ||
|
||||
!IsIdentical(f.typ, g.typ) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -208,19 +206,6 @@ func identicalTypes(a, b *Tuple) bool {
|
|||
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
|
||||
// same length and corresponding entries have identical types.
|
||||
// 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)
|
||||
for _, x := range a {
|
||||
k := qname(x)
|
||||
assert(m[k] == nil) // method list must not have duplicate entries
|
||||
m[k] = x
|
||||
key := x.uniqueName()
|
||||
assert(m[key] == nil) // method list must not have duplicate entries
|
||||
m[key] = x
|
||||
}
|
||||
|
||||
for _, y := range b {
|
||||
k := qname(y)
|
||||
if x := m[k]; x == nil || !IsIdentical(x.typ, y.typ) {
|
||||
key := y.uniqueName()
|
||||
if x := m[key]; x == nil || !IsIdentical(x.typ, y.typ) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
|
|||
// gcimported package scopes contain non-exported
|
||||
// objects such as types used in partially exported
|
||||
// objects - do not accept them
|
||||
if ast.IsExported(obj.Name()) {
|
||||
if obj.IsExported() {
|
||||
// Note: This will change each imported object's scope!
|
||||
// May be an issue for types aliases.
|
||||
check.declare(fileScope, nil, obj)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ type Scope struct {
|
|||
// scope, if any.
|
||||
func NewScope(parent *Scope) *Scope {
|
||||
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)
|
||||
}
|
||||
return scope
|
||||
|
|
@ -42,10 +43,22 @@ func (s *Scope) Parent() *Scope {
|
|||
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
|
||||
// (e.g., for the universe scope, package scope, or
|
||||
// imported packages).
|
||||
// (universe and package scopes).
|
||||
func (s *Scope) Node() ast.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
|
||||
// TODO(gri) if packages were canonicalized, we could just compare the packages
|
||||
for _, obj := range s.entries {
|
||||
// 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 obj.Name() == name && (ast.IsExported(name) || obj.Pkg().path == pkg.path) {
|
||||
if obj.SameName(pkg, name) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
|
@ -138,6 +146,8 @@ func (s *Scope) LookupParent(name string) Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO(gri): Should Insert not be exported?
|
||||
|
||||
// Insert attempts to insert an object obj into scope s.
|
||||
// If s already contains an object with the same package path
|
||||
// and name, Insert leaves s unchanged and returns that object.
|
||||
|
|
@ -155,6 +165,10 @@ func (s *Scope) Insert(obj Object) Object {
|
|||
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) {
|
||||
const ind = ". "
|
||||
indn := strings.Repeat(ind, n)
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ const (
|
|||
_b1 = assert(iota + iota2 == 5)
|
||||
_b2 = len([iota]int{}) // iota may appear in a type!
|
||||
_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
|
||||
|
|
|
|||
|
|
@ -146,6 +146,12 @@ func struct_literals() {
|
|||
_ = T0{1, 2} /* ERROR "too few values" */
|
||||
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
|
||||
_ = 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() {
|
||||
|
|
|
|||
|
|
@ -14,17 +14,8 @@ type Type interface {
|
|||
// Underlying returns the underlying type of a 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() 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.
|
||||
|
|
@ -371,25 +362,6 @@ func (t *Map) Underlying() Type { return t }
|
|||
func (t *Chan) Underlying() Type { return t }
|
||||
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 *Array) String() string { return typeString(t) }
|
||||
func (t *Slice) String() string { return typeString(t) }
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
|
|
@ -132,7 +131,7 @@ func def(obj Object) {
|
|||
}
|
||||
// exported identifiers go into package unsafe
|
||||
scope := Universe
|
||||
if ast.IsExported(name) {
|
||||
if obj.IsExported() {
|
||||
scope = Unsafe.scope
|
||||
// set Pkg field
|
||||
switch obj := obj.(type) {
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
|
|||
}
|
||||
|
||||
case "new":
|
||||
return emitNew(fn, typ.Underlying().Deref(), pos)
|
||||
return emitNew(fn, deref(typ), pos)
|
||||
|
||||
case "len", "cap":
|
||||
// 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
|
||||
// was side-effect free, the whole call would have
|
||||
// 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 {
|
||||
b.expr(fn, args[0]) // for effects only
|
||||
return intLiteral(at.Len())
|
||||
|
|
@ -357,7 +357,7 @@ func (b *builder) selectField(fn *Function, e *ast.SelectorExpr, wantAddr, escap
|
|||
}
|
||||
|
||||
// Apply field selections.
|
||||
st := tx.Deref().Underlying().(*types.Struct)
|
||||
st := deref(tx).Underlying().(*types.Struct)
|
||||
for i, index := range indices {
|
||||
f := st.Field(index)
|
||||
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:
|
||||
st, _ = ft.Deref().Underlying().(*types.Struct)
|
||||
st, _ = deref(ft).Underlying().(*types.Struct)
|
||||
}
|
||||
|
||||
return v
|
||||
|
|
@ -447,7 +447,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
|
|||
return address{addr: v}
|
||||
|
||||
case *ast.CompositeLit:
|
||||
t := fn.Pkg.typeOf(e).Deref()
|
||||
t := deref(fn.Pkg.typeOf(e))
|
||||
var v Value
|
||||
if escaping {
|
||||
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))
|
||||
}
|
||||
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++
|
||||
}
|
||||
|
|
@ -1736,7 +1736,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
|||
|
||||
// Determine number of iterations.
|
||||
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
|
||||
// known statically thanks to the type. We avoid a
|
||||
// data dependence upon x, permitting later dead-code
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
} else {
|
||||
// Method declaration.
|
||||
_, method := namedTypeMethodIndex(
|
||||
recv.Type().Deref().(*types.Named),
|
||||
deref(recv.Type()).(*types.Named),
|
||||
MakeId(name, pkg.Object))
|
||||
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 {
|
||||
v := &UnOp{Op: token.MUL, X: addr}
|
||||
v.setType(addr.Type().Deref())
|
||||
v.setType(deref(addr.Type()))
|
||||
f.emit(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 {
|
||||
s := &Store{
|
||||
Addr: addr,
|
||||
Val: emitConv(f, val, addr.Type().Deref()),
|
||||
Val: emitConv(f, val, deref(addr.Type())),
|
||||
}
|
||||
f.emit(s)
|
||||
return s
|
||||
|
|
|
|||
|
|
@ -557,7 +557,7 @@ func (f *Function) DumpTo(w io.Writer) {
|
|||
if len(f.Locals) > 0 {
|
||||
io.WriteString(w, "# Locals:\n")
|
||||
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
|
||||
addr = fr.env[instr].(*value)
|
||||
}
|
||||
*addr = zero(instr.Type().Deref())
|
||||
*addr = zero(deref(instr.Type()))
|
||||
|
||||
case *ssa.MakeSlice:
|
||||
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)),
|
||||
}
|
||||
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]
|
||||
}
|
||||
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 {
|
||||
switch v := m.(type) {
|
||||
case *ssa.Global:
|
||||
cell := zero(v.Type().Deref())
|
||||
cell := zero(deref(v.Type()))
|
||||
i.globals[v] = &cell
|
||||
}
|
||||
}
|
||||
|
|
@ -619,3 +619,12 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string)
|
|||
}
|
||||
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
|
||||
// extra indirection really hurts precision under Das's
|
||||
// algorithm.
|
||||
switch alloc.Type().Deref().Underlying().(type) {
|
||||
switch deref(alloc.Type()).Underlying().(type) {
|
||||
case *types.Array, *types.Struct:
|
||||
return false
|
||||
}
|
||||
|
|
@ -379,7 +379,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
|||
Comment: alloc.Name(),
|
||||
}
|
||||
phi.pos = alloc.Pos()
|
||||
phi.setType(alloc.Type().Deref())
|
||||
phi.setType(deref(alloc.Type()))
|
||||
phi.block = v
|
||||
if debugLifting {
|
||||
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 {
|
||||
v := renaming[alloc.index]
|
||||
if v == nil {
|
||||
v = zeroLiteral(alloc.Type().Deref())
|
||||
v = zeroLiteral(deref(alloc.Type()))
|
||||
renaming[alloc.index] = v
|
||||
}
|
||||
return v
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ func (a address) store(fn *Function, v Value) {
|
|||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func (v *Alloc) String() string {
|
|||
if v.Heap {
|
||||
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 {
|
||||
|
|
@ -225,7 +225,7 @@ func (v *MakeChan) 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.
|
||||
name := "?"
|
||||
if 0 <= v.Field && v.Field < st.NumFields() {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
if node != nil {
|
||||
t = node.field.Type()
|
||||
}
|
||||
t = t.Deref()
|
||||
t = deref(t)
|
||||
|
||||
if nt, ok := t.(*types.Named); ok {
|
||||
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].
|
||||
for _, p := range cand.path.reverse() {
|
||||
// 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))
|
||||
}
|
||||
sel := &FieldAddr{
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ func pointer(typ types.Type) *types.Pointer {
|
|||
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
|
||||
// within the set of explicitly declared concrete methods of named
|
||||
// type typ. If not found, panic ensues.
|
||||
|
|
|
|||
Loading…
Reference in New Issue