go.tools: bring up to date
Repo was copied from old point. Bad r.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/9504043
diff --git a/go/types/api.go b/go/types/api.go
index 70920c0..1dd46d4 100644
--- a/go/types/api.go
+++ b/go/types/api.go
@@ -29,10 +29,7 @@
// The API is still slightly in flux and the following changes are considered:
//
-// API(gri): Provide accessors uniformly to all fields and do not export fields directly.
-// API(gri): Provide scope information for all objects.
// API(gri): Provide position information for all objects.
-// API(gri): The semantics of QualifiedIdent needs to be revisited.
// API(gri): The GcImporter should probably be in its own package - it is only one of possible importers.
import (
@@ -111,12 +108,12 @@
// entire package is checked. If there are errors, the package may be
// only partially type-checked, and the resulting package may be incomplete
// (missing objects, imports, etc.).
-func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
- return check(ctxt, fset, files)
+func (ctxt *Context) Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) {
+ return check(ctxt, path, fset, files...)
}
// Check is shorthand for ctxt.Check where ctxt is a default (empty) context.
-func Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
+func Check(path string, fset *token.FileSet, files ...*ast.File) (*Package, error) {
var ctxt Context
- return ctxt.Check(fset, files)
+ return ctxt.Check(path, fset, files...)
}
diff --git a/go/types/builtins.go b/go/types/builtins.go
index 5ede421..4bd7d33 100644
--- a/go/types/builtins.go
+++ b/go/types/builtins.go
@@ -21,7 +21,7 @@
// of the call is returned via x. If the call has type errors, the returned x is marked
// as invalid (x.mode == invalid).
//
-func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) {
+func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota int) {
args := call.Args
id := bin.id
@@ -59,7 +59,7 @@
switch id {
case _Append:
- if _, ok := underlying(x.typ).(*Slice); !ok {
+ if _, ok := x.typ.Underlying().(*Slice); !ok {
check.invalidArg(x.pos(), "%s is not a typed slice", x)
goto Error
}
@@ -77,7 +77,7 @@
case _Cap, _Len:
mode := invalid
var val exact.Value
- switch typ := implicitArrayDeref(underlying(x.typ)).(type) {
+ switch typ := implicitArrayDeref(x.typ.Underlying()).(type) {
case *Basic:
if isString(typ) && id == _Len {
if x.mode == constant {
@@ -96,7 +96,7 @@
// function calls; in this case s is not evaluated."
if !check.containsCallsOrReceives(arg0) {
mode = constant
- val = exact.MakeInt64(typ.Len)
+ val = exact.MakeInt64(typ.len)
}
case *Slice, *Chan:
@@ -117,12 +117,12 @@
x.val = val
case _Close:
- ch, ok := underlying(x.typ).(*Chan)
+ ch, ok := x.typ.Underlying().(*Chan)
if !ok {
check.invalidArg(x.pos(), "%s is not a channel", x)
goto Error
}
- if ch.Dir&ast.SEND == 0 {
+ if ch.dir&ast.SEND == 0 {
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
goto Error
}
@@ -156,14 +156,14 @@
goto Error
}
- typ := underlying(x.typ).(*Basic)
+ typ := x.typ.Underlying().(*Basic)
if x.mode == constant && y.mode == constant {
x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val))
} else {
x.mode = value
}
- switch typ.Kind {
+ switch typ.kind {
case Float32:
x.typ = Typ[Complex64]
case Float64:
@@ -202,16 +202,16 @@
}
var dst, src Type
- if t, ok := underlying(x.typ).(*Slice); ok {
- dst = t.Elt
+ if t, ok := x.typ.Underlying().(*Slice); ok {
+ dst = t.elt
}
- switch t := underlying(y.typ).(type) {
+ switch t := y.typ.Underlying().(type) {
case *Basic:
if isString(y.typ) {
src = Typ[Byte]
}
case *Slice:
- src = t.Elt
+ src = t.elt
}
if dst == nil || src == nil {
@@ -228,7 +228,7 @@
x.typ = Typ[Int]
case _Delete:
- m, ok := underlying(x.typ).(*Map)
+ m, ok := x.typ.Underlying().(*Map)
if !ok {
check.invalidArg(x.pos(), "%s is not a map", x)
goto Error
@@ -237,8 +237,8 @@
if x.mode == invalid {
goto Error
}
- if !x.isAssignable(check.ctxt, m.Key) {
- check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key)
+ if !x.isAssignable(check.ctxt, m.key) {
+ check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
goto Error
}
x.mode = novalue
@@ -258,7 +258,7 @@
x.mode = value
}
k := Invalid
- switch underlying(x.typ).(*Basic).Kind {
+ switch x.typ.Underlying().(*Basic).kind {
case Complex64:
k = Float32
case Complex128:
@@ -276,7 +276,7 @@
goto Error
}
var min int // minimum number of arguments
- switch underlying(resultTyp).(type) {
+ switch resultTyp.Underlying().(type) {
case *Slice:
min = 2
case *Map, *Chan:
@@ -308,7 +308,7 @@
goto Error
}
x.mode = variable
- x.typ = &Pointer{Base: resultTyp}
+ x.typ = &Pointer{base: resultTyp}
case _Panic:
x.mode = novalue
@@ -342,12 +342,12 @@
goto Error
}
sel := arg.Sel.Name
- res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name})
+ res := lookupField(x.typ, check.pkg, arg.Sel.Name)
if res.index == nil {
check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
goto Error
}
- offs := check.ctxt.offsetof(deref(x.typ), res.index)
+ offs := check.ctxt.offsetof(x.typ.Deref(), res.index)
if offs < 0 {
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x)
goto Error
@@ -415,7 +415,7 @@
//
func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
- if a, ok := underlying(p.Base).(*Array); ok {
+ if a, ok := p.base.Underlying().(*Array); ok {
return a
}
}
@@ -454,8 +454,8 @@
}
func (check *checker) complexArg(x *operand) bool {
- t, _ := underlying(x.typ).(*Basic)
- if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) {
+ t, _ := x.typ.Underlying().(*Basic)
+ if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) {
return true
}
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
diff --git a/go/types/check.go b/go/types/check.go
index 5a8cf5f..4b41195 100644
--- a/go/types/check.go
+++ b/go/types/check.go
@@ -117,7 +117,7 @@
if ident.Name != "_" {
if alt := scope.Insert(obj); alt != nil {
prevDecl := ""
- if pos := alt.GetPos(); pos.IsValid() {
+ if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
}
check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
@@ -153,9 +153,9 @@
assert(l != nil)
switch obj := obj.(type) {
case *Const:
- obj.Type = typ
+ obj.typ = typ
case *Var:
- obj.Type = typ
+ obj.typ = typ
default:
unreachable()
}
@@ -174,9 +174,9 @@
for _, name := range lhs {
switch obj := check.lookup(name).(type) {
case *Const:
- obj.Type = typ
+ obj.typ = typ
case *Var:
- obj.Type = typ
+ obj.typ = typ
default:
unreachable()
}
@@ -202,7 +202,7 @@
// nothing to do
case *Const:
- if obj.Type != nil {
+ if obj.typ != nil {
return // already checked
}
// The obj.Val field for constants is initialized to its respective
@@ -214,15 +214,15 @@
// know that x is a constant and has type float32, but we don't
// have a value due to the error in the conversion).
if obj.visited {
- check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name)
- obj.Type = Typ[Invalid]
+ check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.Name)
+ obj.typ = Typ[Invalid]
return
}
obj.visited = true
spec := obj.spec
- iota, ok := exact.Int64Val(obj.Val)
+ iota, ok := exact.Int64Val(obj.val)
assert(ok)
- obj.Val = exact.MakeUnknown()
+ obj.val = exact.MakeUnknown()
// determine spec for type and initialization expressions
init := spec
if len(init.Values) == 0 {
@@ -231,12 +231,12 @@
check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota))
case *Var:
- if obj.Type != nil {
+ if obj.typ != nil {
return // already checked
}
if obj.visited {
- check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name)
- obj.Type = Typ[Invalid]
+ check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.Name)
+ obj.typ = Typ[Invalid]
return
}
obj.visited = true
@@ -252,20 +252,20 @@
}
case *TypeName:
- if obj.Type != nil {
+ if obj.typ != nil {
return // already checked
}
- typ := &NamedType{Obj: obj}
- obj.Type = typ // "mark" object so recursion terminates
- typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk))
+ typ := &Named{obj: obj}
+ obj.typ = typ // "mark" object so recursion terminates
+ typ.underlying = check.typ(obj.spec.Type, cycleOk).Underlying()
// typecheck associated method signatures
if scope := check.methods[obj]; scope != nil {
- switch t := typ.Underlying.(type) {
+ switch t := typ.underlying.(type) {
case *Struct:
// struct fields must not conflict with methods
- for _, f := range t.Fields {
+ for _, f := range t.fields {
if m := scope.Lookup(f.Name); m != nil {
- check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name)
+ check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
// ok to continue
}
}
@@ -278,33 +278,33 @@
}
}
// typecheck method signatures
- var methods []*Method
+ var methods ObjSet
for _, obj := range scope.Entries {
m := obj.(*Func)
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
params, _ := check.collectParams(m.decl.Recv, false)
- sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
- m.Type = sig
- methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig})
+ sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
+ m.typ = sig
+ assert(methods.Insert(obj) == nil)
check.later(m, sig, m.decl.Body)
}
- typ.Methods = methods
+ typ.methods = methods
delete(check.methods, obj) // we don't need this scope anymore
}
case *Func:
- if obj.Type != nil {
+ if obj.typ != nil {
return // already checked
}
fdecl := obj.decl
// methods are typechecked when their receivers are typechecked
if fdecl.Recv == nil {
sig := check.typ(fdecl.Type, cycleOk).(*Signature)
- if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) {
+ if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
// ok to continue
}
- obj.Type = sig
+ obj.typ = sig
check.later(obj, sig, fdecl.Body)
}
@@ -374,7 +374,7 @@
scope = new(Scope)
check.methods[tname] = scope
}
- check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth})
+ check.declareIdent(scope, meth.Name, &Func{pkg: check.pkg, name: meth.Name.Name, decl: meth})
}
func (check *checker) decl(decl ast.Decl) {
@@ -406,7 +406,7 @@
// since they are not in any scope. Create a dummy object for them.
if d.Name.Name == "init" {
assert(obj == nil) // all other functions should have an object
- obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d}
+ obj = &Func{pkg: check.pkg, name: d.Name.Name, decl: d}
check.register(d.Name, obj)
}
check.object(obj, false)
@@ -418,12 +418,13 @@
// A bailout panic is raised to indicate early termination.
type bailout struct{}
-func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
+func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) {
// initialize checker
check := checker{
ctxt: ctxt,
fset: fset,
files: files,
+ pkg: &Package{path: path, scope: &Scope{Outer: Universe}, imports: make(map[string]*Package)},
idents: make(map[*ast.Ident]Object),
objects: make(map[*ast.Object]Object),
initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
@@ -476,13 +477,13 @@
if trace {
s := "<function literal>"
if f.obj != nil {
- s = f.obj.Name
+ s = f.obj.name
}
fmt.Println("---", s)
}
check.funcsig = f.sig
check.stmtList(f.body.List)
- if len(f.sig.Results) > 0 && f.body != nil && !check.isTerminating(f.body, "") {
+ if f.sig.results.Len() > 0 && f.body != nil && !check.isTerminating(f.body, "") {
check.errorf(f.body.Rbrace, "missing return")
}
}
diff --git a/go/types/check_test.go b/go/types/check_test.go
index 2ee7f6e..28d9804 100644
--- a/go/types/check_test.go
+++ b/go/types/check_test.go
@@ -121,7 +121,7 @@
}
var s scanner.Scanner
- s.Init(fset.AddFile(filename, fset.Base(), len(src)), src, nil, scanner.ScanComments)
+ s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
var prev string // position string of last non-comment, non-semicolon token
scanFile:
@@ -189,7 +189,7 @@
// typecheck and collect typechecker errors
var ctxt Context
ctxt.Error = func(err error) { errlist = append(errlist, err) }
- ctxt.Check(fset, files)
+ ctxt.Check(testname, fset, files...)
if *listErrors {
t.Errorf("--- %s: %d errors found:", testname, len(errlist))
@@ -224,8 +224,8 @@
if !testBuiltinsDeclared {
testBuiltinsDeclared = true
// Pkg == nil for Universe objects
- def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}})
- def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}})
+ def(&Func{name: "assert", typ: &Builtin{_Assert, "assert", 1, false, true}})
+ def(&Func{name: "trace", typ: &Builtin{_Trace, "trace", 0, true, true}})
}
// For easy debugging w/o changing the testing code,
diff --git a/go/types/conversions.go b/go/types/conversions.go
index c8433b4..3fa8f5a 100644
--- a/go/types/conversions.go
+++ b/go/types/conversions.go
@@ -32,11 +32,11 @@
if x.mode == constant && isConstType(typ) {
// constant conversion
- typ := underlying(typ).(*Basic)
+ typ := typ.Underlying().(*Basic)
// For now just implement string(x) where x is an integer,
// as a temporary work-around for issue 4982, which is a
// common issue.
- if typ.Kind == String {
+ if typ.kind == String {
switch {
case x.isInteger():
codepoint := int64(-1)
@@ -86,8 +86,8 @@
// "x's type and T have identical underlying types"
V := x.typ
- Vu := underlying(V)
- Tu := underlying(T)
+ Vu := V.Underlying()
+ Tu := T.Underlying()
if IsIdentical(Vu, Tu) {
return true
}
@@ -95,7 +95,7 @@
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if IsIdentical(underlying(V.Base), underlying(T.Base)) {
+ if IsIdentical(V.base.Underlying(), T.base.Underlying()) {
return true
}
}
@@ -136,12 +136,12 @@
func isUintptr(typ Type) bool {
t, ok := typ.(*Basic)
- return ok && t.Kind == Uintptr
+ return ok && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
t, ok := typ.(*Basic)
- return ok && t.Kind == UnsafePointer
+ return ok && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
@@ -151,8 +151,8 @@
func isBytesOrRunes(typ Type) bool {
if s, ok := typ.(*Slice); ok {
- t, ok := underlying(s.Elt).(*Basic)
- return ok && (t.Kind == Byte || t.Kind == Rune)
+ t, ok := s.elt.Underlying().(*Basic)
+ return ok && (t.kind == Byte || t.kind == Rune)
}
return false
}
diff --git a/go/types/errors.go b/go/types/errors.go
index 62ee547..88d0991 100644
--- a/go/types/errors.go
+++ b/go/types/errors.go
@@ -53,14 +53,16 @@
func (check *checker) formatMsg(format string, args []interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
+ case operand:
+ panic("internal error: should always pass *operand")
+ case *operand:
+ args[i] = a.String()
case token.Pos:
args[i] = check.fset.Position(a).String()
case ast.Expr:
args[i] = exprString(a)
case Type:
args[i] = typeString(a)
- case operand:
- panic("internal error: should always pass *operand")
}
}
return fmt.Sprintf(format, args...)
@@ -181,6 +183,8 @@
writeExpr(buf, x.Y)
default:
+ // TODO(gri) Consider just calling x.String(). May cause
+ // infinite recursion if we missed a local type.
fmt.Fprintf(buf, "<expr %T>", x)
}
}
@@ -192,40 +196,44 @@
return buf.String()
}
-func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) {
+func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) {
buf.WriteByte('(')
- for i, par := range params {
- if i > 0 {
- buf.WriteString(", ")
+ if tup != nil {
+ for i, v := range tup.vars {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if v.name != "" {
+ buf.WriteString(v.name)
+ buf.WriteByte(' ')
+ }
+ if isVariadic && i == len(tup.vars)-1 {
+ buf.WriteString("...")
+ }
+ writeType(buf, v.typ)
}
- if par.Name != "" {
- buf.WriteString(par.Name)
- buf.WriteByte(' ')
- }
- if isVariadic && i == len(params)-1 {
- buf.WriteString("...")
- }
- writeType(buf, par.Type)
}
buf.WriteByte(')')
}
func writeSignature(buf *bytes.Buffer, sig *Signature) {
- writeParams(buf, sig.Params, sig.IsVariadic)
- if len(sig.Results) == 0 {
+ writeTuple(buf, sig.params, sig.isVariadic)
+
+ n := sig.results.Len()
+ if n == 0 {
// no result
return
}
buf.WriteByte(' ')
- if len(sig.Results) == 1 && sig.Results[0].Name == "" {
+ if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result
- writeType(buf, sig.Results[0].Type.(Type))
+ writeType(buf, sig.results.vars[0].typ)
return
}
// multiple or named result(s)
- writeParams(buf, sig.Results, false)
+ writeTuple(buf, sig.results, false)
}
func writeType(buf *bytes.Buffer, typ Type) {
@@ -234,19 +242,19 @@
buf.WriteString("<nil>")
case *Basic:
- buf.WriteString(t.Name)
+ buf.WriteString(t.name)
case *Array:
- fmt.Fprintf(buf, "[%d]", t.Len)
- writeType(buf, t.Elt)
+ fmt.Fprintf(buf, "[%d]", t.len)
+ writeType(buf, t.elt)
case *Slice:
buf.WriteString("[]")
- writeType(buf, t.Elt)
+ writeType(buf, t.elt)
case *Struct:
buf.WriteString("struct{")
- for i, f := range t.Fields {
+ for i, f := range t.fields {
if i > 0 {
buf.WriteString("; ")
}
@@ -255,46 +263,47 @@
buf.WriteByte(' ')
}
writeType(buf, f.Type)
- if f.Tag != "" {
- fmt.Fprintf(buf, " %q", f.Tag)
+ if tag := t.Tag(i); tag != "" {
+ fmt.Fprintf(buf, " %q", tag)
}
}
buf.WriteByte('}')
case *Pointer:
buf.WriteByte('*')
- writeType(buf, t.Base)
+ writeType(buf, t.base)
- case *Result:
- writeParams(buf, t.Values, false)
+ case *Tuple:
+ writeTuple(buf, t, false)
case *Signature:
buf.WriteString("func")
writeSignature(buf, t)
- case *builtin:
+ case *Builtin:
fmt.Fprintf(buf, "<type of %s>", t.name)
case *Interface:
buf.WriteString("interface{")
- for i, m := range t.Methods {
+ for i, obj := range t.methods.entries {
if i > 0 {
buf.WriteString("; ")
}
- buf.WriteString(m.Name)
- writeSignature(buf, m.Type)
+ m := obj.(*Func)
+ buf.WriteString(m.name)
+ writeSignature(buf, m.typ.(*Signature))
}
buf.WriteByte('}')
case *Map:
buf.WriteString("map[")
- writeType(buf, t.Key)
+ writeType(buf, t.key)
buf.WriteByte(']')
- writeType(buf, t.Elt)
+ writeType(buf, t.elt)
case *Chan:
var s string
- switch t.Dir {
+ switch t.dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
@@ -303,33 +312,21 @@
s = "chan "
}
buf.WriteString(s)
- writeType(buf, t.Elt)
+ writeType(buf, t.elt)
- case *NamedType:
- s := "<NamedType w/o object>"
- if obj := t.Obj; obj != nil {
- if obj.Pkg != nil && obj.Pkg.Path != "" {
- buf.WriteString(obj.Pkg.Path)
+ case *Named:
+ s := "<Named w/o object>"
+ if obj := t.obj; obj != nil {
+ if obj.pkg != nil && obj.pkg.path != "" {
+ buf.WriteString(obj.pkg.path)
buf.WriteString(".")
}
- s = t.Obj.GetName()
+ s = t.obj.name
}
buf.WriteString(s)
default:
- fmt.Fprintf(buf, "<type %T>", t)
+ // For externally defined implementations of Type.
+ buf.WriteString(t.String())
}
}
-
-func (t *Array) String() string { return typeString(t) }
-func (t *Basic) String() string { return typeString(t) }
-func (t *Chan) String() string { return typeString(t) }
-func (t *Interface) String() string { return typeString(t) }
-func (t *Map) String() string { return typeString(t) }
-func (t *NamedType) String() string { return typeString(t) }
-func (t *Pointer) String() string { return typeString(t) }
-func (t *Result) String() string { return typeString(t) }
-func (t *Signature) String() string { return typeString(t) }
-func (t *Slice) String() string { return typeString(t) }
-func (t *Struct) String() string { return typeString(t) }
-func (t *builtin) String() string { return typeString(t) }
diff --git a/go/types/expr.go b/go/types/expr.go
index 0af67a5..b759599 100644
--- a/go/types/expr.go
+++ b/go/types/expr.go
@@ -93,14 +93,14 @@
// named parameter
for _, name := range field.Names {
par := check.lookup(name).(*Var)
- par.Type = typ
+ par.typ = typ
last = par
copy := *par
params = append(params, ©)
}
} else {
// anonymous parameter
- par := &Var{Type: typ}
+ par := &Var{typ: typ}
last = nil // not accessible inside function
params = append(params, par)
}
@@ -109,12 +109,12 @@
// from T to []T (this is the type used inside the function), but
// keep the params list unchanged (this is the externally visible type).
if isVariadic && last != nil {
- last.Type = &Slice{Elt: last.Type}
+ last.typ = &Slice{elt: last.typ}
}
return
}
-func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
+func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) {
if list == nil {
return
}
@@ -131,34 +131,27 @@
continue
}
for _, name := range f.Names {
- methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig})
+ // TODO(gri) provide correct declaration info
+ obj := &Func{check.pkg, name.Name, sig, nil}
+ if alt := methods.Insert(obj); alt != nil {
+ check.errorf(list.Pos(), "multiple methods named %s", name.Name)
+ }
}
} else {
// embedded interface
- utyp := underlying(typ)
+ utyp := typ.Underlying()
if ityp, ok := utyp.(*Interface); ok {
- methods = append(methods, ityp.Methods...)
+ for _, obj := range ityp.methods.entries {
+ if alt := methods.Insert(obj); alt != nil {
+ check.errorf(list.Pos(), "multiple methods named %s", obj.Name())
+ }
+ }
} else if utyp != Typ[Invalid] {
// if utyp is invalid, don't complain (the root cause was reported before)
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
}
}
}
- // Check for double declarations.
- // The parser inserts methods into an interface-local scope, so local
- // double declarations are reported by the parser already. We need to
- // check again for conflicts due to embedded interfaces. This will lead
- // to a 2nd error message if the double declaration was reported before
- // by the parser.
- // TODO(gri) clean this up a bit
- seen := make(map[string]bool)
- for _, m := range methods {
- if seen[m.Name] {
- check.errorf(list.Pos(), "multiple methods named %s", m.Name)
- return // keep multiple entries, lookup will only return the first entry
- }
- seen[m.Name] = true
- }
return
}
@@ -174,7 +167,7 @@
return ""
}
-func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) {
+func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
if list == nil {
return
}
@@ -182,7 +175,14 @@
var typ Type // current field typ
var tag string // current field tag
add := func(name string, isAnonymous bool) {
- fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous})
+ // TODO(gri): rethink this - at the moment we allocate only a prefix
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+ fields = append(fields, &Field{check.pkg, name, typ, isAnonymous})
}
for _, f := range list.List {
@@ -195,11 +195,11 @@
}
} else {
// anonymous field
- switch t := deref(typ).(type) {
+ switch t := typ.Deref().(type) {
case *Basic:
- add(t.Name, true)
- case *NamedType:
- add(t.Obj.GetName(), true)
+ add(t.name, true)
+ case *Named:
+ add(t.obj.name, true)
default:
if typ != Typ[Invalid] {
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
@@ -245,21 +245,21 @@
check.invalidOp(x.pos(), "cannot take address of %s", x)
goto Error
}
- x.typ = &Pointer{Base: x.typ}
+ x.typ = &Pointer{base: x.typ}
return
case token.ARROW:
- typ, ok := underlying(x.typ).(*Chan)
+ typ, ok := x.typ.Underlying().(*Chan)
if !ok {
check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
goto Error
}
- if typ.Dir&ast.RECV == 0 {
+ if typ.dir&ast.RECV == 0 {
check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
goto Error
}
x.mode = valueok
- x.typ = typ.Elt
+ x.typ = typ.elt
return
}
@@ -268,7 +268,7 @@
}
if x.mode == constant {
- typ := underlying(x.typ).(*Basic)
+ typ := x.typ.Underlying().(*Basic)
size := -1
if isUnsigned(typ) {
size = int(check.ctxt.sizeof(typ))
@@ -417,7 +417,7 @@
return
}
- if !isRepresentableConst(x.val, check.ctxt, typ.Kind) {
+ if !isRepresentableConst(x.val, check.ctxt, typ.kind) {
var msg string
if isNumeric(x.typ) && isNumeric(typ) {
msg = "%s overflows (or cannot be accurately represented as) %s"
@@ -517,7 +517,7 @@
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
- old.typ = underlying(typ).(*Basic)
+ old.typ = typ.Underlying().(*Basic)
check.untyped[x] = old
return
}
@@ -551,8 +551,8 @@
if isUntyped(target) {
// both x and target are untyped
- xkind := x.typ.(*Basic).Kind
- tkind := target.(*Basic).Kind
+ xkind := x.typ.(*Basic).kind
+ tkind := target.(*Basic).kind
if isNumeric(x.typ) && isNumeric(target) {
if xkind < tkind {
x.typ = target
@@ -565,7 +565,7 @@
}
// typed target
- switch t := underlying(target).(type) {
+ switch t := target.Underlying().(type) {
case nil:
// We may reach here due to previous type errors.
// Be conservative and don't crash.
@@ -577,7 +577,7 @@
return // error already reported
}
case *Interface:
- if !x.isNil() && len(t.Methods) > 0 /* empty interfaces are ok */ {
+ if !x.isNil() && !t.IsEmpty() /* empty interfaces are ok */ {
goto Error
}
// Update operand types to the default type rather then
@@ -590,7 +590,7 @@
target = Typ[UntypedNil]
} else {
// cannot assign untyped values to non-empty interfaces
- if len(t.Methods) > 0 {
+ if !t.IsEmpty() {
goto Error
}
target = defaultType(x.typ)
@@ -814,7 +814,7 @@
}
if x.mode == constant && y.mode == constant {
- typ := underlying(x.typ).(*Basic)
+ typ := x.typ.Underlying().(*Basic)
// force integer division of integer operands
if op == token.QUO && isInteger(typ) {
op = token.QUO_ASSIGN
@@ -871,7 +871,7 @@
// For details, see comment in go/parser/parser.go, method parseElement.
func (check *checker) compositeLitKey(key ast.Expr) {
if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
- if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
+ if obj := check.pkg.scope.Lookup(ident.Name); obj != nil {
check.register(ident, obj)
} else if obj := Universe.Lookup(ident.Name); obj != nil {
check.register(ident, obj)
@@ -937,13 +937,22 @@
func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
// determine parameter
var par *Var
- n := len(sig.Params)
+ n := sig.params.Len()
if i < n {
- par = sig.Params[i]
- } else if sig.IsVariadic {
- par = sig.Params[n-1]
+ par = sig.params.vars[i]
+ } else if sig.isVariadic {
+ par = sig.params.vars[n-1]
} else {
- check.errorf(arg.Pos(), "too many arguments")
+ var pos token.Pos
+ switch {
+ case arg != nil:
+ pos = arg.Pos()
+ case x != nil:
+ pos = x.pos()
+ default:
+ // TODO(gri) what position to use?
+ }
+ check.errorf(pos, "too many arguments")
return
}
@@ -951,7 +960,7 @@
var z operand
z.mode = variable
z.expr = nil // TODO(gri) can we do better here? (for good error messages)
- z.typ = par.Type
+ z.typ = par.typ
if arg != nil {
check.expr(x, arg, z.typ, -1)
@@ -969,7 +978,7 @@
// spec: "If the final argument is assignable to a slice type []T,
// it may be passed unchanged as the value for a ...T parameter if
// the argument is followed by ..."
- z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
+ z.typ = &Slice{elt: z.typ} // change final parameter type to []T
}
if !check.assignment(x, z.typ) && x.mode != invalid {
@@ -977,8 +986,6 @@
}
}
-var emptyResult Result
-
func (check *checker) callExpr(x *operand) {
// convert x into a user-friendly set of values
var typ Type
@@ -987,7 +994,7 @@
case invalid:
return // nothing to do
case novalue:
- typ = &emptyResult
+ typ = (*Tuple)(nil)
case constant:
typ = x.typ
val = x.val
@@ -1050,7 +1057,7 @@
check.errorf(e.Pos(), "use of package %s not in selector", obj.Name)
goto Error
case *Const:
- if obj.Type == Typ[Invalid] {
+ if obj.typ == Typ[Invalid] {
goto Error
}
x.mode = constant
@@ -1061,11 +1068,11 @@
}
x.val = exact.MakeInt64(int64(iota))
} else {
- x.val = obj.Val // may be nil if we don't know the constant value
+ x.val = obj.val // may be nil if we don't know the constant value
}
case *TypeName:
x.mode = typexpr
- if !cycleOk && underlying(obj.Type) == nil {
+ if !cycleOk && obj.typ.Underlying() == nil {
check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
x.expr = e
x.typ = Typ[Invalid]
@@ -1078,7 +1085,7 @@
default:
unreachable()
}
- x.typ = obj.GetType()
+ x.typ = obj.Type()
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
@@ -1115,7 +1122,7 @@
// We have an "open" [...]T array type.
// Create a new ArrayType with unknown length (-1)
// and finish setting it up after analyzing the literal.
- typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)}
+ typ = &Array{len: -1, elt: check.typ(atyp.Elt, cycleOk)}
openArray = true
}
}
@@ -1128,12 +1135,12 @@
goto Error
}
- switch utyp := underlying(deref(typ)).(type) {
+ switch utyp := typ.Deref().Underlying().(type) {
case *Struct:
if len(e.Elts) == 0 {
break
}
- fields := utyp.Fields
+ fields := utyp.fields
if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
// all elements must have keys
visited := make([]bool, len(fields))
@@ -1148,7 +1155,7 @@
check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
continue
}
- i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name})
+ i := utyp.fieldIndex(check.pkg, key.Name)
if i < 0 {
check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
continue
@@ -1196,14 +1203,14 @@
}
case *Array:
- n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota)
+ n := check.indexedElts(e.Elts, utyp.elt, utyp.len, iota)
// if we have an "open" [...]T array, set the length now that we know it
if openArray {
- utyp.Len = n
+ utyp.len = n
}
case *Slice:
- check.indexedElts(e.Elts, utyp.Elt, -1, iota)
+ check.indexedElts(e.Elts, utyp.elt, -1, iota)
case *Map:
visited := make(map[interface{}]bool, len(e.Elts))
@@ -1215,9 +1222,9 @@
}
check.compositeLitKey(kv.Key)
check.expr(x, kv.Key, nil, iota)
- if !check.assignment(x, utyp.Key) {
+ if !check.assignment(x, utyp.key) {
if x.mode != invalid {
- check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key)
+ check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key)
}
continue
}
@@ -1228,10 +1235,10 @@
}
visited[x.val] = true
}
- check.expr(x, kv.Value, utyp.Elt, iota)
- if !check.assignment(x, utyp.Elt) {
+ check.expr(x, kv.Value, utyp.elt, iota)
+ if !check.assignment(x, utyp.elt) {
if x.mode != invalid {
- check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt)
+ check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elt)
}
continue
}
@@ -1256,11 +1263,11 @@
// selector expressions.
if ident, ok := e.X.(*ast.Ident); ok {
if pkg, ok := check.lookup(ident).(*Package); ok {
- exp := pkg.Scope.Lookup(sel)
+ exp := pkg.scope.Lookup(sel)
if exp == nil {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
goto Error
- } else if !ast.IsExported(exp.GetName()) {
+ } else if !ast.IsExported(exp.Name()) {
// gcimported package scopes contain non-exported
// objects such as types used in partially exported
// objects - do not accept them
@@ -1275,17 +1282,17 @@
case *Const:
assert(exp.Val != nil)
x.mode = constant
- x.typ = exp.Type
- x.val = exp.Val
+ x.typ = exp.typ
+ x.val = exp.val
case *TypeName:
x.mode = typexpr
- x.typ = exp.Type
+ x.typ = exp.typ
case *Var:
x.mode = variable
- x.typ = exp.Type
+ x.typ = exp.typ
case *Func:
x.mode = value
- x.typ = exp.Type
+ x.typ = exp.typ
default:
unreachable()
}
@@ -1298,7 +1305,7 @@
if x.mode == invalid {
goto Error
}
- res := lookupField(x.typ, QualifiedName{check.pkg, sel})
+ res := lookupField(x.typ, check.pkg, sel)
if res.mode == invalid {
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
goto Error
@@ -1314,11 +1321,15 @@
// argument of the method expression's function type
// TODO(gri) at the moment, method sets don't correctly track
// pointer vs non-pointer receivers => typechecker is too lenient
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
x.mode = value
x.typ = &Signature{
- Params: append([]*Var{{Type: x.typ}}, sig.Params...),
- Results: sig.Results,
- IsVariadic: sig.IsVariadic,
+ params: NewTuple(append([]*Var{{typ: x.typ}}, params...)...),
+ results: sig.results,
+ isVariadic: sig.isVariadic,
}
} else {
// regular selector
@@ -1334,7 +1345,7 @@
valid := false
length := int64(-1) // valid if >= 0
- switch typ := underlying(x.typ).(type) {
+ switch typ := x.typ.Underlying().(type) {
case *Basic:
if isString(typ) {
valid = true
@@ -1350,36 +1361,36 @@
case *Array:
valid = true
- length = typ.Len
+ length = typ.len
if x.mode != variable {
x.mode = value
}
- x.typ = typ.Elt
+ x.typ = typ.elt
case *Pointer:
- if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
valid = true
- length = typ.Len
+ length = typ.len
x.mode = variable
- x.typ = typ.Elt
+ x.typ = typ.elt
}
case *Slice:
valid = true
x.mode = variable
- x.typ = typ.Elt
+ x.typ = typ.elt
case *Map:
var key operand
check.expr(&key, e.Index, nil, iota)
- if !check.assignment(&key, typ.Key) {
+ if !check.assignment(&key, typ.key) {
if key.mode != invalid {
- check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.Key)
+ check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key)
}
goto Error
}
x.mode = valueok
- x.typ = typ.Elt
+ x.typ = typ.elt
x.expr = e
return
}
@@ -1405,7 +1416,7 @@
valid := false
length := int64(-1) // valid if >= 0
- switch typ := underlying(x.typ).(type) {
+ switch typ := x.typ.Underlying().(type) {
case *Basic:
if isString(typ) {
valid = true
@@ -1419,26 +1430,26 @@
x.mode = value
// x.typ doesn't change, but if it is an untyped
// string it becomes string (see also issue 4913).
- if typ.Kind == UntypedString {
+ if typ.kind == UntypedString {
x.typ = Typ[String]
}
}
case *Array:
valid = true
- length = typ.Len + 1 // +1 for slice
+ length = typ.len + 1 // +1 for slice
if x.mode != variable {
check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
goto Error
}
- x.typ = &Slice{Elt: typ.Elt}
+ x.typ = &Slice{elt: typ.elt}
case *Pointer:
- if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
valid = true
- length = typ.Len + 1 // +1 for slice
+ length = typ.len + 1 // +1 for slice
x.mode = variable
- x.typ = &Slice{Elt: typ.Elt}
+ x.typ = &Slice{elt: typ.elt}
}
case *Slice:
@@ -1479,7 +1490,7 @@
goto Error
}
var T *Interface
- if T, _ = underlying(x.typ).(*Interface); T == nil {
+ if T, _ = x.typ.Underlying().(*Interface); T == nil {
check.invalidOp(x.pos(), "%s is not an interface", x)
goto Error
}
@@ -1499,7 +1510,7 @@
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
- check.errorf(e.Type.Pos(), msg, x, typ, method.Name)
+ check.errorf(e.Type.Pos(), msg, x, typ, method.name)
// ok to continue
}
x.mode = valueok
@@ -1512,7 +1523,7 @@
goto Error
} else if x.mode == typexpr {
check.conversion(x, e, x.typ, iota)
- } else if sig, ok := underlying(x.typ).(*Signature); ok {
+ } else if sig, ok := x.typ.Underlying().(*Signature); ok {
// check parameters
// If we have a trailing ... at the end of the parameter
@@ -1520,7 +1531,7 @@
// []T of a variadic function parameter x ...T.
passSlice := false
if e.Ellipsis.IsValid() {
- if sig.IsVariadic {
+ if sig.isVariadic {
passSlice = true
} else {
check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
@@ -1543,13 +1554,14 @@
if x.mode == invalid {
goto Error // TODO(gri): we can do better
}
- if t, _ := x.typ.(*Result); t != nil {
+ if t, ok := x.typ.(*Tuple); ok {
// multiple result values
- n = len(t.Values)
- for i, obj := range t.Values {
+ n = t.Len()
+ for i := 0; i < n; i++ {
+ obj := t.At(i)
x.mode = value
x.expr = nil // TODO(gri) can we do better here? (for good error messages)
- x.typ = obj.Type
+ x.typ = obj.typ
check.argument(sig, i, nil, x, passSlice && i+1 == n)
}
} else {
@@ -1567,29 +1579,29 @@
}
// determine if we have enough arguments
- if sig.IsVariadic {
+ if sig.isVariadic {
// a variadic function accepts an "empty"
// last argument: count one extra
n++
}
- if n < len(sig.Params) {
+ if n < sig.params.Len() {
check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
// ok to continue
}
// determine result
- switch len(sig.Results) {
+ switch sig.results.Len() {
case 0:
x.mode = novalue
case 1:
x.mode = value
- x.typ = sig.Results[0].Type
+ x.typ = sig.results.vars[0].typ
default:
x.mode = value
- x.typ = &Result{Values: sig.Results}
+ x.typ = sig.results
}
- } else if bin, ok := x.typ.(*builtin); ok {
+ } else if bin, ok := x.typ.(*Builtin); ok {
check.builtin(x, e, bin, iota)
} else {
@@ -1603,11 +1615,11 @@
case invalid:
goto Error
case typexpr:
- x.typ = &Pointer{Base: x.typ}
+ x.typ = &Pointer{base: x.typ}
default:
- if typ, ok := underlying(x.typ).(*Pointer); ok {
+ if typ, ok := x.typ.Underlying().(*Pointer); ok {
x.mode = variable
- x.typ = typ.Base
+ x.typ = typ.base
} else {
check.invalidOp(x.pos(), "cannot indirect %s", x)
goto Error
@@ -1656,33 +1668,34 @@
check.errorf(x.pos(), "invalid array length %s", x)
goto Error
}
- x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)}
+ x.typ = &Array{len: n, elt: check.typ(e.Elt, cycleOk)}
} else {
- x.typ = &Slice{Elt: check.typ(e.Elt, true)}
+ x.typ = &Slice{elt: check.typ(e.Elt, true)}
}
x.mode = typexpr
case *ast.StructType:
x.mode = typexpr
- x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
+ fields, tags := check.collectFields(e.Fields, cycleOk)
+ x.typ = &Struct{fields: fields, tags: tags}
case *ast.FuncType:
params, isVariadic := check.collectParams(e.Params, true)
results, _ := check.collectParams(e.Results, false)
x.mode = typexpr
- x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
+ x.typ = &Signature{recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
case *ast.InterfaceType:
x.mode = typexpr
- x.typ = &Interface{Methods: check.collectMethods(e.Methods)}
+ x.typ = &Interface{methods: check.collectMethods(e.Methods)}
case *ast.MapType:
x.mode = typexpr
- x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)}
+ x.typ = &Map{key: check.typ(e.Key, true), elt: check.typ(e.Value, true)}
case *ast.ChanType:
x.mode = typexpr
- x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)}
+ x.typ = &Chan{dir: e.Dir, elt: check.typ(e.Value, true)}
default:
if debug {
diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go
index ed9b856..d417263 100644
--- a/go/types/gcimporter.go
+++ b/go/types/gcimporter.go
@@ -124,7 +124,7 @@
}
// no need to re-import if the package was imported completely before
- if pkg = imports[id]; pkg != nil && pkg.Complete {
+ if pkg = imports[id]; pkg != nil && pkg.complete {
return
}
@@ -177,7 +177,7 @@
if false {
// check consistency of imports map
for _, pkg := range imports {
- if pkg.Name == "" {
+ if pkg.name == "" {
fmt.Printf("no package name for %s\n", pkg.Path)
}
}
@@ -201,45 +201,45 @@
func declConst(pkg *Package, name string) *Const {
// the constant may have been imported before - if it exists
// already in the respective scope, return that constant
- scope := pkg.Scope
+ scope := pkg.scope
if obj := scope.Lookup(name); obj != nil {
return obj.(*Const)
}
// otherwise create a new constant and insert it into the scope
- obj := &Const{Pkg: pkg, Name: name}
+ obj := &Const{pkg: pkg, name: name}
scope.Insert(obj)
return obj
}
func declTypeName(pkg *Package, name string) *TypeName {
- scope := pkg.Scope
+ scope := pkg.scope
if obj := scope.Lookup(name); obj != nil {
return obj.(*TypeName)
}
- obj := &TypeName{Pkg: pkg, Name: name}
+ obj := &TypeName{pkg: pkg, name: name}
// a named type may be referred to before the underlying type
// is known - set it up
- obj.Type = &NamedType{Obj: obj}
+ obj.typ = &Named{obj: obj}
scope.Insert(obj)
return obj
}
func declVar(pkg *Package, name string) *Var {
- scope := pkg.Scope
+ scope := pkg.scope
if obj := scope.Lookup(name); obj != nil {
return obj.(*Var)
}
- obj := &Var{Pkg: pkg, Name: name}
+ obj := &Var{pkg: pkg, name: name}
scope.Insert(obj)
return obj
}
func declFunc(pkg *Package, name string) *Func {
- scope := pkg.Scope
+ scope := pkg.scope
if obj := scope.Lookup(name); obj != nil {
return obj.(*Func)
}
- obj := &Func{Pkg: pkg, Name: name}
+ obj := &Func{pkg: pkg, name: name}
scope.Insert(obj)
return obj
}
@@ -360,7 +360,7 @@
}
pkg := p.imports[id]
if pkg == nil && name != "" {
- pkg = &Package{Name: name, Path: id, Scope: new(Scope)}
+ pkg = &Package{name: name, path: id, scope: new(Scope)}
p.imports[id] = pkg
}
return pkg
@@ -387,7 +387,7 @@
id := p.expect(scanner.Ident)
obj := Universe.Lookup(id)
if obj, ok := obj.(*TypeName); ok {
- return obj.Type
+ return obj.typ
}
p.errorf("not a basic type: %s", id)
return nil
@@ -404,7 +404,7 @@
if err != nil {
p.error(err)
}
- return &Array{Len: n, Elt: elt}
+ return &Array{len: n, elt: elt}
}
// MapType = "map" "[" Type "]" Type .
@@ -415,7 +415,7 @@
key := p.parseType()
p.expect(']')
elt := p.parseType()
- return &Map{Key: key, Elt: elt}
+ return &Map{key: key, elt: elt}
}
// Name = identifier | "?" | QualifiedName .
@@ -447,7 +447,7 @@
// doesn't exist yet, create a fake package instead
pkg = p.getPkg(id, "")
if pkg == nil {
- pkg = &Package{Path: id}
+ pkg = &Package{path: id}
}
}
default:
@@ -458,23 +458,27 @@
// Field = Name Type [ string_lit ] .
//
-func (p *gcParser) parseField() *Field {
+func (p *gcParser) parseField() (*Field, string) {
var f Field
f.Pkg, f.Name = p.parseName(true)
f.Type = p.parseType()
+ tag := ""
if p.tok == scanner.String {
- f.Tag = p.expect(scanner.String)
+ tag = p.expect(scanner.String)
}
if f.Name == "" {
// anonymous field - typ must be T or *T and T must be a type name
- if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil {
- f.Name = typ.Obj.GetName()
- f.IsAnonymous = true
- } else {
+ switch typ := f.Type.Deref().(type) {
+ case *Basic: // basic types are named types
+ f.Name = typ.name
+ case *Named:
+ f.Name = typ.obj.name
+ default:
p.errorf("anonymous field expected")
}
+ f.IsAnonymous = true
}
- return &f
+ return &f, tag
}
// StructType = "struct" "{" [ FieldList ] "}" .
@@ -482,6 +486,7 @@
//
func (p *gcParser) parseStructType() Type {
var fields []*Field
+ var tags []string
p.expectKeyword("struct")
p.expect('{')
@@ -489,11 +494,19 @@
if len(fields) > 0 {
p.expect(';')
}
- fields = append(fields, p.parseField())
+ fld, tag := p.parseField()
+ // TODO(gri) same code in collectFields (expr.go) - factor?
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+ fields = append(fields, fld)
}
p.expect('}')
- return &Struct{Fields: fields}
+ return &Struct{fields: fields, tags: tags}
}
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
@@ -512,7 +525,7 @@
if p.tok == scanner.String {
p.next()
}
- par = &Var{Name: name, Type: typ} // Pkg == nil
+ par = &Var{name: name, typ: typ} // Pkg == nil
return
}
@@ -555,7 +568,7 @@
}
}
- return &Signature{Params: params, Results: results, IsVariadic: isVariadic}
+ return &Signature{params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
}
// InterfaceType = "interface" "{" [ MethodList ] "}" .
@@ -567,21 +580,23 @@
// visible in the export data.
//
func (p *gcParser) parseInterfaceType() Type {
- var methods []*Method
+ var methods ObjSet
p.expectKeyword("interface")
p.expect('{')
- for p.tok != '}' {
- if len(methods) > 0 {
+ for i := 0; p.tok != '}'; i++ {
+ if i > 0 {
p.expect(';')
}
pkg, name := p.parseName(true)
- typ := p.parseSignature()
- methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
+ sig := p.parseSignature()
+ if alt := methods.Insert(&Func{pkg, name, sig, nil}); alt != nil {
+ p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
+ }
}
p.expect('}')
- return &Interface{Methods: methods}
+ return &Interface{methods: methods}
}
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
@@ -600,7 +615,7 @@
dir = ast.RECV
}
elt := p.parseType()
- return &Chan{Dir: dir, Elt: elt}
+ return &Chan{dir: dir, elt: elt}
}
// Type =
@@ -636,19 +651,19 @@
case '@':
// TypeName
pkg, name := p.parseExportedName()
- return declTypeName(pkg, name).Type
+ return declTypeName(pkg, name).typ
case '[':
p.next() // look ahead
if p.tok == ']' {
// SliceType
p.next()
- return &Slice{Elt: p.parseType()}
+ return &Slice{elt: p.parseType()}
}
return p.parseArrayType()
case '*':
// PointerType
p.next()
- return &Pointer{Base: p.parseType()}
+ return &Pointer{base: p.parseType()}
case '<':
return p.parseChanType()
case '(':
@@ -736,7 +751,7 @@
obj := declConst(pkg, name)
var x operand
if p.tok != '=' {
- obj.Type = p.parseType()
+ obj.typ = p.parseType()
}
p.expect('=')
switch p.tok {
@@ -787,11 +802,11 @@
default:
p.errorf("expected literal got %s", scanner.TokenString(p.tok))
}
- if obj.Type == nil {
- obj.Type = x.typ
+ if obj.typ == nil {
+ obj.typ = x.typ
}
assert(x.val != nil)
- obj.Val = x.val
+ obj.val = x.val
}
// TypeDecl = "type" ExportedName Type .
@@ -808,8 +823,8 @@
// a given type declaration.
typ := p.parseType()
- if name := obj.Type.(*NamedType); name.Underlying == nil {
- name.Underlying = typ
+ if name := obj.typ.(*Named); name.underlying == nil {
+ name.underlying = typ
}
}
@@ -819,7 +834,7 @@
p.expectKeyword("var")
pkg, name := p.parseExportedName()
obj := declVar(pkg, name)
- obj.Type = p.parseType()
+ obj.typ = p.parseType()
}
// Func = Signature [ Body ] .
@@ -851,26 +866,20 @@
p.expect(')')
// determine receiver base type object
- typ := recv.Type
+ typ := recv.typ
if ptr, ok := typ.(*Pointer); ok {
- typ = ptr.Base
+ typ = ptr.base
}
- base := typ.(*NamedType)
+ base := typ.(*Named)
// parse method name, signature, and possibly inlined body
pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package.
sig := p.parseFunc()
- sig.Recv = recv
+ sig.recv = recv
// add method to type unless type was imported before
// and method exists already
- // TODO(gri) investigate if this can be avoided
- for _, m := range base.Methods {
- if m.Name == name {
- return // method was added before
- }
- }
- base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig})
+ base.methods.Insert(&Func{pkg, name, sig, nil})
}
// FuncDecl = "func" ExportedName Func .
@@ -879,7 +888,7 @@
// "func" already consumed
pkg, name := p.parseExportedName()
typ := p.parseFunc()
- declFunc(pkg, name).Type = typ
+ declFunc(pkg, name).typ = typ
}
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
@@ -939,7 +948,7 @@
}
// package was imported completely and without errors
- pkg.Complete = true
+ pkg.complete = true
return pkg
}
diff --git a/go/types/gcimporter_test.go b/go/types/gcimporter_test.go
index b793eb4..bc0378a 100644
--- a/go/types/gcimporter_test.go
+++ b/go/types/gcimporter_test.go
@@ -146,7 +146,7 @@
continue
}
- obj := pkg.Scope.Lookup(objName)
+ obj := pkg.scope.Lookup(objName)
// TODO(gri) should define an accessor on Object
var kind ast.ObjKind
@@ -154,16 +154,16 @@
switch obj := obj.(type) {
case *Const:
kind = ast.Con
- typ = obj.Type
+ typ = obj.typ
case *TypeName:
kind = ast.Typ
- typ = obj.Type
+ typ = obj.typ
case *Var:
kind = ast.Var
- typ = obj.Type
+ typ = obj.typ
case *Func:
kind = ast.Fun
- typ = obj.Type
+ typ = obj.typ
default:
unreachable()
}
@@ -172,7 +172,7 @@
t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind)
}
- str := typeString(underlying(typ))
+ str := typeString(typ.Underlying())
if str != test.typ {
t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ)
}
diff --git a/go/types/objects.go b/go/types/objects.go
index e91f818..96cb8c1 100644
--- a/go/types/objects.go
+++ b/go/types/objects.go
@@ -16,143 +16,157 @@
// All objects implement the Object interface.
//
type Object interface {
- GetPkg() *Package
- GetName() string
- GetType() Type
- GetPos() token.Pos
-
- anObject()
+ Pkg() *Package // nil for objects in the Universe scope
+ Scope() *Scope
+ Name() string
+ Type() Type
+ Pos() token.Pos
+ // TODO(gri) provide String method!
}
// A Package represents the contents (objects) of a Go package.
type Package struct {
- Name string
- Path string // import path, "" for current (non-imported) package
- Scope *Scope // package-level scope
- Imports map[string]*Package // map of import paths to imported packages
- Complete bool // if set, this package was imported completely
+ name string
+ path string // import path, "" for current (non-imported) package
+ scope *Scope // package-level scope
+ imports map[string]*Package // map of import paths to imported packages
+ complete bool // if set, this package was imported completely
spec *ast.ImportSpec
}
+func NewPackage(path, name string) *Package {
+ return &Package{name: name, path: path, complete: true}
+}
+
+func (obj *Package) Pkg() *Package { return obj }
+func (obj *Package) Scope() *Scope { return obj.scope }
+func (obj *Package) Name() string { return obj.name }
+func (obj *Package) Type() Type { return Typ[Invalid] }
+func (obj *Package) Pos() token.Pos {
+ if obj.spec == nil {
+ return token.NoPos
+ }
+ return obj.spec.Pos()
+}
+func (obj *Package) Path() string { return obj.path }
+func (obj *Package) Imports() map[string]*Package { return obj.imports }
+func (obj *Package) Complete() bool { return obj.complete }
+
// A Const represents a declared constant.
type Const struct {
- Pkg *Package
- Name string
- Type Type
- Val exact.Value
+ pkg *Package
+ name string
+ typ Type
+ val exact.Value
visited bool // for initialization cycle detection
spec *ast.ValueSpec
}
-// A TypeName represents a declared type.
-type TypeName struct {
- Pkg *Package
- Name string
- Type Type // *NamedType or *Basic
-
- spec *ast.TypeSpec
-}
-
-// A Variable represents a declared variable (including function parameters and results).
-type Var struct {
- Pkg *Package // nil for parameters
- Name string
- Type Type
-
- visited bool // for initialization cycle detection
- decl interface{}
-}
-
-// A Func represents a declared function.
-type Func struct {
- Pkg *Package
- Name string
- Type Type // *Signature or *Builtin
-
- decl *ast.FuncDecl
-}
-
-func (obj *Package) GetPkg() *Package { return obj }
-func (obj *Const) GetPkg() *Package { return obj.Pkg }
-func (obj *TypeName) GetPkg() *Package { return obj.Pkg }
-func (obj *Var) GetPkg() *Package { return obj.Pkg }
-func (obj *Func) GetPkg() *Package { return obj.Pkg }
-
-func (obj *Package) GetName() string { return obj.Name }
-func (obj *Const) GetName() string { return obj.Name }
-func (obj *TypeName) GetName() string { return obj.Name }
-func (obj *Var) GetName() string { return obj.Name }
-func (obj *Func) GetName() string { return obj.Name }
-
-func (obj *Package) GetType() Type { return Typ[Invalid] }
-func (obj *Const) GetType() Type { return obj.Type }
-func (obj *TypeName) GetType() Type { return obj.Type }
-func (obj *Var) GetType() Type { return obj.Type }
-func (obj *Func) GetType() Type { return obj.Type }
-
-func (obj *Package) GetPos() token.Pos {
- if obj.spec == nil {
- return token.NoPos
- }
- return obj.spec.Pos()
-}
-
-func (obj *Const) GetPos() token.Pos {
+func (obj *Const) Pkg() *Package { return obj.pkg }
+func (obj *Const) Scope() *Scope { panic("unimplemented") }
+func (obj *Const) Name() string { return obj.name }
+func (obj *Const) Type() Type { return obj.typ }
+func (obj *Const) Pos() token.Pos {
if obj.spec == nil {
return token.NoPos
}
for _, n := range obj.spec.Names {
- if n.Name == obj.Name {
+ if n.Name == obj.name {
return n.Pos()
}
}
return token.NoPos
}
-func (obj *TypeName) GetPos() token.Pos {
+func (obj *Const) Val() exact.Value { return obj.val }
+
+// A TypeName represents a declared type.
+type TypeName struct {
+ pkg *Package
+ name string
+ typ Type // *Named or *Basic
+
+ spec *ast.TypeSpec
+}
+
+func NewTypeName(pkg *Package, name string, typ Type) *TypeName {
+ return &TypeName{pkg, name, typ, nil}
+}
+
+func (obj *TypeName) Pkg() *Package { return obj.pkg }
+func (obj *TypeName) Scope() *Scope { panic("unimplemented") }
+func (obj *TypeName) Name() string { return obj.name }
+func (obj *TypeName) Type() Type { return obj.typ }
+func (obj *TypeName) Pos() token.Pos {
if obj.spec == nil {
return token.NoPos
}
return obj.spec.Pos()
}
-func (obj *Var) GetPos() token.Pos {
+// A Variable represents a declared variable (including function parameters and results).
+type Var struct {
+ pkg *Package // nil for parameters
+ name string
+ typ Type
+
+ visited bool // for initialization cycle detection
+ decl interface{}
+}
+
+func NewVar(pkg *Package, name string, typ Type) *Var {
+ return &Var{pkg, name, typ, false, nil}
+}
+
+func (obj *Var) Pkg() *Package { return obj.pkg }
+func (obj *Var) Scope() *Scope { panic("unimplemented") }
+func (obj *Var) Name() string { return obj.name }
+func (obj *Var) Type() Type { return obj.typ }
+func (obj *Var) Pos() token.Pos {
switch d := obj.decl.(type) {
case *ast.Field:
for _, n := range d.Names {
- if n.Name == obj.Name {
+ if n.Name == obj.name {
return n.Pos()
}
}
case *ast.ValueSpec:
for _, n := range d.Names {
- if n.Name == obj.Name {
+ if n.Name == obj.name {
return n.Pos()
}
}
case *ast.AssignStmt:
for _, x := range d.Lhs {
- if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name {
+ if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.name {
return ident.Pos()
}
}
}
return token.NoPos
}
-func (obj *Func) GetPos() token.Pos {
+
+// A Func represents a declared function.
+type Func struct {
+ pkg *Package
+ name string
+ typ Type // *Signature or *Builtin
+
+ decl *ast.FuncDecl
+}
+
+func (obj *Func) Pkg() *Package { return obj.pkg }
+func (obj *Func) Scope() *Scope { panic("unimplemented") }
+func (obj *Func) Name() string { return obj.name }
+func (obj *Func) Type() Type { return obj.typ }
+func (obj *Func) Pos() token.Pos {
if obj.decl != nil && obj.decl.Name != nil {
return obj.decl.Name.Pos()
}
return token.NoPos
}
-func (*Package) anObject() {}
-func (*Const) anObject() {}
-func (*TypeName) anObject() {}
-func (*Var) anObject() {}
-func (*Func) anObject() {}
-
// newObj returns a new Object for a given *ast.Object.
// It does not canonicalize them (it always returns a new one).
// For canonicalization, see check.lookup.
@@ -171,9 +185,9 @@
unreachable()
case ast.Con:
iota := astObj.Data.(int)
- return &Const{Pkg: pkg, Name: name, Type: typ, Val: exact.MakeInt64(int64(iota)), spec: astObj.Decl.(*ast.ValueSpec)}
+ return &Const{pkg: pkg, name: name, typ: typ, val: exact.MakeInt64(int64(iota)), spec: astObj.Decl.(*ast.ValueSpec)}
case ast.Typ:
- return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)}
+ return &TypeName{pkg: pkg, name: name, typ: typ, spec: astObj.Decl.(*ast.TypeSpec)}
case ast.Var:
switch astObj.Decl.(type) {
case *ast.Field: // function parameters
@@ -182,9 +196,9 @@
default:
unreachable() // everything else is not ok
}
- return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl}
+ return &Var{pkg: pkg, name: name, typ: typ, decl: astObj.Decl}
case ast.Fun:
- return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)}
+ return &Func{pkg: pkg, name: name, typ: typ, decl: astObj.Decl.(*ast.FuncDecl)}
case ast.Lbl:
unreachable() // for now
}
diff --git a/go/types/objset.go b/go/types/objset.go
new file mode 100644
index 0000000..bd3ea01
--- /dev/null
+++ b/go/types/objset.go
@@ -0,0 +1,64 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+)
+
+// An ObjSet maintains a set of objects identified by
+// their name and package that declares them.
+//
+type ObjSet struct {
+ entries []Object // set entries in insertion order
+}
+
+// Lookup returns the object with the given package and name
+// if it is found in ObjSet s, otherwise it returns nil.
+//
+func (s *ObjSet) Lookup(pkg *Package, name string) Object {
+ 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) {
+ return obj
+ }
+ }
+ return nil
+}
+
+// Insert attempts to insert an object obj into ObjSet s.
+// If s already contains an object from the same package
+// with the same name, Insert leaves s unchanged and returns
+// that object. Otherwise it inserts obj and returns nil.
+//
+func (s *ObjSet) Insert(obj Object) Object {
+ pkg := obj.Pkg()
+ name := obj.Name()
+ assert(obj.Type() != nil)
+ if alt := s.Lookup(pkg, name); alt != nil {
+ return alt
+ }
+ s.entries = append(s.entries, obj)
+ return nil
+}
+
+// Debugging support
+func (s *ObjSet) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "ObjSet %p {", s)
+ if s != nil && len(s.entries) > 0 {
+ fmt.Fprintln(&buf)
+ for _, obj := range s.entries {
+ fmt.Fprintf(&buf, "\t%s.%s\t%T\n", obj.Pkg().path, obj.Name(), obj)
+ }
+ }
+ fmt.Fprintf(&buf, "}\n")
+ return buf.String()
+}
diff --git a/go/types/operand.go b/go/types/operand.go
index ce84cbb..e2e2786 100644
--- a/go/types/operand.go
+++ b/go/types/operand.go
@@ -136,8 +136,8 @@
return true
}
- Vu := underlying(V)
- Tu := underlying(T)
+ Vu := V.Underlying()
+ Tu := T.Underlying()
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
@@ -155,8 +155,8 @@
// x is a bidirectional channel value, T is a channel
// type, x's type V and T have identical element types,
// and at least one of V or T is not a named type
- if Vc, ok := Vu.(*Chan); ok && Vc.Dir == ast.SEND|ast.RECV {
- if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) {
+ if Vc, ok := Vu.(*Chan); ok && Vc.dir == ast.SEND|ast.RECV {
+ if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.elt, Tc.elt) {
return !isNamed(V) || !isNamed(T)
}
}
@@ -166,7 +166,7 @@
if x.isNil() {
switch t := Tu.(type) {
case *Basic:
- if t.Kind == UnsafePointer {
+ if t.kind == UnsafePointer {
return true
}
case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
@@ -182,15 +182,15 @@
switch t := Tu.(type) {
case *Basic:
if x.mode == constant {
- return isRepresentableConst(x.val, ctxt, t.Kind)
+ return isRepresentableConst(x.val, ctxt, t.kind)
}
// The result of a comparison is an untyped boolean,
// but may not be a constant.
if Vb, _ := Vu.(*Basic); Vb != nil {
- return Vb.Kind == UntypedBool && isBoolean(Tu)
+ return Vb.kind == UntypedBool && isBoolean(Tu)
}
case *Interface:
- return x.isNil() || len(t.Methods) == 0
+ return x.isNil() || t.IsEmpty()
case *Pointer, *Signature, *Slice, *Map, *Chan:
return x.isNil()
}
@@ -214,7 +214,7 @@
}
type embeddedType struct {
- typ *NamedType
+ typ *Named
index []int // field index sequence
multiples bool // if set, typ is embedded multiple times at the same level
}
@@ -224,9 +224,9 @@
// the result describes the field mode and type; otherwise the result mode is invalid.
// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
//
-func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) {
+func lookupFieldBreadthFirst(list []embeddedType, pkg *Package, name string) (res lookupResult) {
// visited records the types that have been searched already.
- visited := make(map[*NamedType]bool)
+ visited := make(map[*Named]bool)
// embedded types of the next lower level
var next []embeddedType
@@ -264,20 +264,19 @@
visited[typ] = true
// look for a matching attached method
- for _, m := range typ.Methods {
- if name.IsSame(m.QualifiedName) {
- assert(m.Type != nil)
- if !potentialMatch(e.multiples, value, m.Type) {
- return // name collision
- }
+ if obj := typ.methods.Lookup(pkg, name); obj != nil {
+ m := obj.(*Func)
+ assert(m.typ != nil)
+ if !potentialMatch(e.multiples, value, m.typ) {
+ return // name collision
}
}
- switch t := typ.Underlying.(type) {
+ switch t := typ.underlying.(type) {
case *Struct:
// look for a matching field and collect embedded types
- for i, f := range t.Fields {
- if name.IsSame(f.QualifiedName) {
+ for i, f := range t.fields {
+ if f.isMatch(pkg, name) {
assert(f.Type != nil)
if !potentialMatch(e.multiples, variable, f.Type) {
return // name collision
@@ -300,7 +299,7 @@
if f.IsAnonymous && res.mode == invalid {
// Ignore embedded basic types - only user-defined
// named types can have methods or have struct fields.
- if t, _ := deref(f.Type).(*NamedType); t != nil {
+ if t, _ := f.Type.Deref().(*Named); t != nil {
var index []int
index = append(index, e.index...) // copy e.index
index = append(index, i)
@@ -311,12 +310,11 @@
case *Interface:
// look for a matching method
- for _, m := range t.Methods {
- if name.IsSame(m.QualifiedName) {
- assert(m.Type != nil)
- if !potentialMatch(e.multiples, value, m.Type) {
- return // name collision
- }
+ if obj := t.methods.Lookup(pkg, name); obj != nil {
+ m := obj.(*Func)
+ assert(m.typ != nil)
+ if !potentialMatch(e.multiples, value, m.typ) {
+ return // name collision
}
}
}
@@ -349,7 +347,7 @@
return
}
-func findType(list []embeddedType, typ *NamedType) *embeddedType {
+func findType(list []embeddedType, typ *Named) *embeddedType {
for i := range list {
if p := &list[i]; p.typ == typ {
return p
@@ -358,24 +356,23 @@
return nil
}
-func lookupField(typ Type, name QualifiedName) lookupResult {
- typ = deref(typ)
+func lookupField(typ Type, pkg *Package, name string) lookupResult {
+ typ = typ.Deref()
- if t, ok := typ.(*NamedType); ok {
- for _, m := range t.Methods {
- if name.IsSame(m.QualifiedName) {
- assert(m.Type != nil)
- return lookupResult{value, m.Type, nil}
- }
+ if t, ok := typ.(*Named); ok {
+ if obj := t.methods.Lookup(pkg, name); obj != nil {
+ m := obj.(*Func)
+ assert(m.typ != nil)
+ return lookupResult{value, m.typ, nil}
}
- typ = t.Underlying
+ typ = t.underlying
}
switch t := typ.(type) {
case *Struct:
var next []embeddedType
- for i, f := range t.Fields {
- if name.IsSame(f.QualifiedName) {
+ for i, f := range t.fields {
+ if f.isMatch(pkg, name) {
return lookupResult{variable, f.Type, []int{i}}
}
if f.IsAnonymous {
@@ -384,20 +381,20 @@
// ignore it.
// Ignore embedded basic types - only user-defined
// named types can have methods or have struct fields.
- if t, _ := deref(f.Type).(*NamedType); t != nil {
+ if t, _ := f.Type.Deref().(*Named); t != nil {
next = append(next, embeddedType{t, []int{i}, false})
}
}
}
if len(next) > 0 {
- return lookupFieldBreadthFirst(next, name)
+ return lookupFieldBreadthFirst(next, pkg, name)
}
case *Interface:
- for _, m := range t.Methods {
- if name.IsSame(m.QualifiedName) {
- return lookupResult{value, m.Type, nil}
- }
+ if obj := t.methods.Lookup(pkg, name); obj != nil {
+ m := obj.(*Func)
+ assert(m.typ != nil)
+ return lookupResult{value, m.typ, nil}
}
}
diff --git a/go/types/predicates.go b/go/types/predicates.go
index a99c91a..521cd54 100644
--- a/go/types/predicates.go
+++ b/go/types/predicates.go
@@ -10,82 +10,82 @@
if _, ok := typ.(*Basic); ok {
return ok
}
- _, ok := typ.(*NamedType)
+ _, ok := typ.(*Named)
return ok
}
func isBoolean(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsBoolean != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsBoolean != 0
}
func isInteger(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsInteger != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsInteger != 0
}
func isUnsigned(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsUnsigned != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUnsigned != 0
}
func isFloat(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsFloat != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsFloat != 0
}
func isComplex(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsComplex != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsComplex != 0
}
func isNumeric(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsNumeric != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsNumeric != 0
}
func isString(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsString != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsString != 0
}
func isUntyped(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsUntyped != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUntyped != 0
}
func isOrdered(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsOrdered != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsOrdered != 0
}
func isConstType(typ Type) bool {
- t, ok := underlying(typ).(*Basic)
- return ok && t.Info&IsConstType != 0
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsConstType != 0
}
func isComparable(typ Type) bool {
- switch t := underlying(typ).(type) {
+ switch t := typ.Underlying().(type) {
case *Basic:
- return t.Kind != Invalid && t.Kind != UntypedNil
+ return t.kind != Invalid && t.kind != UntypedNil
case *Pointer, *Interface, *Chan:
// assumes types are equal for pointers and channels
return true
case *Struct:
- for _, f := range t.Fields {
+ for _, f := range t.fields {
if !isComparable(f.Type) {
return false
}
}
return true
case *Array:
- return isComparable(t.Elt)
+ return isComparable(t.elt)
}
return false
}
func hasNil(typ Type) bool {
- switch underlying(typ).(type) {
+ switch typ.Underlying().(type) {
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true
}
@@ -104,20 +104,20 @@
// aliases, thus we cannot solely rely on the x == y check
// above.
if y, ok := y.(*Basic); ok {
- return x.Kind == y.Kind
+ return x.kind == y.kind
}
case *Array:
// Two array types are identical if they have identical element types
// and the same array length.
if y, ok := y.(*Array); ok {
- return x.Len == y.Len && IsIdentical(x.Elt, y.Elt)
+ return x.len == y.len && IsIdentical(x.elt, y.elt)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
- return IsIdentical(x.Elt, y.Elt)
+ return IsIdentical(x.elt, y.elt)
}
case *Struct:
@@ -126,13 +126,13 @@
// and identical tags. Two anonymous fields are considered to have the same
// name. Lower-case field names from different packages are always different.
if y, ok := y.(*Struct); ok {
- if len(x.Fields) == len(y.Fields) {
- for i, f := range x.Fields {
- g := y.Fields[i]
- if !f.QualifiedName.IsSame(g.QualifiedName) ||
- !IsIdentical(f.Type, g.Type) ||
- f.Tag != g.Tag ||
- f.IsAnonymous != g.IsAnonymous {
+ if len(x.fields) == len(y.fields) {
+ for i, f := range x.fields {
+ g := y.fields[i]
+ if f.IsAnonymous != g.IsAnonymous ||
+ x.Tag(i) != y.Tag(i) ||
+ !f.isMatch(g.Pkg, g.Name) ||
+ !IsIdentical(f.Type, g.Type) {
return false
}
}
@@ -143,7 +143,7 @@
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
- return IsIdentical(x.Base, y.Base)
+ return IsIdentical(x.base, y.base)
}
case *Signature:
@@ -152,9 +152,9 @@
// and either both functions are variadic or neither is. Parameter and result
// names are not required to match.
if y, ok := y.(*Signature); ok {
- return identicalTypes(x.Params, y.Params) &&
- identicalTypes(x.Results, y.Results) &&
- x.IsVariadic == y.IsVariadic
+ return x.isVariadic == y.isVariadic &&
+ identicalTypes(x.params, y.params) &&
+ identicalTypes(x.results, y.results)
}
case *Interface:
@@ -162,27 +162,27 @@
// the same names and identical function types. Lower-case method names from
// different packages are always different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
- return identicalMethods(x.Methods, y.Methods) // methods are sorted
+ return identicalMethods(x.methods, y.methods) // methods are sorted
}
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
- return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt)
+ return IsIdentical(x.key, y.key) && IsIdentical(x.elt, y.elt)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
- return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt)
+ return x.dir == y.dir && IsIdentical(x.elt, y.elt)
}
- case *NamedType:
+ case *Named:
// Two named types are identical if their type names originate
// in the same type declaration.
- if y, ok := y.(*NamedType); ok {
- return x.Obj == y.Obj
+ if y, ok := y.(*Named); ok {
+ return x.obj == y.obj
}
}
@@ -191,56 +191,45 @@
// identicalTypes returns true if both lists a and b have the
// same length and corresponding objects have identical types.
-func identicalTypes(a, b []*Var) bool {
- if len(a) != len(b) {
+func identicalTypes(a, b *Tuple) bool {
+ if a.Len() != b.Len() {
return false
}
- for i, x := range a {
- y := b[i]
- if !IsIdentical(x.Type, y.Type) {
- return false
+ if a != nil {
+ for i, x := range a.vars {
+ y := b.vars[i]
+ if !IsIdentical(x.typ, y.typ) {
+ return false
+ }
}
}
return true
}
-// identicalMethods returns true if both lists a and b have the
+// identicalMethods returns true if both object sets a and b have the
// same length and corresponding methods have identical types.
// TODO(gri) make this more efficient
-func identicalMethods(a, b []*Method) bool {
- if len(a) != len(b) {
+func identicalMethods(a, b ObjSet) bool {
+ if len(a.entries) != len(b.entries) {
return false
}
- m := make(map[QualifiedName]*Method)
- for _, x := range a {
- assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries
- m[x.QualifiedName] = x
+ m := make(map[string]*Func)
+ for _, obj := range a.entries {
+ x := obj.(*Func)
+ qname := x.pkg.path + "." + x.name
+ assert(m[qname] == nil) // method list must not have duplicate entries
+ m[qname] = x
}
- for _, y := range b {
- if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) {
+ for _, obj := range b.entries {
+ y := obj.(*Func)
+ qname := y.pkg.path + "." + y.name
+ if x := m[qname]; x == nil || !IsIdentical(x.typ, y.typ) {
return false
}
}
return true
}
-// underlying returns the underlying type of typ.
-func underlying(typ Type) Type {
- // Basic types are representing themselves directly even though they are named.
- if typ, ok := typ.(*NamedType); ok {
- return typ.Underlying // underlying types are never NamedTypes
- }
- return typ
-}
-
-// deref returns a pointer's base type; otherwise it returns typ.
-func deref(typ Type) Type {
- if typ, ok := underlying(typ).(*Pointer); ok {
- return typ.Base
- }
- return typ
-}
-
// defaultType returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. If there is no
// corresponding untyped type, the result is Typ[Invalid].
@@ -248,7 +237,7 @@
func defaultType(typ Type) Type {
if t, ok := typ.(*Basic); ok {
k := Invalid
- switch t.Kind {
+ switch t.kind {
// case UntypedNil:
// There is no default type for nil. For a good error message,
// catch this case before calling this function.
@@ -273,16 +262,18 @@
// missingMethod returns (nil, false) if typ implements T, otherwise
// it returns the first missing method required by T and whether it
// is missing or simply has the wrong type.
+// TODO(gri) make method of Type and/or stand-alone predicate.
//
-func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
+func missingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
// TODO(gri): this needs to correctly compare method names (taking package into account)
// TODO(gri): distinguish pointer and non-pointer receivers
// an interface type implements T if it has no methods with conflicting signatures
// Note: This is stronger than the current spec. Should the spec require this?
- if ityp, _ := underlying(typ).(*Interface); ityp != nil {
- for _, m := range T.Methods {
- res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField
- if res.mode != invalid && !IsIdentical(res.typ, m.Type) {
+ if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
+ for _, obj := range T.methods.entries {
+ m := obj.(*Func)
+ res := lookupField(ityp, m.pkg, m.name) // TODO(gri) no need to go via lookupField
+ if res.mode != invalid && !IsIdentical(res.typ, m.typ) {
return m, true
}
}
@@ -290,12 +281,13 @@
}
// a concrete type implements T if it implements all methods of T.
- for _, m := range T.Methods {
- res := lookupField(typ, m.QualifiedName)
+ for _, obj := range T.methods.entries {
+ m := obj.(*Func)
+ res := lookupField(typ, m.pkg, m.name)
if res.mode == invalid {
return m, false
}
- if !IsIdentical(res.typ, m.Type) {
+ if !IsIdentical(res.typ, m.typ) {
return m, true
}
}
diff --git a/go/types/resolve.go b/go/types/resolve.go
index 43db607..0b3a774 100644
--- a/go/types/resolve.go
+++ b/go/types/resolve.go
@@ -15,28 +15,28 @@
alt := scope.Insert(obj)
if alt == nil && altScope != nil {
// see if there is a conflicting declaration in altScope
- alt = altScope.Lookup(obj.GetName())
+ alt = altScope.Lookup(obj.Name())
}
if alt != nil {
prevDecl := ""
// for dot-imports, local declarations are declared first - swap messages
if dotImport.IsValid() {
- if pos := alt.GetPos(); pos.IsValid() {
+ if pos := alt.Pos(); pos.IsValid() {
check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s",
- obj.GetName(), check.fset.Position(dotImport)))
+ obj.Name(), check.fset.Position(dotImport)))
return
}
// get by w/o other position
- check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName()))
+ check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.Name()))
return
}
- if pos := alt.GetPos(); pos.IsValid() {
+ if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos))
}
- check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl))
+ check.errorf(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name(), prevDecl))
}
}
@@ -51,18 +51,17 @@
}
func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
- pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)}
- check.pkg = pkg
+ pkg := check.pkg
// complete package scope
i := 0
for _, file := range check.files {
// package names must match
switch name := file.Name.Name; {
- case pkg.Name == "":
- pkg.Name = name
- case name != pkg.Name:
- check.errorf(file.Package, "package %s; expected %s", name, pkg.Name)
+ case pkg.name == "":
+ pkg.name = name
+ case name != pkg.name:
+ check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
continue // ignore this file
}
@@ -92,13 +91,13 @@
if name.Name == "_" {
continue
}
- pkg.Scope.Insert(check.lookup(name))
+ pkg.scope.Insert(check.lookup(name))
}
case *ast.TypeSpec:
if s.Name.Name == "_" {
continue
}
- pkg.Scope.Insert(check.lookup(s.Name))
+ pkg.scope.Insert(check.lookup(s.Name))
default:
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
}
@@ -112,7 +111,7 @@
if d.Name.Name == "_" || d.Name.Name == "init" {
continue // blank (_) and init functions are inaccessible
}
- pkg.Scope.Insert(check.lookup(d.Name))
+ pkg.scope.Insert(check.lookup(d.Name))
default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
@@ -124,14 +123,14 @@
for _, file := range check.files {
// build file scope by processing all imports
importErrors := false
- fileScope := &Scope{Outer: pkg.Scope}
+ fileScope := &Scope{Outer: pkg.scope}
for _, spec := range file.Imports {
if importer == nil {
importErrors = true
continue
}
path, _ := strconv.Unquote(spec.Path.Value)
- imp, err := importer(pkg.Imports, path)
+ imp, err := importer(pkg.imports, path)
if err != nil {
check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
importErrors = true
@@ -142,7 +141,7 @@
// import failed. Consider adjusting the logic here a bit.
// local name overrides imported package name
- name := imp.Name
+ name := imp.name
if spec.Name != nil {
name = spec.Name.Name
}
@@ -150,12 +149,12 @@
// add import to file scope
if name == "." {
// merge imported scope with file scope
- for _, obj := range imp.Scope.Entries {
+ for _, obj := range imp.scope.Entries {
// gcimported package scopes contain non-exported
// objects such as types used in partially exported
// objects - do not accept them
- if ast.IsExported(obj.GetName()) {
- check.declareObj(fileScope, pkg.Scope, obj, spec.Pos())
+ if ast.IsExported(obj.Name()) {
+ check.declareObj(fileScope, pkg.scope, obj, spec.Pos())
}
}
// TODO(gri) consider registering the "." identifier
@@ -167,8 +166,8 @@
// (do not re-use imp in the file scope but create
// a new object instead; the Decl field is different
// for different files)
- obj := &Package{Name: name, Scope: imp.Scope, spec: spec}
- check.declareObj(fileScope, pkg.Scope, obj, token.NoPos)
+ obj := &Package{name: name, scope: imp.scope, spec: spec}
+ check.declareObj(fileScope, pkg.scope, obj, token.NoPos)
}
}
@@ -178,7 +177,7 @@
// (objects in the universe may be shadowed by imports;
// with missing imports, identifiers might get resolved
// incorrectly to universe objects)
- pkg.Scope.Outer = nil
+ pkg.scope.Outer = nil
}
i := 0
for _, ident := range file.Unresolved {
@@ -190,7 +189,7 @@
}
file.Unresolved = file.Unresolved[0:i]
- pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors)
+ pkg.scope.Outer = Universe // reset outer scope (is nil if there were importErrors)
}
return
diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go
index d4e3644..ea49111 100644
--- a/go/types/resolver_test.go
+++ b/go/types/resolver_test.go
@@ -66,14 +66,14 @@
idents := make(map[*ast.Ident]Object)
var ctxt Context
ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj }
- pkg, err := ctxt.Check(fset, files)
+ pkg, err := ctxt.Check("testResolveQualifiedIdents", fset, files...)
if err != nil {
t.Fatal(err)
}
// check that all packages were imported
for _, name := range pkgnames {
- if pkg.Imports[name] == nil {
+ if pkg.imports[name] == nil {
t.Errorf("package %s not imported", name)
}
}
@@ -116,14 +116,14 @@
for _, list := range x.Fields.List {
for _, f := range list.Names {
assert(idents[f] == nil)
- idents[f] = &Var{Pkg: pkg, Name: f.Name}
+ idents[f] = &Var{pkg: pkg, name: f.Name}
}
}
case *ast.InterfaceType:
for _, list := range x.Methods.List {
for _, f := range list.Names {
assert(idents[f] == nil)
- idents[f] = &Func{Pkg: pkg, Name: f.Name}
+ idents[f] = &Func{pkg: pkg, name: f.Name}
}
}
case *ast.CompositeLit:
@@ -131,7 +131,7 @@
if kv, ok := e.(*ast.KeyValueExpr); ok {
if k, ok := kv.Key.(*ast.Ident); ok {
assert(idents[k] == nil)
- idents[k] = &Var{Pkg: pkg, Name: k.Name}
+ idents[k] = &Var{pkg: pkg, name: k.Name}
}
}
}
diff --git a/go/types/return.go b/go/types/return.go
index 8644d28..6c69ef4 100644
--- a/go/types/return.go
+++ b/go/types/return.go
@@ -36,7 +36,7 @@
// rather then ordinary functions that have a predeclared
// function type. This would simplify code here and else-
// where.
- if f, _ := obj.(*Func); f != nil && f.Type == predeclaredFunctions[_Panic] {
+ if f, _ := obj.(*Func); f != nil && f.typ == predeclaredFunctions[_Panic] {
return true
}
}
diff --git a/go/types/scope.go b/go/types/scope.go
index 463ee40..7d8ab56 100644
--- a/go/types/scope.go
+++ b/go/types/scope.go
@@ -28,7 +28,7 @@
return s.large[name]
}
for _, obj := range s.Entries {
- if obj.GetName() == name {
+ if obj.Name() == name {
return obj
}
}
@@ -41,7 +41,7 @@
// Otherwise it inserts obj and returns nil.
//
func (s *Scope) Insert(obj Object) Object {
- name := obj.GetName()
+ name := obj.Name()
if alt := s.Lookup(name); alt != nil {
return alt
}
@@ -53,7 +53,7 @@
if s.large == nil {
m := make(map[string]Object, len(s.Entries))
for _, obj := range s.Entries {
- m[obj.GetName()] = obj
+ m[obj.Name()] = obj
}
s.large = m
}
@@ -70,7 +70,7 @@
if s != nil && len(s.Entries) > 0 {
fmt.Fprintln(&buf)
for _, obj := range s.Entries {
- fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj)
+ fmt.Fprintf(&buf, "\t%s\t%T\n", obj.Name(), obj)
}
}
fmt.Fprintf(&buf, "}\n")
diff --git a/go/types/sizes.go b/go/types/sizes.go
index ef6499b..be53eb7 100644
--- a/go/types/sizes.go
+++ b/go/types/sizes.go
@@ -21,9 +21,9 @@
if offsets == nil {
// compute offsets on demand
if f := ctxt.Offsetsof; f != nil {
- offsets = f(s.Fields)
+ offsets = f(s.fields)
// sanity checks
- if len(offsets) != len(s.Fields) {
+ if len(offsets) != len(s.fields) {
panic("Context.Offsetsof returned the wrong number of offsets")
}
for _, o := range offsets {
@@ -32,7 +32,7 @@
}
}
} else {
- offsets = DefaultOffsetsof(s.Fields)
+ offsets = DefaultOffsetsof(s.fields)
}
s.offsets = offsets
}
@@ -45,12 +45,12 @@
func (ctxt *Context) offsetof(typ Type, index []int) int64 {
var o int64
for _, i := range index {
- s, _ := underlying(typ).(*Struct)
+ s, _ := typ.Underlying().(*Struct)
if s == nil {
return -1
}
o += ctxt.offsetsof(s)[i]
- typ = s.Fields[i].Type
+ typ = s.fields[i].Type
}
return o
}
@@ -74,17 +74,17 @@
func DefaultAlignof(typ Type) int64 {
// For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively.
- switch t := underlying(typ).(type) {
+ switch t := typ.Underlying().(type) {
case *Array:
// spec: "For a variable x of array type: unsafe.Alignof(x)
// is the same as unsafe.Alignof(x[0]), but at least 1."
- return DefaultAlignof(t.Elt)
+ return DefaultAlignof(t.elt)
case *Struct:
// spec: "For a variable x of struct type: unsafe.Alignof(x)
// is the largest of the values unsafe.Alignof(x.f) for each
// field f of x, but at least 1."
max := int64(1)
- for _, f := range t.Fields {
+ for _, f := range t.fields {
if a := DefaultAlignof(f.Type); a > max {
max = a
}
@@ -129,32 +129,32 @@
// DefaultSizeof implements the default size computation
// for unsafe.Sizeof. It is used if Context.Sizeof == nil.
func DefaultSizeof(typ Type) int64 {
- switch t := underlying(typ).(type) {
+ switch t := typ.Underlying().(type) {
case *Basic:
if s := t.size; s > 0 {
return s
}
- if t.Kind == String {
+ if t.kind == String {
return DefaultPtrSize * 2
}
case *Array:
- a := DefaultAlignof(t.Elt)
- s := DefaultSizeof(t.Elt)
- return align(s, a) * t.Len // may be 0
+ a := DefaultAlignof(t.elt)
+ s := DefaultSizeof(t.elt)
+ return align(s, a) * t.len // may be 0
case *Slice:
return DefaultPtrSize * 3
case *Struct:
- n := len(t.Fields)
+ n := len(t.fields)
if n == 0 {
return 0
}
offsets := t.offsets
if t.offsets == nil {
// compute offsets on demand
- offsets = DefaultOffsetsof(t.Fields)
+ offsets = DefaultOffsetsof(t.fields)
t.offsets = offsets
}
- return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type)
+ return offsets[n-1] + DefaultSizeof(t.fields[n-1].Type)
case *Signature:
return DefaultPtrSize * 2
}
diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go
index 8b26411..de03fd8 100644
--- a/go/types/stdlib_test.go
+++ b/go/types/stdlib_test.go
@@ -42,7 +42,7 @@
}
// typecheck typechecks the given package files.
-func typecheck(t *testing.T, filenames []string) {
+func typecheck(t *testing.T, path string, filenames []string) {
fset := token.NewFileSet()
// parse package files
@@ -75,7 +75,7 @@
ctxt := Context{
Error: func(err error) { t.Error(err) },
}
- ctxt.Check(fset, files)
+ ctxt.Check(path, fset, files...)
pkgCount++
}
@@ -121,7 +121,7 @@
// typecheck package in directory
if files := pkgfiles(t, dir); files != nil {
- typecheck(t, files)
+ typecheck(t, dir, files)
}
// traverse subdirectories, but don't walk into testdata
diff --git a/go/types/stmt.go b/go/types/stmt.go
index 2243868..3aa5019 100644
--- a/go/types/stmt.go
+++ b/go/types/stmt.go
@@ -24,9 +24,9 @@
return false
}
- if t, ok := x.typ.(*Result); ok {
+ if t, ok := x.typ.(*Tuple); ok {
// TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
- check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
+ check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid
return false
}
@@ -102,17 +102,17 @@
unreachable()
case *Const:
- typ = obj.Type // may already be Typ[Invalid]
+ typ = obj.typ // may already be Typ[Invalid]
if typ == nil {
typ = Typ[Invalid]
if x.mode != invalid {
typ = x.typ
}
- obj.Type = typ
+ obj.typ = typ
}
case *Var:
- typ = obj.Type // may already be Typ[Invalid]
+ typ = obj.typ // may already be Typ[Invalid]
if typ == nil {
typ = Typ[Invalid]
if x.mode != invalid {
@@ -127,7 +127,7 @@
}
}
}
- obj.Type = typ
+ obj.typ = typ
}
}
@@ -147,10 +147,10 @@
// for constants, set their value
if obj, _ := obj.(*Const); obj != nil {
- obj.Val = exact.MakeUnknown() // failure case: we don't know the constant value
+ obj.val = exact.MakeUnknown() // failure case: we don't know the constant value
if x.mode == constant {
if isConstType(x.typ) {
- obj.Val = x.val
+ obj.val = x.val
} else if x.typ != Typ[Invalid] {
check.errorf(x.pos(), "%s has invalid constant type", x)
}
@@ -190,12 +190,13 @@
goto Error
}
- if t, _ := x.typ.(*Result); t != nil && len(lhs) == len(t.Values) {
+ if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() {
// function result
x.mode = value
- for i, obj := range t.Values {
+ for i := 0; i < len(lhs); i++ {
+ obj := t.At(i)
x.expr = nil // TODO(gri) should do better here
- x.typ = obj.Type
+ x.typ = obj.typ
check.assign1to1(lhs[i], nil, &x, decl, iota)
}
return
@@ -225,9 +226,9 @@
}
switch obj := check.lookup(ident).(type) {
case *Const:
- obj.Type = Typ[Invalid]
+ obj.typ = Typ[Invalid]
case *Var:
- obj.Type = Typ[Invalid]
+ obj.typ = Typ[Invalid]
default:
unreachable()
}
@@ -313,7 +314,7 @@
// check.register. Perhaps this can be avoided.)
check.expr(&x, e.Fun, nil, -1)
if x.mode != invalid {
- if b, ok := x.typ.(*builtin); ok && !b.isStatement {
+ if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
used = false
}
}
@@ -339,7 +340,7 @@
if ch.mode == invalid || x.mode == invalid {
return
}
- if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) {
+ if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir&ast.SEND == 0 || !check.assignment(&x, tch.elt) {
if x.mode != invalid {
check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
}
@@ -423,18 +424,18 @@
case *ast.ReturnStmt:
sig := check.funcsig
- if n := len(sig.Results); n > 0 {
+ if n := sig.results.Len(); n > 0 {
// TODO(gri) should not have to compute lhs, named every single time - clean this up
lhs := make([]ast.Expr, n)
named := false // if set, function has named results
- for i, res := range sig.Results {
- if len(res.Name) > 0 {
+ for i, res := range sig.results.vars {
+ if len(res.name) > 0 {
// a blank (_) result parameter is a named result
named = true
}
- name := ast.NewIdent(res.Name)
+ name := ast.NewIdent(res.name)
name.NamePos = s.Pos()
- check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil
+ check.register(name, &Var{name: res.name, typ: res.typ}) // Pkg == nil
lhs[i] = name
}
if len(s.Results) > 0 || !named {
@@ -570,7 +571,7 @@
return
}
var T *Interface
- if T, _ = underlying(x.typ).(*Interface); T == nil {
+ if T, _ = x.typ.Underlying().(*Interface); T == nil {
check.errorf(x.pos(), "%s is not an interface", &x)
return
}
@@ -593,7 +594,7 @@
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
- check.errorf(expr.Pos(), msg, &x, typ, method.Name)
+ check.errorf(expr.Pos(), msg, &x, typ, method.name)
// ok to continue
}
}
@@ -605,7 +606,7 @@
if len(clause.List) != 1 || typ == nil {
typ = x.typ
}
- lhs.Type = typ
+ lhs.typ = typ
}
check.stmtList(clause.Body)
}
@@ -614,7 +615,7 @@
// assumes different types for different clauses. Set it back to the type of the
// TypeSwitchGuard expression so that that variable always has a valid type.
if lhs != nil {
- lhs.Type = x.typ
+ lhs.typ = x.typ
}
case *ast.SelectStmt:
@@ -655,7 +656,7 @@
// determine key/value types
var key, val Type
- switch typ := underlying(x.typ).(type) {
+ switch typ := x.typ.Underlying().(type) {
case *Basic:
if isString(typ) {
key = Typ[UntypedInt]
@@ -663,21 +664,21 @@
}
case *Array:
key = Typ[UntypedInt]
- val = typ.Elt
+ val = typ.elt
case *Slice:
key = Typ[UntypedInt]
- val = typ.Elt
+ val = typ.elt
case *Pointer:
- if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
key = Typ[UntypedInt]
- val = typ.Elt
+ val = typ.elt
}
case *Map:
- key = typ.Key
- val = typ.Elt
+ key = typ.key
+ val = typ.elt
case *Chan:
- key = typ.Elt
- if typ.Dir&ast.RECV == 0 {
+ key = typ.elt
+ if typ.dir&ast.RECV == 0 {
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
// ok to continue
}
diff --git a/go/types/types.go b/go/types/types.go
index b9a17eb..6ee4e7e 100644
--- a/go/types/types.go
+++ b/go/types/types.go
@@ -6,10 +6,23 @@
import "go/ast"
+// A Type represents a type of Go.
// All types implement the Type interface.
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
- aType()
+
+ // 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.
@@ -72,86 +85,176 @@
// A Basic represents a basic type.
type Basic struct {
- Kind BasicKind
- Info BasicInfo
+ kind BasicKind
+ info BasicInfo
size int64 // use DefaultSizeof to get size
- Name string
+ name string
}
-// An Array represents an array type [Len]Elt.
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+// An Array represents an array type.
type Array struct {
- Len int64
- Elt Type
+ len int64
+ elt Type
}
-// A Slice represents a slice type []Elt.
+// NewArray returns a new array type for the given element type and length.
+func NewArray(elem Type, len int64) *Array { return &Array{len, elem} }
+
+// Len returns the length of array a.
+func (a *Array) Len() int64 { return a.len }
+
+// Elem returns element type of array a.
+func (a *Array) Elem() Type { return a.elt }
+
+// A Slice represents a slice type.
type Slice struct {
- Elt Type
+ elt Type
}
-// A QualifiedName is a name qualified with the package that declared the name.
-// Note: Pkg may be a fake package (no name, no scope) because the GC compiler's
-// export information doesn't provide full information in some cases.
-// TODO(gri): Should change Pkg to PkgPath since it's the only thing we care about.
-type QualifiedName struct {
- Pkg *Package // nil only for predeclared error.Error (exported)
- Name string // unqualified type name for anonymous fields
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elt }
+
+// A Field represents a field of a struct.
+// TODO(gri): Should make this just a Var?
+type Field struct {
+ Pkg *Package
+ Name string
+ Type Type
+ IsAnonymous bool
}
-// IsSame reports whether p and q are the same.
-func (p QualifiedName) IsSame(q QualifiedName) bool {
+// A Struct represents a struct type.
+type Struct struct {
+ fields []*Field
+ tags []string // field tags; nil of there are no tags
+ offsets []int64 // field offsets in bytes, lazily computed
+}
+
+func NewStruct(fields []*Field, tags []string) *Struct {
+ return &Struct{fields: fields, tags: tags}
+}
+
+func (s *Struct) NumFields() int { return len(s.fields) }
+func (s *Struct) Field(i int) *Field { return s.fields[i] }
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+func (s *Struct) ForEachField(f func(*Field)) {
+ for _, fld := range s.fields {
+ f(fld)
+ }
+}
+
+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 p.Name != q.Name {
+ if name != f.Name {
return false
}
- // p.Name == q.Name
- return ast.IsExported(p.Name) || p.Pkg.Path == q.Pkg.Path
+ // f.Name == name
+ return ast.IsExported(name) || pkg.path == f.Pkg.path
}
-// A Field represents a field of a struct.
-type Field struct {
- QualifiedName
- Type Type
- Tag string
- IsAnonymous bool
-}
-
-// A Struct represents a struct type struct{...}.
-type Struct struct {
- Fields []*Field
- offsets []int64 // field offsets in bytes, lazily computed
-}
-
-func (typ *Struct) fieldIndex(name QualifiedName) int {
- for i, f := range typ.Fields {
- if f.QualifiedName.IsSame(name) {
+func (s *Struct) fieldIndex(pkg *Package, name string) int {
+ for i, f := range s.fields {
+ if f.isMatch(pkg, name) {
return i
}
}
return -1
}
-// A Pointer represents a pointer type *Base.
+// A Pointer represents a pointer type.
type Pointer struct {
- Base Type
+ base Type
}
-// A Result represents a (multi-value) function call result.
-type Result struct {
- Values []*Var // Signature.Results of the function called
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
}
-// A Signature represents a user-defined function type func(...) (...).
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{x}
+ }
+ return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+// ForEach calls f with each variable of tuple t in index order.
+// TODO(gri): Do we keep ForEach or should we abandon it in favor or Len and At?
+func (t *Tuple) ForEach(f func(*Var)) {
+ if t != nil {
+ for _, x := range t.vars {
+ f(x)
+ }
+ }
+}
+
+// A Signature represents a (non-builtin) function type.
type Signature struct {
- Recv *Var // nil if not a method
- Params []*Var // (incoming) parameters from left to right; or nil
- Results []*Var // (outgoing) results from left to right; or nil
- IsVariadic bool // true if the last parameter's type is of the form ...T
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ isVariadic bool // true if the last parameter's type is of the form ...T
}
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If isVariadic is set, the function
+// is variadic.
+func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature {
+ return &Signature{recv, params, results, isVariadic}
+}
+
+// Recv returns the receiver of signature s, or nil.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// IsVariadic reports whether the signature s is variadic.
+func (s *Signature) IsVariadic() bool { return s.isVariadic }
+
// builtinId is an id of a builtin function.
type builtinId int
@@ -184,8 +287,8 @@
_Trace
)
-// A builtin represents the type of a built-in function.
-type builtin struct {
+// A Builtin represents the type of a built-in function.
+type Builtin struct {
id builtinId
name string
nargs int // number of arguments (minimum if variadic)
@@ -193,44 +296,145 @@
isStatement bool // true if the built-in is valid as an expression statement
}
-// A Method represents a method.
-type Method struct {
- QualifiedName
- Type *Signature
+// Name returns the name of the built-in function b.
+func (b *Builtin) Name() string {
+ return b.name
}
-// An Interface represents an interface type interface{...}.
+// An Interface represents an interface type.
type Interface struct {
- Methods []*Method // TODO(gri) consider keeping them in sorted order
+ methods ObjSet
}
-// A Map represents a map type map[Key]Elt.
+// NumMethods returns the number of methods of interface t.
+func (t *Interface) NumMethods() int { return len(t.methods.entries) }
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+func (t *Interface) Method(i int) *Func {
+ return t.methods.entries[i].(*Func)
+}
+
+// IsEmpty() reports whether t is an empty interface.
+func (t *Interface) IsEmpty() bool { return len(t.methods.entries) == 0 }
+
+// ForEachMethod calls f with each method of interface t in index order.
+// TODO(gri) Should we abandon this in favor of NumMethods and Method?
+func (t *Interface) ForEachMethod(f func(*Func)) {
+ for _, obj := range t.methods.entries {
+ f(obj.(*Func))
+ }
+}
+
+// A Map represents a map type.
type Map struct {
- Key, Elt Type
+ key, elt Type
}
-// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key, elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elt }
+
+// A Chan represents a channel type.
type Chan struct {
- Dir ast.ChanDir
- Elt Type
+ dir ast.ChanDir
+ elt Type
}
-// A NamedType represents a named type as declared in a type declaration.
-type NamedType struct {
- Obj *TypeName // corresponding declared object
- Underlying Type // nil if not fully declared yet; never a *NamedType
- Methods []*Method // TODO(gri) consider keeping them in sorted order
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ast.ChanDir, elem Type) *Chan {
+ return &Chan{dir, elem}
}
-func (*Basic) aType() {}
-func (*Array) aType() {}
-func (*Slice) aType() {}
-func (*Struct) aType() {}
-func (*Pointer) aType() {}
-func (*Result) aType() {}
-func (*Signature) aType() {}
-func (*builtin) aType() {}
-func (*Interface) aType() {}
-func (*Map) aType() {}
-func (*Chan) aType() {}
-func (*NamedType) aType() {}
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ast.ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elt }
+
+// A Named represents a named type.
+type Named struct {
+ obj *TypeName // corresponding declared object
+ underlying Type // nil if not fully declared yet; never a *Named
+ methods ObjSet // directly associated methods (not the method set of this type)
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+func NewNamed(obj *TypeName, underlying Type, methods ObjSet) *Named {
+ typ := &Named{obj, underlying, methods}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+// TypeName returns the type name for the named type t.
+func (t *Named) Obj() *TypeName { return t.obj }
+
+// NumMethods returns the number of methods directly associated with named type t.
+func (t *Named) NumMethods() int { return len(t.methods.entries) }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func {
+ return t.methods.entries[i].(*Func)
+}
+
+// ForEachMethod calls f with each method associated with t in index order.
+// TODO(gri) Should we abandon this in favor of NumMethods and Method?
+func (t *Named) ForEachMethod(fn func(*Func)) {
+ for _, obj := range t.methods.entries {
+ fn(obj.(*Func))
+ }
+}
+
+// Implementations for Type methods.
+
+func (t *Basic) Underlying() Type { return t }
+func (t *Array) Underlying() Type { return t }
+func (t *Slice) Underlying() Type { return t }
+func (t *Struct) Underlying() Type { return t }
+func (t *Pointer) Underlying() Type { return t }
+func (t *Tuple) Underlying() Type { return t }
+func (t *Signature) Underlying() Type { return t }
+func (t *Builtin) Underlying() Type { return t }
+func (t *Interface) Underlying() Type { return t }
+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 {
+ 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) }
+func (t *Struct) String() string { return typeString(t) }
+func (t *Pointer) String() string { return typeString(t) }
+func (t *Tuple) String() string { return typeString(t) }
+func (t *Signature) String() string { return typeString(t) }
+func (t *Builtin) String() string { return typeString(t) }
+func (t *Interface) String() string { return typeString(t) }
+func (t *Map) String() string { return typeString(t) }
+func (t *Chan) String() string { return typeString(t) }
+func (t *Named) String() string { return typeString(t) }
diff --git a/go/types/types_test.go b/go/types/types_test.go
index 8e228fa..6b08872 100644
--- a/go/types/types_test.go
+++ b/go/types/types_test.go
@@ -20,7 +20,7 @@
if err != nil {
return nil, err
}
- pkg, err := Check(fset, []*ast.File{file})
+ pkg, err := Check("", fset, file)
return pkg, err
}
@@ -110,7 +110,7 @@
t.Errorf("%s: %s", src, err)
continue
}
- typ := underlying(pkg.Scope.Lookup("T").GetType())
+ typ := pkg.scope.Lookup("T").Type().Underlying()
str := typeString(typ)
if str != test.str {
t.Errorf("%s: got %s, want %s", test.src, str, test.str)
diff --git a/go/types/universe.go b/go/types/universe.go
index 3658352..68cafc6 100644
--- a/go/types/universe.go
+++ b/go/types/universe.go
@@ -57,13 +57,13 @@
}
var predeclaredConstants = [...]*Const{
- {Name: "true", Type: Typ[UntypedBool], Val: exact.MakeBool(true)},
- {Name: "false", Type: Typ[UntypedBool], Val: exact.MakeBool(false)},
- {Name: "iota", Type: Typ[UntypedInt], Val: exact.MakeInt64(0)},
- {Name: "nil", Type: Typ[UntypedNil], Val: exact.MakeNil()},
+ {name: "true", typ: Typ[UntypedBool], val: exact.MakeBool(true)},
+ {name: "false", typ: Typ[UntypedBool], val: exact.MakeBool(false)},
+ {name: "iota", typ: Typ[UntypedInt], val: exact.MakeInt64(0)},
+ {name: "nil", typ: Typ[UntypedNil], val: exact.MakeNil()},
}
-var predeclaredFunctions = [...]*builtin{
+var predeclaredFunctions = [...]*Builtin{
{_Append, "append", 1, true, false},
{_Cap, "cap", 1, false, false},
{_Close, "close", 1, false, true},
@@ -87,21 +87,23 @@
func init() {
Universe = new(Scope)
- Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
+ Unsafe = &Package{name: "unsafe", scope: new(Scope)}
// predeclared types
for _, t := range Typ {
- def(&TypeName{Name: t.Name, Type: t})
+ def(&TypeName{name: t.name, typ: t})
}
for _, t := range aliases {
- def(&TypeName{Name: t.Name, Type: t})
+ def(&TypeName{name: t.name, typ: t})
}
// error type
{
// Error has a nil package in its qualified name since it is in no package
- err := &Method{QualifiedName{nil, "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
- def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}})
+ var methods ObjSet
+ sig := &Signature{results: NewTuple(&Var{name: "", typ: Typ[String]})}
+ methods.Insert(&Func{nil, "Error", sig, nil})
+ def(&TypeName{name: "error", typ: &Named{underlying: &Interface{methods: methods}}})
}
for _, c := range predeclaredConstants {
@@ -109,7 +111,7 @@
}
for _, f := range predeclaredFunctions {
- def(&Func{Name: f.name, Type: f})
+ def(&Func{name: f.name, typ: f})
}
universeIota = Universe.Lookup("iota").(*Const)
@@ -120,24 +122,24 @@
// scope; other objects are inserted in the universe scope.
//
func def(obj Object) {
- name := obj.GetName()
+ name := obj.Name()
if strings.Index(name, " ") >= 0 {
return // nothing to do
}
// fix Obj link for named types
- if typ, ok := obj.GetType().(*NamedType); ok {
- typ.Obj = obj.(*TypeName)
+ if typ, ok := obj.Type().(*Named); ok {
+ typ.obj = obj.(*TypeName)
}
// exported identifiers go into package unsafe
scope := Universe
if ast.IsExported(name) {
- scope = Unsafe.Scope
+ scope = Unsafe.scope
// set Pkg field
switch obj := obj.(type) {
case *TypeName:
- obj.Pkg = Unsafe
+ obj.pkg = Unsafe
case *Func:
- obj.Pkg = Unsafe
+ obj.pkg = Unsafe
default:
unreachable()
}
diff --git a/ssa/builder.go b/ssa/builder.go
index 3604517..9f304b4 100644
--- a/ssa/builder.go
+++ b/ssa/builder.go
@@ -34,8 +34,15 @@
"code.google.com/p/go.tools/go/types"
)
+type opaqueType struct {
+ types.Type
+ name string
+}
+
+func (t *opaqueType) String() string { return t.name }
+
var (
- varOk = &types.Var{Name: "ok", Type: tBool}
+ varOk = types.NewVar(nil, "ok", tBool)
// Type constants.
tBool = types.Typ[types.Bool]
@@ -47,15 +54,14 @@
tInt = types.Typ[types.Int]
tInvalid = types.Typ[types.Invalid]
tUntypedNil = types.Typ[types.UntypedNil]
- tRangeIter = &types.Basic{Name: "iter"} // the type of all "range" iterators
+ tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators
tEface = new(types.Interface)
// The result type of a "select".
- tSelect = &types.Result{Values: []*types.Var{
- {Name: "index", Type: tInt},
- {Name: "recv", Type: tEface},
- varOk,
- }}
+ tSelect = types.NewTuple(
+ types.NewVar(nil, "index", tInt),
+ types.NewVar(nil, "recv", tEface),
+ varOk)
// SSA Value constants.
vZero = intLiteral(0)
@@ -77,7 +83,7 @@
// Loader is a SourceLoader function that finds, loads and
// parses Go source files for a given import path. (It is
// ignored if the mode bits include UseGCImporter.)
- // See (e.g.) GoRootLoader.
+ // See (e.g.) MakeGoBuildLoader.
Loader SourceLoader
// RetainAST is an optional user predicate that determines
@@ -149,7 +155,7 @@
Packages: make(map[string]*Package),
Builtins: make(map[types.Object]*Builtin),
methodSets: make(map[types.Type]MethodSet),
- concreteMethods: make(map[*types.Method]*Function),
+ concreteMethods: make(map[*types.Func]*Function),
mode: context.Mode,
},
Context: context,
@@ -322,24 +328,28 @@
return fn.emit(&c)
case *ast.IndexExpr:
- mapt := underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Map)
- typ = mapt.Elt
- tuple = fn.emit(&Lookup{
+ mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map)
+ typ = mapt.Elem()
+ lookup := &Lookup{
X: b.expr(fn, e.X),
- Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
CommaOk: true,
- })
+ }
+ lookup.setPos(e.Lbrack)
+ tuple = fn.emit(lookup)
case *ast.TypeAssertExpr:
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.TypeOf(e))
case *ast.UnaryExpr: // must be receive <-
- typ = underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Chan).Elt
- tuple = fn.emit(&UnOp{
+ typ = fn.Pkg.TypeOf(e.X).Underlying().(*types.Chan).Elem()
+ unop := &UnOp{
Op: token.ARROW,
X: b.expr(fn, e.X),
CommaOk: true,
- })
+ }
+ unop.setPos(e.OpPos)
+ tuple = fn.emit(unop)
default:
panic(fmt.Sprintf("unexpected exprN: %T", e))
@@ -351,10 +361,10 @@
tuple.(interface {
setType(types.Type)
- }).setType(&types.Result{Values: []*types.Var{
- {Name: "value", Type: typ},
+ }).setType(types.NewTuple(
+ types.NewVar(nil, "value", typ),
varOk,
- }})
+ ))
return tuple
}
@@ -369,7 +379,7 @@
func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.Type, pos token.Pos) Value {
switch name {
case "make":
- switch underlyingType(typ).(type) {
+ switch typ.Underlying().(type) {
case *types.Slice:
n := b.expr(fn, args[1])
m := n
@@ -379,8 +389,8 @@
v := &MakeSlice{
Len: n,
Cap: m,
- Pos: pos,
}
+ v.setPos(pos)
v.setType(typ)
return fn.emit(v)
@@ -389,7 +399,8 @@
if len(args) == 2 {
res = b.expr(fn, args[1])
}
- v := &MakeMap{Reserve: res, Pos: pos}
+ v := &MakeMap{Reserve: res}
+ v.setPos(pos)
v.setType(typ)
return fn.emit(v)
@@ -398,13 +409,14 @@
if len(args) == 2 {
sz = b.expr(fn, args[1])
}
- v := &MakeChan{Size: sz, Pos: pos}
+ v := &MakeChan{Size: sz}
+ v.setPos(pos)
v.setType(typ)
return fn.emit(v)
}
case "new":
- return emitNew(fn, indirectType(underlyingType(typ)), pos)
+ return emitNew(fn, typ.Underlying().Deref(), pos)
case "len", "cap":
// Special case: len or cap of an array or *array is
@@ -412,15 +424,18 @@
// We must still evaluate the value, though. (If it
// was side-effect free, the whole call would have
// been constant-folded.)
- t := underlyingType(deref(fn.Pkg.TypeOf(args[0])))
+ t := fn.Pkg.TypeOf(args[0]).Deref().Underlying()
if at, ok := t.(*types.Array); ok {
b.expr(fn, args[0]) // for effects only
- return intLiteral(at.Len)
+ return intLiteral(at.Len())
}
// Otherwise treat as normal.
case "panic":
- fn.emit(&Panic{X: emitConv(fn, b.expr(fn, args[0]), tEface)})
+ fn.emit(&Panic{
+ X: emitConv(fn, b.expr(fn, args[0]), tEface),
+ pos: pos,
+ })
fn.currentBlock = fn.newBasicBlock("unreachable")
return vFalse // any non-nil Value will do
}
@@ -433,11 +448,12 @@
// in a potentially escaping way.
//
func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value {
- id := makeId(e.Sel.Name, fn.Pkg.Types)
- st := underlyingType(deref(fn.Pkg.TypeOf(e.X))).(*types.Struct)
+ id := MakeId(e.Sel.Name, fn.Pkg.Types)
+ st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct)
index := -1
- for i, f := range st.Fields {
- if IdFromQualifiedName(f.QualifiedName) == id {
+ for i, n := 0, st.NumFields(); i < n; i++ {
+ f := st.Field(i)
+ if MakeId(f.Name, f.Pkg) == id {
index = i
break
}
@@ -451,10 +467,11 @@
}
}
fieldType := fn.Pkg.TypeOf(e)
+ pos := e.Sel.Pos()
if wantAddr {
- return b.fieldAddr(fn, e.X, path, index, fieldType, escaping)
+ return b.fieldAddr(fn, e.X, path, index, fieldType, pos, escaping)
}
- return b.fieldExpr(fn, e.X, path, index, fieldType)
+ return b.fieldExpr(fn, e.X, path, index, fieldType, pos)
}
// fieldAddr evaluates the base expression (a struct or *struct),
@@ -464,17 +481,17 @@
//
// (fieldType can be derived from base+index.)
//
-func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, escaping bool) Value {
+func (b *Builder) fieldAddr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos, escaping bool) Value {
var x Value
if path != nil {
- switch underlyingType(path.field.Type).(type) {
+ switch path.field.Type.Underlying().(type) {
case *types.Struct:
- x = b.fieldAddr(fn, base, path.tail, path.index, path.field.Type, escaping)
+ x = b.fieldAddr(fn, base, path.tail, path.index, path.field.Type, token.NoPos, escaping)
case *types.Pointer:
- x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type)
+ x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos)
}
} else {
- switch underlyingType(fn.Pkg.TypeOf(base)).(type) {
+ switch fn.Pkg.TypeOf(base).Underlying().(type) {
case *types.Struct:
x = b.addr(fn, base, escaping).(address).addr
case *types.Pointer:
@@ -485,6 +502,7 @@
X: x,
Field: index,
}
+ v.setPos(pos)
v.setType(pointer(fieldType))
return fn.emit(v)
}
@@ -496,19 +514,20 @@
//
// (fieldType can be derived from base+index.)
//
-func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type) Value {
+func (b *Builder) fieldExpr(fn *Function, base ast.Expr, path *anonFieldPath, index int, fieldType types.Type, pos token.Pos) Value {
var x Value
if path != nil {
- x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type)
+ x = b.fieldExpr(fn, base, path.tail, path.index, path.field.Type, token.NoPos)
} else {
x = b.expr(fn, base)
}
- switch underlyingType(x.Type()).(type) {
+ switch x.Type().Underlying().(type) {
case *types.Struct:
v := &Field{
X: x,
Field: index,
}
+ v.setPos(pos)
v.setType(fieldType)
return fn.emit(v)
@@ -517,6 +536,7 @@
X: x,
Field: index,
}
+ v.setPos(pos)
v.setType(pointer(fieldType))
return emitLoad(fn, fn.emit(v))
}
@@ -557,7 +577,7 @@
return address{v}
case *ast.CompositeLit:
- t := deref(fn.Pkg.TypeOf(e))
+ t := fn.Pkg.TypeOf(e).Deref()
var v Value
if escaping {
v = emitNew(fn, t, e.Lbrace)
@@ -576,7 +596,7 @@
if v, ok := b.lookup(fn.Pkg, obj); ok {
return address{v}
}
- panic("undefined package-qualified name: " + obj.GetName())
+ panic("undefined package-qualified name: " + obj.Name())
}
// e.f where e is an expression.
@@ -585,21 +605,21 @@
case *ast.IndexExpr:
var x Value
var et types.Type
- switch t := underlyingType(fn.Pkg.TypeOf(e.X)).(type) {
+ switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) {
case *types.Array:
x = b.addr(fn, e.X, escaping).(address).addr
- et = pointer(t.Elt)
+ et = pointer(t.Elem())
case *types.Pointer: // *array
x = b.expr(fn, e.X)
- et = pointer(underlyingType(t.Base).(*types.Array).Elt)
+ et = pointer(t.Elem().Underlying().(*types.Array).Elem())
case *types.Slice:
x = b.expr(fn, e.X)
- et = pointer(t.Elt)
+ et = pointer(t.Elem())
case *types.Map:
return &element{
m: b.expr(fn, e.X),
- k: emitConv(fn, b.expr(fn, e.Index), t.Key),
- t: t.Elt,
+ k: emitConv(fn, b.expr(fn, e.Index), t.Key()),
+ t: t.Elem(),
}
default:
panic("unexpected container type in IndexExpr: " + t.String())
@@ -629,7 +649,7 @@
if addr, ok := loc.(address); ok {
if e, ok := e.(*ast.CompositeLit); ok {
typ := addr.typ()
- switch underlyingType(typ).(type) {
+ switch typ.Underlying().(type) {
case *types.Pointer: // implicit & -- possibly escaping
ptr := b.addr(fn, e, true).(address).addr
addr.store(fn, ptr) // copy address
@@ -665,8 +685,8 @@
posn := b.Prog.Files.Position(e.Type.Func)
fn2 := &Function{
Name_: fmt.Sprintf("func@%d.%d", posn.Line, posn.Column),
- Signature: underlyingType(fn.Pkg.TypeOf(e.Type)).(*types.Signature),
- Pos: e.Type.Func,
+ Signature: fn.Pkg.TypeOf(e.Type).Underlying().(*types.Signature),
+ pos: e.Type.Func,
Enclosing: fn,
Pkg: fn.Pkg,
Prog: b.Prog,
@@ -697,8 +717,20 @@
case *ast.CallExpr:
typ := fn.Pkg.TypeOf(e)
if fn.Pkg.IsType(e.Fun) {
- // Type conversion, e.g. string(x) or big.Int(x)
- return emitConv(fn, b.expr(fn, e.Args[0]), typ)
+ // Explicit type conversion, e.g. string(x) or big.Int(x)
+ x := b.expr(fn, e.Args[0])
+ y := emitConv(fn, x, typ)
+ if y != x {
+ switch y := y.(type) {
+ case *Convert:
+ y.pos = e.Lparen
+ case *ChangeType:
+ y.pos = e.Lparen
+ case *MakeInterface:
+ y.pos = e.Lparen
+ }
+ }
+ return y
}
// Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := e.Fun.(*ast.Ident); ok {
@@ -726,6 +758,7 @@
Op: e.Op,
X: b.expr(fn, e.X),
}
+ v.setPos(e.OpPos)
v.setType(fn.Pkg.TypeOf(e))
return fn.emit(v)
default:
@@ -750,7 +783,7 @@
case *ast.SliceExpr:
var low, high Value
var x Value
- switch underlyingType(fn.Pkg.TypeOf(e.X)).(type) {
+ switch fn.Pkg.TypeOf(e.X).Underlying().(type) {
case *types.Array:
// Potentially escaping.
x = b.addr(fn, e.X, true).(address).addr
@@ -793,7 +826,7 @@
// (*T).f or T.f, the method f from the method-set of type T.
if fn.Pkg.IsType(e.X) {
- id := makeId(e.Sel.Name, fn.Pkg.Types)
+ id := MakeId(e.Sel.Name, fn.Pkg.Types)
typ := fn.Pkg.TypeOf(e.X)
if m := b.Prog.MethodSet(typ)[id]; m != nil {
return m
@@ -807,24 +840,25 @@
return b.selector(fn, e, false, false)
case *ast.IndexExpr:
- switch t := underlyingType(fn.Pkg.TypeOf(e.X)).(type) {
+ switch t := fn.Pkg.TypeOf(e.X).Underlying().(type) {
case *types.Array:
// Non-addressable array (in a register).
v := &Index{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), tInt),
}
- v.setType(t.Elt)
+ v.setType(t.Elem())
return fn.emit(v)
case *types.Map:
// Maps are not addressable.
- mapt := underlyingType(fn.Pkg.TypeOf(e.X)).(*types.Map)
+ mapt := fn.Pkg.TypeOf(e.X).Underlying().(*types.Map)
v := &Lookup{
X: b.expr(fn, e.X),
- Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
}
- v.setType(mapt.Elt)
+ v.setPos(e.Lbrack)
+ v.setType(mapt.Elem())
return fn.emit(v)
case *types.Basic: // => string
@@ -833,6 +867,7 @@
X: b.expr(fn, e.X),
Index: b.expr(fn, e.Index),
}
+ v.setPos(e.Lbrack)
v.setType(tByte)
return fn.emit(v)
@@ -864,7 +899,7 @@
// occurring in e.
//
func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
- c.Pos = e.Lparen
+ c.pos = e.Lparen
c.HasEllipsis = e.Ellipsis != 0
// Is the call of the form x.f()?
@@ -886,7 +921,7 @@
c.Func = v
return
}
- panic("undefined package-qualified name: " + obj.GetName())
+ panic("undefined package-qualified name: " + obj.Name())
}
// Case 2a: X.f() or (*X).f(): a statically dipatched call to
@@ -905,12 +940,12 @@
// Case 2: x.f(): a statically dispatched call to a method
// from the method-set of X or perhaps *X (if x is addressable
// but not a pointer).
- id := makeId(sel.Sel.Name, fn.Pkg.Types)
+ id := MakeId(sel.Sel.Name, fn.Pkg.Types)
// Consult method-set of X.
if m := b.Prog.MethodSet(typ)[id]; m != nil {
var recv Value
aptr := isPointer(typ)
- fptr := isPointer(m.Signature.Recv.Type)
+ fptr := isPointer(m.Signature.Recv().Type())
if aptr == fptr {
// Actual's and formal's "pointerness" match.
recv = b.expr(fn, sel.X)
@@ -937,7 +972,7 @@
}
}
- switch t := underlyingType(typ).(type) {
+ switch t := typ.Underlying().(type) {
case *types.Struct, *types.Pointer:
// Case 3: x.f() where x.f is a function value in a
// struct field f; not a method call. f is a 'var'
@@ -949,7 +984,7 @@
// Case 4: x.f() where a dynamically dispatched call
// to an interface method f. f is a 'func' object in
// the Methods of types.Interface X
- c.Method, _ = methodIndex(t, t.Methods, id)
+ c.Method, _ = methodIndex(t, id)
c.Recv = b.expr(fn, sel.X)
default:
@@ -967,9 +1002,9 @@
for i, arg := range e.Args {
// TODO(gri): annoyingly Signature.Params doesn't
// reflect the slice type for a final ...T param.
- t := sig.Params[i].Type
- if sig.IsVariadic && i == len(e.Args)-1 {
- t = &types.Slice{Elt: t}
+ t := sig.Params().At(i).Type()
+ if sig.IsVariadic() && i == len(e.Args)-1 {
+ t = types.NewSlice(t)
}
args = append(args, emitConv(fn, b.expr(fn, arg), t))
}
@@ -985,9 +1020,9 @@
// args; a suffix of them may end up in a varargs slice.
for _, arg := range e.Args {
v := b.expr(fn, arg)
- if ttuple, ok := v.Type().(*types.Result); ok { // MRV chain
- for i, t := range ttuple.Values {
- args = append(args, emitExtract(fn, v, i, t.Type))
+ if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain
+ for i, n := 0, ttuple.Len(); i < n; i++ {
+ args = append(args, emitExtract(fn, v, i, ttuple.At(i).Type()))
}
} else {
args = append(args, v)
@@ -995,28 +1030,25 @@
}
// Actual->formal assignability conversions for normal parameters.
- np := len(sig.Params) // number of normal parameters
- if sig.IsVariadic {
+ np := sig.Params().Len() // number of normal parameters
+ if sig.IsVariadic() {
np--
}
for i := 0; i < np; i++ {
- args[offset+i] = emitConv(fn, args[offset+i], sig.Params[i].Type)
+ args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type())
}
// Actual->formal assignability conversions for variadic parameter,
// and construction of slice.
- if sig.IsVariadic {
+ if sig.IsVariadic() {
varargs := args[offset+np:]
- vt := sig.Params[np].Type
- st := &types.Slice{Elt: vt}
+ vt := sig.Params().At(np).Type()
+ st := types.NewSlice(vt)
if len(varargs) == 0 {
args = append(args, nilLiteral(st))
} else {
// Replace a suffix of args with a slice containing it.
- at := &types.Array{
- Elt: vt,
- Len: int64(len(varargs)),
- }
+ at := types.NewArray(vt, int64(len(varargs)))
a := emitNew(fn, at, e.Lparen)
for i, arg := range varargs {
iaddr := &IndexAddr{
@@ -1044,7 +1076,7 @@
b.setCallFunc(fn, e, c)
// Then append the other actual parameters.
- sig, _ := underlyingType(fn.Pkg.TypeOf(e.Fun)).(*types.Signature)
+ sig, _ := fn.Pkg.TypeOf(e.Fun).Underlying().(*types.Signature)
if sig == nil {
sig = builtinCallSignature(&fn.Pkg.TypeInfo, e)
}
@@ -1161,13 +1193,12 @@
defer logStack("build globals %s", spec.Names)()
}
tuple := b.exprN(init, spec.Values[0])
- rtypes := tuple.Type().(*types.Result).Values
+ result := tuple.Type().(*types.Tuple)
for i, id := range spec.Names {
if !isBlankIdent(id) {
g := b.globals[init.Pkg.ObjectOf(id)].(*Global)
g.spec = nil // just an optimization
- emitStore(init, g,
- emitExtract(init, tuple, i, rtypes[i].Type))
+ emitStore(init, g, emitExtract(init, tuple, i, result.At(i).Type()))
}
}
}
@@ -1206,11 +1237,11 @@
default:
// e.g. var x, y = pos()
tuple := b.exprN(fn, spec.Values[0])
- rtypes := tuple.Type().(*types.Result).Values
+ result := tuple.Type().(*types.Tuple)
for i, id := range spec.Names {
if !isBlankIdent(id) {
lhs := fn.addNamedLocal(fn.Pkg.ObjectOf(id))
- emitStore(fn, lhs, emitExtract(fn, tuple, i, rtypes[i].Type))
+ emitStore(fn, lhs, emitExtract(fn, tuple, i, result.At(i).Type()))
}
}
}
@@ -1262,13 +1293,30 @@
} else {
// e.g. x, y = pos()
tuple := b.exprN(fn, rhss[0])
- rtypes := tuple.Type().(*types.Result).Values
+ result := tuple.Type().(*types.Tuple)
for i, lval := range lvals {
- lval.store(fn, emitExtract(fn, tuple, i, rtypes[i].Type))
+ lval.store(fn, emitExtract(fn, tuple, i, result.At(i).Type()))
}
}
}
+// arrayLen returns the length of the array whose composite literal elements are elts.
+func (b *Builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
+ var max int64 = -1
+ var i int64 = -1
+ for _, e := range elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ i = b.expr(fn, kv.Key).(*Literal).Int64()
+ } else {
+ i++
+ }
+ if i > max {
+ max = i
+ }
+ }
+ return max + 1
+}
+
// compLit emits to fn code to initialize a composite literal e at
// address addr with type typ, typically allocated by Alloc.
// Nested composite literals are recursively initialized in place
@@ -1278,13 +1326,14 @@
// TODO(adonovan): document how and why typ ever differs from
// fn.Pkg.TypeOf(e).
- switch t := underlyingType(typ).(type) {
+ switch t := typ.Underlying().(type) {
case *types.Struct:
for i, e := range e.Elts {
fieldIndex := i
if kv, ok := e.(*ast.KeyValueExpr); ok {
fname := kv.Key.(*ast.Ident).Name
- for i, sf := range t.Fields {
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ sf := t.Field(i)
if sf.Name == fname {
fieldIndex = i
e = kv.Value
@@ -1292,7 +1341,7 @@
}
}
}
- sf := t.Fields[fieldIndex]
+ sf := t.Field(fieldIndex)
faddr := &FieldAddr{
X: addr,
Field: fieldIndex,
@@ -1307,14 +1356,14 @@
var array Value
switch t := t.(type) {
case *types.Slice:
- at = &types.Array{Elt: t.Elt} // set Len later
+ at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))
array = emitNew(fn, at, e.Lbrace)
case *types.Array:
at = t
array = addr
}
+
var idx *Literal
- var max int64 = -1
for _, e := range e.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
idx = b.expr(fn, kv.Key).(*Literal)
@@ -1326,34 +1375,33 @@
}
idx = intLiteral(idxval)
}
- if idx.Int64() > max {
- max = idx.Int64()
- }
iaddr := &IndexAddr{
X: array,
Index: idx,
}
- iaddr.setType(pointer(at.Elt))
+ iaddr.setType(pointer(at.Elem()))
fn.emit(iaddr)
b.exprInPlace(fn, address{iaddr}, e)
}
if t != at { // slice
- at.Len = max + 1
s := &Slice{X: array}
+ s.setPos(e.Lbrace)
s.setType(t)
emitStore(fn, addr, fn.emit(s))
}
case *types.Map:
- m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts))), Pos: e.Lbrace}
+ m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))}
+ m.setPos(e.Lbrace)
m.setType(typ)
emitStore(fn, addr, fn.emit(m))
for _, e := range e.Elts {
e := e.(*ast.KeyValueExpr)
up := &MapUpdate{
Map: m,
- Key: emitConv(fn, b.expr(fn, e.Key), t.Key),
- Value: emitConv(fn, b.expr(fn, e.Value), t.Elt),
+ Key: emitConv(fn, b.expr(fn, e.Key), t.Key()),
+ Value: emitConv(fn, b.expr(fn, e.Value), t.Elem()),
+ pos: e.Colon,
}
fn.emit(up)
}
@@ -1614,7 +1662,7 @@
Dir: ast.SEND,
Chan: ch,
Send: emitConv(fn, b.expr(fn, comm.Value),
- underlyingType(ch.Type()).(*types.Chan).Elt),
+ ch.Type().Underlying().(*types.Chan).Elem()),
})
case *ast.AssignStmt: // x := <-ch
@@ -1647,6 +1695,7 @@
States: states,
Blocking: blocking,
}
+ triple.setPos(s.Select)
triple.setType(tSelect)
fn.emit(triple)
idx := emitExtract(fn, triple, 0, tInt)
@@ -1675,12 +1724,12 @@
switch comm := clause.Comm.(type) {
case *ast.AssignStmt: // x := <-states[state].Chan
xdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[0].(*ast.Ident)))
- recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), indirectType(xdecl.Type()))
+ recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref())
emitStore(fn, xdecl, recv)
if len(comm.Lhs) == 2 { // x, ok := ...
okdecl := fn.addNamedLocal(fn.Pkg.ObjectOf(comm.Lhs[1].(*ast.Ident)))
- emitStore(fn, okdecl, emitExtract(fn, triple, 2, indirectType(okdecl.Type())))
+ emitStore(fn, okdecl, emitExtract(fn, triple, 2, okdecl.Type().Deref()))
}
}
b.stmtList(fn, clause.Body)
@@ -1776,13 +1825,13 @@
// Determine number of iterations.
var length Value
- if arr, ok := deref(x.Type()).(*types.Array); ok {
+ if arr, ok := x.Type().Deref().(*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
// elimination if x is pure, static unrolling, etc.
// Ranging over a nil *array may have >0 iterations.
- length = intLiteral(arr.Len)
+ length = intLiteral(arr.Len())
} else {
// length = len(x).
var c Call
@@ -1814,13 +1863,13 @@
k = emitLoad(fn, index)
if tv != nil {
- switch t := underlyingType(x.Type()).(type) {
+ switch t := x.Type().Underlying().(type) {
case *types.Array:
instr := &Index{
X: x,
Index: k,
}
- instr.setType(t.Elt)
+ instr.setType(t.Elem())
v = fn.emit(instr)
case *types.Pointer: // *array
@@ -1828,7 +1877,7 @@
X: x,
Index: k,
}
- instr.setType(pointer(t.Base.(*types.Array).Elt))
+ instr.setType(pointer(t.Elem().(*types.Array).Elem()))
v = emitLoad(fn, fn.emit(instr))
case *types.Slice:
@@ -1836,7 +1885,7 @@
X: x,
Index: k,
}
- instr.setType(pointer(t.Elt))
+ instr.setType(pointer(t.Elem()))
v = emitLoad(fn, fn.emit(instr))
default:
@@ -1851,7 +1900,7 @@
// tk and tv are the types of the key/value results k and v, or nil
// if the respective component is not wanted.
//
-func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type) (k, v Value, loop, done *BasicBlock) {
+func (b *Builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
//
// it = range x
// loop: (target of continue)
@@ -1874,6 +1923,7 @@
}
rng := &Range{X: x}
+ rng.setPos(pos)
rng.setType(tRangeIter)
it := fn.emit(rng)
@@ -1881,17 +1931,17 @@
emitJump(fn, loop)
fn.currentBlock = loop
- _, isString := underlyingType(x.Type()).(*types.Basic)
+ _, isString := x.Type().Underlying().(*types.Basic)
okv := &Next{
Iter: it,
IsString: isString,
}
- okv.setType(&types.Result{Values: []*types.Var{
+ okv.setType(types.NewTuple(
varOk,
- {Name: "k", Type: tk},
- {Name: "v", Type: tv},
- }})
+ types.NewVar(nil, "k", tk),
+ types.NewVar(nil, "v", tv),
+ ))
fn.emit(okv)
body := fn.newBasicBlock("rangeiter.body")
@@ -1933,10 +1983,10 @@
X: x,
CommaOk: true,
}
- recv.setType(&types.Result{Values: []*types.Var{
- {Name: "k", Type: tk},
+ recv.setType(types.NewTuple(
+ types.NewVar(nil, "k", tk),
varOk,
- }})
+ ))
ko := fn.emit(recv)
body := fn.newBasicBlock("rangechan.body")
done = fn.newBasicBlock("rangechan.done")
@@ -1979,7 +2029,7 @@
var k, v Value
var loop, done *BasicBlock
- switch rt := underlyingType(x.Type()).(type) {
+ switch rt := x.Type().Underlying().(type) {
case *types.Slice, *types.Array, *types.Pointer: // *array
k, v, loop, done = b.rangeIndexed(fn, x, tv)
@@ -1987,7 +2037,7 @@
k, loop, done = b.rangeChan(fn, x, tk)
case *types.Map, *types.Basic: // string
- k, v, loop, done = b.rangeIter(fn, x, tk, tv)
+ k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For)
default:
panic("Cannot range over: " + rt.String())
@@ -2058,7 +2108,8 @@
fn.emit(&Send{
Chan: b.expr(fn, s.Chan),
X: emitConv(fn, b.expr(fn, s.Value),
- underlyingType(fn.Pkg.TypeOf(s.Chan)).(*types.Chan).Elt),
+ fn.Pkg.TypeOf(s.Chan).Underlying().(*types.Chan).Elem()),
+ pos: s.Arrow,
})
case *ast.IncDecStmt:
@@ -2112,18 +2163,19 @@
}
var results []Value
- if len(s.Results) == 1 && len(fn.Signature.Results) > 1 {
+ if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 {
// Return of one expression in a multi-valued function.
tuple := b.exprN(fn, s.Results[0])
- for i, v := range tuple.Type().(*types.Result).Values {
+ ttuple := tuple.Type().(*types.Tuple)
+ for i, n := 0, ttuple.Len(); i < n; i++ {
results = append(results,
- emitConv(fn, emitExtract(fn, tuple, i, v.Type),
- fn.Signature.Results[i].Type))
+ emitConv(fn, emitExtract(fn, tuple, i, ttuple.At(i).Type()),
+ fn.Signature.Results().At(i).Type()))
}
} else {
// 1:1 return, or no-arg return in non-void function.
for i, r := range s.Results {
- v := emitConv(fn, b.expr(fn, r), fn.Signature.Results[i].Type)
+ v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type())
results = append(results, v)
}
}
@@ -2144,7 +2196,7 @@
results = append(results, emitLoad(fn, r))
}
}
- fn.emit(&Ret{Results: results})
+ fn.emit(&Ret{Results: results, pos: s.Return})
fn.currentBlock = fn.newBasicBlock("unreachable")
case *ast.BranchStmt:
@@ -2248,18 +2300,18 @@
// We set Function.Params even though there is no body
// code to reference them. This simplifies clients.
- if recv := fn.Signature.Recv; recv != nil {
- fn.addParam(recv.Name, recv.Type)
+ if recv := fn.Signature.Recv(); recv != nil {
+ fn.addParam(recv.Name(), recv.Type())
}
- for _, param := range fn.Signature.Params {
- fn.addParam(param.Name, param.Type)
- }
+ fn.Signature.Params().ForEach(func(p *types.Var) {
+ fn.addParam(p.Name(), p.Type())
+ })
}
return
}
if fn.Prog.mode&LogSource != 0 {
defer logStack("build function %s @ %s",
- fn.FullName(), fn.Prog.Files.Position(fn.Pos))()
+ fn.FullName(), fn.Prog.Files.Position(fn.pos))()
}
fn.startBody()
fn.createSyntacticParams(fn.Pkg.idents)
@@ -2281,16 +2333,16 @@
// phase.
//
func (b *Builder) memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
- name := obj.GetName()
+ name := obj.Name()
switch obj := obj.(type) {
case *types.TypeName:
- pkg.Members[name] = &Type{NamedType: obj.Type.(*types.NamedType)}
+ pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)}
case *types.Const:
pkg.Members[name] = &Constant{
Name_: name,
- Value: newLiteral(obj.Val, obj.Type),
- Pos: obj.GetPos(),
+ Value: newLiteral(obj.Val(), obj.Type()),
+ pos: obj.Pos(),
}
case *types.Var:
@@ -2298,8 +2350,8 @@
g := &Global{
Pkg: pkg,
Name_: name,
- Type_: pointer(obj.Type), // address
- Pos: obj.GetPos(),
+ Type_: pointer(obj.Type()), // address
+ pos: obj.Pos(),
spec: spec,
}
b.globals[obj] = g
@@ -2307,7 +2359,6 @@
case *types.Func:
var fs *funcSyntax
- var pos token.Pos
if decl, ok := syntax.(*ast.FuncDecl); ok {
fs = &funcSyntax{
recvField: decl.Recv,
@@ -2315,28 +2366,24 @@
resultFields: decl.Type.Results,
body: decl.Body,
}
- // TODO(gri): make GcImported types.Object
- // implement the full object interface
- // including Pos(). Or at least not crash.
- pos = obj.GetPos()
}
- sig := obj.Type.(*types.Signature)
+ sig := obj.Type().(*types.Signature)
fn := &Function{
Name_: name,
Signature: sig,
- Pos: pos,
+ pos: obj.Pos(), // (iff syntax)
Pkg: pkg,
Prog: b.Prog,
syntax: fs,
}
- if sig.Recv == nil {
+ if sig.Recv() == nil {
// Function declaration.
b.globals[obj] = fn
pkg.Members[name] = fn
} else {
// Method declaration.
- nt := deref(sig.Recv.Type).(*types.NamedType)
- _, method := methodIndex(nt, nt.Methods, makeId(name, pkg.Types))
+ nt := sig.Recv().Type().Deref().(*types.Named)
+ _, method := methodIndex(nt, MakeId(name, pkg.Types))
b.Prog.concreteMethods[method] = fn
}
@@ -2383,8 +2430,8 @@
case *ast.FuncDecl:
id := decl.Name
if decl.Recv == nil && id.Name == "init" {
- if !pkg.Init.Pos.IsValid() {
- pkg.Init.Pos = decl.Name.Pos()
+ if !pkg.Init.pos.IsValid() {
+ pkg.Init.pos = decl.Name.Pos()
}
return // init blocks aren't functions
}
@@ -2397,7 +2444,7 @@
// typecheck invokes the type-checker on files and returns the
// type-checker's package so formed, plus the AST type information.
//
-func (b *Builder) typecheck(files []*ast.File) (*types.Package, *TypeInfo, error) {
+func (b *Builder) typecheck(importPath string, files []*ast.File) (*types.Package, *TypeInfo, error) {
info := &TypeInfo{
types: make(map[ast.Expr]types.Type),
idents: make(map[*ast.Ident]types.Object),
@@ -2416,7 +2463,7 @@
// - isBlankIdent(ident) <=> obj.GetType()==nil
info.idents[ident] = obj
}
- typkg, firstErr := tc.Check(b.Prog.Files, files)
+ typkg, firstErr := tc.Check(importPath, b.Prog.Files, files...)
tc.Expr = nil
tc.Ident = nil
if firstErr != nil {
@@ -2437,7 +2484,7 @@
// source files.
//
func (b *Builder) CreatePackage(importPath string, files []*ast.File) (*Package, error) {
- typkg, info, err := b.typecheck(files)
+ typkg, info, err := b.typecheck(importPath, files)
if err != nil {
return nil, err
}
@@ -2458,15 +2505,6 @@
// from the gc compiler's object files; no code will be available.
//
func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, files []*ast.File, info *TypeInfo) *Package {
- // The typechecker sets types.Package.Path only for GcImported
- // packages, since it doesn't know import path until after typechecking is done.
- // Here we ensure it is always set, since we know the correct path.
- if typkg.Path == "" {
- typkg.Path = importPath
- } else if typkg.Path != importPath {
- panic(fmt.Sprintf("%s != %s", typkg.Path, importPath))
- }
-
p := &Package{
Prog: b.Prog,
Types: typkg,
@@ -2507,7 +2545,7 @@
// No code.
// No position information.
- for _, obj := range p.Types.Scope.Entries {
+ for _, obj := range p.Types.Scope().Entries {
b.memberFromObject(p, obj, nil)
}
}
@@ -2555,9 +2593,10 @@
continue
}
obj := pkg.ObjectOf(id).(*types.TypeName)
- for _, method := range obj.Type.(*types.NamedType).Methods {
- b.buildFunction(b.Prog.concreteMethods[method])
- }
+ nt := obj.Type().(*types.Named)
+ nt.ForEachMethod(func(m *types.Func) {
+ b.buildFunction(b.Prog.concreteMethods[m])
+ })
}
}
@@ -2645,32 +2684,35 @@
init.currentBlock = doinit
emitStore(init, initguard, vTrue)
- // TODO(gri): fix: the types.Package.Imports map may contains
- // entries for other package's import statements, if produced
- // by GcImport. Project it down to just the ones for us.
- imports := make(map[string]*types.Package)
+ // Call the init() function of each package we import.
+ // We iterate over the syntax (p.Files.Imports) not the types
+ // (p.Types.Imports()) because the latter may contain the
+ // transitive closure of dependencies,
+ // e.g. when using GcImporter.
+ seen := make(map[*types.Package]bool)
for _, file := range p.Files {
for _, imp := range file.Imports {
path, _ := strconv.Unquote(imp.Path.Value)
- if path != "unsafe" {
- imports[path] = p.Types.Imports[path]
+ if path == "unsafe" {
+ continue
}
- }
- }
+ typkg := p.Types.Imports()[path]
+ if seen[typkg] {
+ continue
+ }
+ seen[typkg] = true
- // Call the init() function of each package we import.
- // Order is unspecified (and is in fact nondeterministic).
- for name, imported := range imports {
- p2 := b.packages[imported]
- if p2 == nil {
- panic("Building " + p.Name() + ": CreatePackage has not been called for package " + name)
- }
+ p2 := b.packages[typkg]
+ if p2 == nil {
+ panic("Building " + p.Name() + ": CreatePackage has not been called for package " + path)
+ }
- var v Call
- v.Call.Func = p2.Init
- v.Call.Pos = init.Pos
- v.setType(new(types.Result))
- init.emit(&v)
+ var v Call
+ v.Call.Func = p2.Init
+ v.Call.pos = init.pos
+ v.setType(types.NewTuple())
+ init.emit(&v)
+ }
}
// Visit the package's var decls and init funcs in source
diff --git a/ssa/doc.go b/ssa/doc.go
index 11f93c5..1ebbedc 100644
--- a/ssa/doc.go
+++ b/ssa/doc.go
@@ -27,8 +27,9 @@
// By supplying an instance of the SourceLocator function prototype,
// clients may control how the builder locates, loads and parses Go
// sources files for imported packages. This package provides
-// GorootLoader, which uses go/build to locate packages in the Go
-// source distribution, and go/parser to parse them.
+// MakeGoBuildLoader, which creates a loader that uses go/build to
+// locate packages in the Go source distribution, and go/parser to
+// parse them.
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
@@ -38,6 +39,61 @@
// subsequent analyses; this pass can be skipped by setting the
// NaiveForm builder flag.
//
+// The primary interfaces of this package are:
+//
+// - Member: a named member of a Go package.
+// - Value: an expression that yields a value.
+// - Instruction: a statement that consumes values and performs computation.
+//
+// A computation that yields a result implements both the Value and
+// Instruction interfaces. The following table shows for each
+// concrete type which of these interfaces it implements.
+//
+// Value? Instruction? Member?
+// *Alloc ✔ ✔
+// *BinOp ✔ ✔
+// *Builtin ✔ ✔
+// *Call ✔ ✔
+// *Capture ✔
+// *ChangeInterface ✔ ✔
+// *ChangeType ✔ ✔
+// *Constant ✔ (const)
+// *Convert ✔ ✔
+// *Defer ✔
+// *Extract ✔ ✔
+// *Field ✔ ✔
+// *FieldAddr ✔ ✔
+// *Function ✔ ✔ (func)
+// *Global ✔ ✔ (var)
+// *Go ✔
+// *If ✔
+// *Index ✔ ✔
+// *IndexAddr ✔ ✔
+// *Jump ✔
+// *Literal ✔
+// *Lookup ✔ ✔
+// *MakeChan ✔ ✔
+// *MakeClosure ✔ ✔
+// *MakeInterface ✔ ✔
+// *MakeMap ✔ ✔
+// *MakeSlice ✔ ✔
+// *MapUpdate ✔
+// *Next ✔ ✔
+// *Panic ✔
+// *Parameter ✔
+// *Phi ✔ ✔
+// *Range ✔ ✔
+// *Ret ✔
+// *RunDefers ✔
+// *Select ✔ ✔
+// *Slice ✔ ✔
+// *Type ✔ (type)
+// *TypeAssert ✔ ✔
+// *UnOp ✔ ✔
+//
+// Other key types in this package include: Program, Package, Function
+// and BasicBlock.
+//
// The program representation constructed by this package is fully
// resolved internally, i.e. it does not rely on the names of Values,
// Packages, Functions, Types or BasicBlocks for the correct
@@ -59,32 +115,31 @@
//
// const message = "Hello, World!"
//
-// func hello() {
+// func main() {
// fmt.Println(message)
// }
//
// The SSA Builder creates a *Program containing a main *Package such
// as this:
//
-// Package(Name: "main")
+// Package (Name: "main")
// Members:
-// "message": *Literal (Type: untyped string, Value: "Hello, World!")
+// "message": *Constant (Type: untyped string, Value: "Hello, World!")
// "init·guard": *Global (Type: *bool)
-// "hello": *Function (Type: func())
+// "main": *Function (Type: func())
// Init: *Function (Type: func())
//
-// The printed representation of the function main.hello is shown
+// The printed representation of the function main.main is shown
// below. Within the function listing, the name of each BasicBlock
// such as ".0.entry" is printed left-aligned, followed by the block's
-// instructions, i.e. implementations of Instruction.
+// Instructions.
// For each instruction that defines an SSA virtual register
// (i.e. implements Value), the type of that value is shown in the
// right column.
//
-// # Name: main.hello
+// # Name: main.main
// # Declared at hello.go:7:6
-// # Type: func()
-// func hello():
+// func main():
// .0.entry:
// t0 = new [1]interface{} *[1]interface{}
// t1 = &t0[0:untyped integer] *interface{}
@@ -102,14 +157,21 @@
// TODO(adonovan): demonstrate more features in the example:
// parameters and control flow at the least.
//
-// TODO(adonovan): Consider how token.Pos source location information
-// should be made available generally. Currently it is only present in
-// Package, Function and CallCommon.
-//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
-// TODO(adonovan): build tables/functions that relate source variables
-// to SSA variables to assist user interfaces that make queries about
-// specific source entities.
+// TODO(adonovan): Consider how token.Pos source location information
+// should be made available generally. Currently it is only present
+// in package Members and selected Instructions for which there is a
+// direct source correspondence. We'll need to work harder to tie all
+// defs/uses of named variables together, esp. because SSA splits them
+// into separate webs.
+//
+// TODO(adonovan): it is practically impossible for clients to
+// construct well-formed SSA functions/packages/programs directly; we
+// assume this is the job of the ssa.Builder alone.
+// Nonetheless it may be wise to give clients a little more
+// flexibility. For example, analysis tools may wish to construct a
+// fake ssa.Function for the root of the callgraph, a fake "reflect"
+// package, etc.
package ssa
diff --git a/ssa/emit.go b/ssa/emit.go
index 7b15288..786ff67 100644
--- a/ssa/emit.go
+++ b/ssa/emit.go
@@ -15,7 +15,7 @@
return f.emit(&Alloc{
Type_: pointer(typ),
Heap: true,
- Pos: pos,
+ pos: pos,
})
}
@@ -24,7 +24,7 @@
//
func emitLoad(f *Function, addr Value) Value {
v := &UnOp{Op: token.MUL, X: addr}
- v.setType(indirectType(addr.Type()))
+ v.setType(addr.Type().Deref())
return f.emit(v)
}
@@ -60,8 +60,8 @@
// comparison comparison 'x op y'.
//
func emitCompare(f *Function, op token.Token, x, y Value) Value {
- xt := underlyingType(x.Type())
- yt := underlyingType(y.Type())
+ xt := x.Type().Underlying()
+ yt := y.Type().Underlying()
// Special case to optimise a tagless SwitchStmt so that
// these are equivalent
@@ -71,7 +71,7 @@
// even in the case when e's type is an interface.
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
- if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 {
+ if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
return y
}
}
@@ -99,38 +99,55 @@
return f.emit(v)
}
-// emitConv emits to f code to convert Value val to exactly type typ,
-// and returns the converted value. Implicit conversions are implied
-// by language assignability rules in the following operations:
+// isValuePreserving returns true if a conversion from ut_src to
+// ut_dst is value-preserving, i.e. just a change of type.
+// Precondition: neither argument is a named type.
//
-// - from rvalue type to lvalue type in assignments.
-// - from actual- to formal-parameter types in function calls.
-// - from return value type to result type in return statements.
-// - population of struct fields, array and slice elements, and map
-// keys and values within compoisite literals
-// - from index value to index type in indexing expressions.
-// - for both arguments of comparisons.
-// - from value type to channel type in send expressions.
+func isValuePreserving(ut_src, ut_dst types.Type) bool {
+ // Identical underlying types?
+ if types.IsIdentical(ut_dst, ut_src) {
+ return true
+ }
+
+ switch ut_dst.(type) {
+ case *types.Chan:
+ // Conversion between channel types?
+ _, ok := ut_src.(*types.Chan)
+ return ok
+
+ case *types.Pointer:
+ // Conversion between pointers with identical base types?
+ _, ok := ut_src.(*types.Pointer)
+ return ok
+
+ case *types.Signature:
+ // Conversion between f(T) function and (T) func f() method?
+ // TODO(adonovan): is this sound? Discuss with gri.
+ _, ok := ut_src.(*types.Signature)
+ return ok
+ }
+ return false
+}
+
+// emitConv emits to f code to convert Value val to exactly type typ,
+// and returns the converted value. Implicit conversions are required
+// by language assignability rules in assignments, parameter passing,
+// etc.
//
func emitConv(f *Function, val Value, typ types.Type) Value {
- // fmt.Printf("emitConv %s -> %s, %T", val.Type(), typ, val) // debugging
+ t_src := val.Type()
// Identical types? Conversion is a no-op.
- if types.IsIdentical(val.Type(), typ) {
+ if types.IsIdentical(t_src, typ) {
return val
}
- ut_dst := underlyingType(typ)
- ut_src := underlyingType(val.Type())
+ ut_dst := typ.Underlying()
+ ut_src := t_src.Underlying()
- // Identical underlying types? Conversion is a name change.
- if types.IsIdentical(ut_dst, ut_src) {
- // TODO(adonovan): make this use a distinct
- // instruction, ChangeType. This instruction must
- // also cover the cases of channel type restrictions and
- // conversions between pointers to identical base
- // types.
- c := &Conv{X: val}
+ // Just a change of type, but not value or representation?
+ if isValuePreserving(ut_src, ut_dst) {
+ c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
}
@@ -150,13 +167,13 @@
// Convert (non-nil) "untyped" literals to their default type.
// TODO(gri): expose types.isUntyped().
- if t, ok := ut_src.(*types.Basic); ok && t.Info&types.IsUntyped != 0 {
+ if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
val = emitConv(f, val, DefaultType(ut_src))
}
mi := &MakeInterface{
X: val,
- Methods: f.Prog.MethodSet(val.Type()),
+ Methods: f.Prog.MethodSet(t_src),
}
mi.setType(typ)
return f.emit(mi)
@@ -172,7 +189,7 @@
}
// A representation-changing conversion.
- c := &Conv{X: val}
+ c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
}
@@ -183,7 +200,7 @@
func emitStore(f *Function, addr, val Value) {
f.emit(&Store{
Addr: addr,
- Val: emitConv(f, val, indirectType(addr.Type())),
+ Val: emitConv(f, val, addr.Type().Deref()),
})
}
@@ -226,8 +243,8 @@
//
func emitTypeAssert(f *Function, x Value, t types.Type) Value {
// Simplify infallible assertions.
- txi := underlyingType(x.Type()).(*types.Interface)
- if ti, ok := underlyingType(t).(*types.Interface); ok {
+ txi := x.Type().Underlying().(*types.Interface)
+ if ti, ok := t.Underlying().(*types.Interface); ok {
if types.IsIdentical(ti, txi) {
return x
}
@@ -256,10 +273,10 @@
AssertedType: t,
CommaOk: true,
}
- a.setType(&types.Result{Values: []*types.Var{
- {Name: "value", Type: t},
+ a.setType(types.NewTuple(
+ types.NewVar(nil, "value", t),
varOk,
- }})
+ ))
return f.emit(a)
}
@@ -273,11 +290,12 @@
for _, arg := range f.Params[1:] {
call.Call.Args = append(call.Call.Args, arg)
}
- nr := len(f.Signature.Results)
+ tresults := f.Signature.Results()
+ nr := tresults.Len()
if nr == 1 {
- call.Type_ = f.Signature.Results[0].Type
+ call.Type_ = tresults.At(0).Type()
} else {
- call.Type_ = &types.Result{Values: f.Signature.Results}
+ call.Type_ = tresults
}
tuple := f.emit(call)
var ret Ret
@@ -287,8 +305,8 @@
case 1:
ret.Results = []Value{tuple}
default:
- for i, o := range call.Type().(*types.Result).Values {
- v := emitExtract(f, tuple, i, o.Type)
+ for i := 0; i < nr; i++ {
+ v := emitExtract(f, tuple, i, tresults.At(i).Type())
// TODO(adonovan): in principle, this is required:
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
// but in practice emitTailCall is only used when
diff --git a/ssa/example_test.go b/ssa/example_test.go
new file mode 100644
index 0000000..a7c6629
--- /dev/null
+++ b/ssa/example_test.go
@@ -0,0 +1,62 @@
+package ssa_test
+
+import (
+ "code.google.com/p/go.tools/ssa"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "os"
+)
+
+// This example demonstrates the SSA builder.
+func Example() {
+ const hello = `
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, World!")
+}
+`
+
+ // Construct a builder. Imports will be loaded as if by 'go build'.
+ builder := ssa.NewBuilder(&ssa.Context{Loader: ssa.MakeGoBuildLoader(nil)})
+
+ // Parse the input file.
+ file, err := parser.ParseFile(builder.Prog.Files, "hello.go", hello, parser.DeclarationErrors)
+ if err != nil {
+ fmt.Printf("Parsing failed: %s\n", err.Error())
+ return
+ }
+
+ // Create a "main" package containing one file.
+ mainPkg, err := builder.CreatePackage("main", []*ast.File{file})
+ if err != nil {
+ fmt.Printf("Type-checking failed: %s\n", err.Error())
+ return
+ }
+
+ // Build SSA code for bodies of functions in mainPkg.
+ builder.BuildPackage(mainPkg)
+
+ // Print out the package-level functions.
+ for _, mem := range mainPkg.Members {
+ if fn, ok := mem.(*ssa.Function); ok {
+ fn.DumpTo(os.Stdout)
+ }
+ }
+
+ // Output:
+ // # Name: main.main
+ // # Declared at hello.go:6:6
+ // func main():
+ // .0.entry: P:0 S:0
+ // a0 = new [1]interface{} *[1]interface{}
+ // t0 = &a0[0:untyped integer] *interface{}
+ // t1 = make interface interface{} <- string ("Hello, World!":string) interface{}
+ // *t0 = t1
+ // t2 = slice a0[:] []interface{}
+ // t3 = fmt.Println(t2) (n int, err error)
+ // ret
+}
diff --git a/ssa/func.go b/ssa/func.go
index 4b93cf7..d16dd11 100644
--- a/ssa/func.go
+++ b/ssa/func.go
@@ -179,11 +179,12 @@
// Subsequent lifting will eliminate spills where possible.
//
func (f *Function) addSpilledParam(obj types.Object) {
- name := obj.GetName()
- param := f.addParam(name, obj.GetType())
+ name := obj.Name()
+ param := f.addParam(name, obj.Type())
spill := &Alloc{
Name_: name + "~", // "~" means "spilled"
- Type_: pointer(obj.GetType()),
+ Type_: pointer(obj.Type()),
+ pos: obj.Pos(),
}
f.objects[obj] = spill
f.Locals = append(f.Locals, spill)
@@ -210,7 +211,7 @@
// f.syntax != nil, i.e. this is a Go source function.
// f.startBody() was called.
// Postcondition:
-// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv ? 1 : 0)
+// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
//
func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
// Receiver (at most one inner iteration).
@@ -221,8 +222,8 @@
}
// Anonymous receiver? No need to spill.
if field.Names == nil {
- recvVar := f.Signature.Recv
- f.addParam(recvVar.Name, recvVar.Type)
+ recvVar := f.Signature.Recv()
+ f.addParam(recvVar.Name(), recvVar.Type())
}
}
}
@@ -236,8 +237,8 @@
}
// Anonymous parameter? No need to spill.
if field.Names == nil {
- paramVar := f.Signature.Params[len(f.Params)-n]
- f.addParam(paramVar.Name, paramVar.Type)
+ paramVar := f.Signature.Params().At(len(f.Params) - n)
+ f.addParam(paramVar.Name(), paramVar.Type())
}
}
}
@@ -371,8 +372,8 @@
// Precondition: f.syntax != nil (i.e. a Go source function).
//
func (f *Function) addNamedLocal(obj types.Object) *Alloc {
- l := f.addLocal(obj.GetType(), obj.GetPos())
- l.Name_ = obj.GetName()
+ l := f.addLocal(obj.Type(), obj.Pos())
+ l.Name_ = obj.Name()
f.objects[obj] = l
return l
}
@@ -381,7 +382,7 @@
// to function f and returns it. pos is the optional source location.
//
func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
- v := &Alloc{Type_: pointer(typ), Pos: pos}
+ v := &Alloc{Type_: pointer(typ), pos: pos}
f.Locals = append(f.Locals, v)
f.emit(v)
return v
@@ -414,7 +415,7 @@
// Definition must be in an enclosing function;
// plumb it through intervening closures.
if f.Enclosing == nil {
- panic("no Value for type.Object " + obj.GetName())
+ panic("no Value for type.Object " + obj.Name())
}
v := &Capture{Outer: f.Enclosing.lookup(obj, true)} // escaping
f.objects[obj] = v
@@ -453,13 +454,13 @@
return f.Name_
}
- recv := f.Signature.Recv
+ recv := f.Signature.Recv()
// Synthetic?
if f.Pkg == nil {
var recvType types.Type
if recv != nil {
- recvType = recv.Type // bridge method
+ recvType = recv.Type() // bridge method
} else {
recvType = f.Params[0].Type() // interface method thunk
}
@@ -468,13 +469,13 @@
// Declared method?
if recv != nil {
- return fmt.Sprintf("(%s).%s", recv.Type, f.Name_)
+ return fmt.Sprintf("(%s).%s", recv.Type(), f.Name_)
}
// Package-level function.
// Prefix with package name for cross-package references only.
if from != f.Pkg {
- return fmt.Sprintf("%s.%s", f.Pkg.Types.Path, f.Name_)
+ return fmt.Sprintf("%s.%s", f.Pkg.Types.Path(), f.Name_)
}
return f.Name_
}
@@ -484,7 +485,7 @@
//
func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Parameter) {
io.WriteString(w, "func ")
- if sig.Recv != nil {
+ if recv := sig.Recv(); recv != nil {
io.WriteString(w, "(")
if n := params[0].Name(); n != "" {
io.WriteString(w, n)
@@ -502,23 +503,22 @@
}
io.WriteString(w, v.Name())
io.WriteString(w, " ")
- if sig.IsVariadic && i == len(params)-1 {
+ if sig.IsVariadic() && i == len(params)-1 {
io.WriteString(w, "...")
- io.WriteString(w, underlyingType(v.Type()).(*types.Slice).Elt.String())
+ io.WriteString(w, v.Type().Underlying().(*types.Slice).Elem().String())
} else {
io.WriteString(w, v.Type().String())
}
}
io.WriteString(w, ")")
- if res := sig.Results; res != nil {
+ if n := sig.Results().Len(); n > 0 {
io.WriteString(w, " ")
- var t types.Type
- if len(res) == 1 && res[0].Name == "" {
- t = res[0].Type
+ r := sig.Results()
+ if n == 1 && r.At(0).Name() == "" {
+ io.WriteString(w, r.At(0).Type().String())
} else {
- t = &types.Result{Values: res}
+ io.WriteString(w, r.String())
}
- io.WriteString(w, t.String())
}
}
@@ -527,7 +527,7 @@
//
func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Name: %s\n", f.FullName())
- fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos))
+ fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos()))
if f.Enclosing != nil {
fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name())
@@ -543,7 +543,7 @@
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(), indirectType(l.Type()))
+ fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), l.Type().Deref())
}
}
diff --git a/ssa/importer.go b/ssa/importer.go
index d6b0106..0e1ebc2 100644
--- a/ssa/importer.go
+++ b/ssa/importer.go
@@ -53,7 +53,7 @@
} else {
files, err = b.Context.Loader(b.Prog.Files, path)
if err == nil {
- typkg, info, err = b.typecheck(files)
+ typkg, info, err = b.typecheck(path, files)
}
}
if err != nil {
@@ -69,28 +69,35 @@
return typkg, nil
}
-// GorootLoader is an implementation of the SourceLoader function
-// prototype that loads and parses Go source files from the package
-// directory beneath $GOROOT/src/pkg.
+// MakeGoBuildLoader returns an implementation of the SourceLoader
+// function prototype that locates packages using the go/build
+// libraries. It may return nil upon gross misconfiguration
+// (e.g. os.Getwd() failed).
//
-// TODO(adonovan): get rsc and adg (go/build owners) to review this.
-// TODO(adonovan): permit clients to specify a non-default go/build.Context.
+// ctxt specifies the go/build.Context to use; if nil, the default
+// Context is used.
//
-func GorootLoader(fset *token.FileSet, path string) (files []*ast.File, err error) {
- // TODO(adonovan): fix: Do we need cwd? Shouldn't ImportDir(path) / $GOROOT suffice?
+func MakeGoBuildLoader(ctxt *build.Context) SourceLoader {
srcDir, err := os.Getwd()
if err != nil {
- return // serious misconfiguration
+ return nil // serious misconfiguration
}
- bp, err := build.Import(path, srcDir, 0)
- if err != nil {
- return // import failed
+ if ctxt == nil {
+ ctxt = &build.Default
}
- files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
- if err != nil {
- return nil, err
+ return func(fset *token.FileSet, path string) (files []*ast.File, err error) {
+ // TODO(adonovan): fix: Do we need cwd? Shouldn't
+ // ImportDir(path) / $GOROOT suffice?
+ bp, err := ctxt.Import(path, srcDir, 0)
+ if err != nil {
+ return // import failed
+ }
+ files, err = ParseFiles(fset, bp.Dir, bp.GoFiles...)
+ if err != nil {
+ return nil, err
+ }
+ return
}
- return
}
// ParseFiles parses the Go source files files within directory dir
diff --git a/ssa/interp/interp.go b/ssa/interp/interp.go
index ad6db3a..c428237 100644
--- a/ssa/interp/interp.go
+++ b/ssa/interp/interp.go
@@ -158,14 +158,17 @@
case *ssa.Call:
fn, args := prepareCall(fr, &instr.Call)
- fr.env[instr] = call(fr.i, fr, instr.Call.Pos, fn, args)
-
- case *ssa.Conv:
- fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
+ fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args)
case *ssa.ChangeInterface:
fr.env[instr] = fr.get(instr.X) // (can't fail)
+ case *ssa.ChangeType:
+ fr.env[instr] = fr.get(instr.X) // (can't fail)
+
+ case *ssa.Convert:
+ fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))
+
case *ssa.MakeInterface:
fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}
@@ -214,13 +217,13 @@
return kJump
case *ssa.Defer:
- pos := instr.Call.Pos // TODO(gri): workaround for go/types bug in typeswitch+funclit.
+ pos := instr.Pos() // TODO(gri): workaround for go/types bug in typeswitch+funclit.
fn, args := prepareCall(fr, &instr.Call)
fr.defers = append(fr.defers, func() { call(fr.i, fr, pos, fn, args) })
case *ssa.Go:
fn, args := prepareCall(fr, &instr.Call)
- go call(fr.i, nil, instr.Call.Pos, fn, args)
+ go call(fr.i, nil, instr.Pos(), fn, args)
case *ssa.MakeChan:
fr.env[instr] = make(chan value, asInt(fr.get(instr.Size)))
@@ -235,11 +238,11 @@
// local
addr = fr.env[instr].(*value)
}
- *addr = zero(indirectType(instr.Type()))
+ *addr = zero(instr.Type().Deref())
case *ssa.MakeSlice:
slice := make([]value, asInt(fr.get(instr.Cap)))
- tElt := underlyingType(instr.Type()).(*types.Slice).Elt
+ tElt := instr.Type().Underlying().(*types.Slice).Elem()
for i := range slice {
slice[i] = zero(tElt)
}
@@ -250,7 +253,7 @@
if instr.Reserve != nil {
reserve = asInt(fr.get(instr.Reserve))
}
- fr.env[instr] = makeMap(underlyingType(instr.Type()).(*types.Map).Key, reserve)
+ fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)
case *ssa.Range:
fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type())
@@ -344,7 +347,7 @@
}
var recvV iface
if chosen != -1 {
- recvV.t = underlyingType(instr.States[chosen].Chan.Type()).(*types.Chan).Elt
+ recvV.t = instr.States[chosen].Chan.Type().Underlying().(*types.Chan).Elem()
if recvOk {
// No need to copy since send makes an unaliased copy.
recvV.v = recv.Interface().(value)
@@ -385,8 +388,8 @@
// Unreachable in well-typed programs.
panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id))
}
- _, aptr := recv.v.(*value) // actual pointerness
- _, fptr := m.Signature.Recv.Type.(*types.Pointer) // formal pointerness
+ _, aptr := recv.v.(*value) // actual pointerness
+ _, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness
switch {
case aptr == fptr:
args = append(args, copyVal(recv.v))
@@ -438,7 +441,7 @@
if i.mode&EnableTracing != 0 {
fset := fn.Prog.Files
// TODO(adonovan): fix: loc() lies for external functions.
- fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos))
+ fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn.FullName(), loc(fset, fn.Pos()))
suffix := ""
if caller != nil {
suffix = ", resuming " + caller.fn.FullName() + loc(fset, callpos)
@@ -466,7 +469,7 @@
locals: make([]value, len(fn.Locals)),
}
for i, l := range fn.Locals {
- fr.locals[i] = zero(indirectType(l.Type()))
+ fr.locals[i] = zero(l.Type().Deref())
fr.env[l] = &fr.locals[i]
}
for i, p := range fn.Params {
@@ -550,7 +553,7 @@
for _, m := range pkg.Members {
switch v := m.(type) {
case *ssa.Global:
- cell := zero(indirectType(v.Type()))
+ cell := zero(v.Type().Deref())
i.globals[v] = &cell
}
}
diff --git a/ssa/interp/interp_test.go b/ssa/interp/interp_test.go
index 6783816..3b9c3f8 100644
--- a/ssa/interp/interp_test.go
+++ b/ssa/interp/interp_test.go
@@ -140,7 +140,10 @@
inputs = append(inputs, dir+i)
}
- b := ssa.NewBuilder(&ssa.Context{Mode: ssa.SanityCheckFunctions, Loader: ssa.GorootLoader})
+ b := ssa.NewBuilder(&ssa.Context{
+ Mode: ssa.SanityCheckFunctions,
+ Loader: ssa.MakeGoBuildLoader(nil),
+ })
files, err := ssa.ParseFiles(b.Prog.Files, ".", inputs...)
if err != nil {
t.Errorf("ssa.ParseFiles(%s) failed: %s", inputs, err.Error())
diff --git a/ssa/interp/ops.go b/ssa/interp/ops.go
index d0b31af..49c8b84 100644
--- a/ssa/interp/ops.go
+++ b/ssa/interp/ops.go
@@ -3,7 +3,6 @@
import (
"fmt"
"go/token"
- "os"
"runtime"
"strings"
"unsafe"
@@ -29,9 +28,9 @@
}
// By destination type:
- switch t := underlyingType(l.Type()).(type) {
+ switch t := l.Type().Underlying().(type) {
case *types.Basic:
- switch t.Kind {
+ switch t.Kind() {
case types.Bool, types.UntypedBool:
return exact.BoolVal(l.Value)
case types.Int, types.UntypedInt:
@@ -79,9 +78,9 @@
}
case *types.Slice:
- switch et := underlyingType(t.Elt).(type) {
+ switch et := t.Elem().Underlying().(type) {
case *types.Basic:
- switch et.Kind {
+ switch et.Kind() {
case types.Byte: // string -> []byte
var v []value
for _, b := range []byte(exact.StringVal(l.Value)) {
@@ -155,13 +154,13 @@
func zero(t types.Type) value {
switch t := t.(type) {
case *types.Basic:
- if t.Kind == types.UntypedNil {
+ if t.Kind() == types.UntypedNil {
panic("untyped nil has no zero value")
}
- if t.Info&types.IsUntyped != 0 {
+ if t.Info()&types.IsUntyped != 0 {
t = ssa.DefaultType(t).(*types.Basic)
}
- switch t.Kind {
+ switch t.Kind() {
case types.Bool:
return false
case types.Int:
@@ -204,27 +203,27 @@
case *types.Pointer:
return (*value)(nil)
case *types.Array:
- a := make(array, t.Len)
+ a := make(array, t.Len())
for i := range a {
- a[i] = zero(t.Elt)
+ a[i] = zero(t.Elem())
}
return a
- case *types.NamedType:
- return zero(t.Underlying)
+ case *types.Named:
+ return zero(t.Underlying())
case *types.Interface:
return iface{} // nil type, methodset and value
case *types.Slice:
return []value(nil)
case *types.Struct:
- s := make(structure, len(t.Fields))
+ s := make(structure, t.NumFields())
for i := range s {
- s[i] = zero(t.Fields[i].Type)
+ s[i] = zero(t.Field(i).Type)
}
return s
case *types.Chan:
return chan value(nil)
case *types.Map:
- if usesBuiltinMap(t.Key) {
+ if usesBuiltinMap(t.Key()) {
return map[value]value(nil)
}
return (*hashmap)(nil)
@@ -277,7 +276,7 @@
if ok {
v = copyVal(v)
} else {
- v = zero(underlyingType(instr.X.Type()).(*types.Map).Elt)
+ v = zero(instr.X.Type().Underlying().(*types.Map).Elem())
}
if instr.CommaOk {
v = tuple{v, ok}
@@ -759,7 +758,7 @@
case token.ARROW: // receive
v, ok := <-x.(chan value)
if !ok {
- v = zero(underlyingType(instr.X.Type()).(*types.Chan).Elt)
+ v = zero(instr.X.Type().Underlying().(*types.Chan).Elem())
}
if instr.CommaOk {
v = tuple{v, ok}
@@ -834,7 +833,7 @@
func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
var v value
err := ""
- if idst, ok := underlyingType(instr.AssertedType).(*types.Interface); ok {
+ if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
v = itf
err = checkInterface(i, idst, itf)
@@ -1089,31 +1088,24 @@
}
// conv converts the value x of type t_src to type t_dst and returns
-// the result. Possible cases are described with the ssa.Conv
-// operator. Panics if the dynamic conversion fails.
+// the result.
+// Possible cases are described with the ssa.Convert operator.
//
func conv(t_dst, t_src types.Type, x value) value {
- ut_src := underlyingType(t_src)
- ut_dst := underlyingType(t_dst)
-
- // Same underlying types?
- // TODO(adonovan): consider a dedicated ssa.ChangeType instruction.
- // TODO(adonovan): fix: what about channels of different direction?
- if types.IsIdentical(ut_dst, ut_src) {
- return x
- }
+ ut_src := t_src.Underlying()
+ ut_dst := t_dst.Underlying()
// Destination type is not an "untyped" type.
- if b, ok := ut_dst.(*types.Basic); ok && b.Info&types.IsUntyped != 0 {
- panic("conversion to 'untyped' type: " + b.String())
+ if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+ panic("oops: conversion to 'untyped' type: " + b.String())
}
// Nor is it an interface type.
if _, ok := ut_dst.(*types.Interface); ok {
if _, ok := ut_src.(*types.Interface); ok {
- panic("oops: Conv should be ChangeInterface")
+ panic("oops: Convert should be ChangeInterface")
} else {
- panic("oops: Conv should be MakeInterface")
+ panic("oops: Convert should be MakeInterface")
}
}
@@ -1126,34 +1118,23 @@
// + string -> []byte/[]rune.
//
// All are treated the same: first we extract the value to the
- // widest representation (bool, int64, uint64, float64,
- // complex128, or string), then we convert it to the desired
- // type.
+ // widest representation (int64, uint64, float64, complex128,
+ // or string), then we convert it to the desired type.
switch ut_src := ut_src.(type) {
- case *types.Signature:
- // TODO(adonovan): fix: this is a hacky workaround for the
- // unsound conversion of Signature types from
- // func(T)() to func()(T), i.e. arg0 <-> receiver
- // conversion. Talk to gri about correct approach.
- fmt.Fprintln(os.Stderr, "Warning: unsound Signature conversion")
- return x
-
case *types.Pointer:
switch ut_dst := ut_dst.(type) {
case *types.Basic:
// *value to unsafe.Pointer?
- if ut_dst.Kind == types.UnsafePointer {
+ if ut_dst.Kind() == types.UnsafePointer {
return unsafe.Pointer(x.(*value))
}
- case *types.Pointer:
- return x
}
case *types.Slice:
// []byte or []rune -> string
// TODO(adonovan): fix: type B byte; conv([]B -> string).
- switch ut_src.Elt.(*types.Basic).Kind {
+ switch ut_src.Elem().(*types.Basic).Kind() {
case types.Byte:
x := x.([]value)
b := make([]byte, 0, len(x))
@@ -1174,15 +1155,10 @@
case *types.Basic:
x = widen(x)
- // bool?
- if _, ok := x.(bool); ok {
- return x
- }
-
// integer -> string?
// TODO(adonovan): fix: test integer -> named alias of string.
- if ut_src.Info&types.IsInteger != 0 {
- if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind == types.String {
+ if ut_src.Info()&types.IsInteger != 0 {
+ if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String {
return string(asInt(x))
}
}
@@ -1193,7 +1169,7 @@
case *types.Slice:
var res []value
// TODO(adonovan): fix: test named alias of rune, byte.
- switch ut_dst.Elt.(*types.Basic).Kind {
+ switch ut_dst.Elem().(*types.Basic).Kind() {
case types.Rune:
for _, r := range []rune(s) {
res = append(res, r)
@@ -1206,7 +1182,7 @@
return res
}
case *types.Basic:
- if ut_dst.Kind == types.String {
+ if ut_dst.Kind() == types.String {
return x.(string)
}
}
@@ -1214,7 +1190,7 @@
}
// unsafe.Pointer -> *value
- if ut_src.Kind == types.UnsafePointer {
+ if ut_src.Kind() == types.UnsafePointer {
// TODO(adonovan): this is wrong and cannot
// really be fixed with the current design.
//
@@ -1230,8 +1206,8 @@
}
// Conversions between complex numeric types?
- if ut_src.Info&types.IsComplex != 0 {
- switch ut_dst.(*types.Basic).Kind {
+ if ut_src.Info()&types.IsComplex != 0 {
+ switch ut_dst.(*types.Basic).Kind() {
case types.Complex64:
return complex64(x.(complex128))
case types.Complex128:
@@ -1241,8 +1217,8 @@
}
// Conversions between non-complex numeric types?
- if ut_src.Info&types.IsNumeric != 0 {
- kind := ut_dst.(*types.Basic).Kind
+ if ut_src.Info()&types.IsNumeric != 0 {
+ kind := ut_dst.(*types.Basic).Kind()
switch x := x.(type) {
case int64: // signed integer -> numeric?
switch kind {
@@ -1344,32 +1320,15 @@
// interface itype.
// On success it returns "", on failure, an error message.
//
-func checkInterface(i *interpreter, itype types.Type, x iface) string {
+func checkInterface(i *interpreter, itype *types.Interface, x iface) string {
mset := findMethodSet(i, x.t)
- for _, m := range underlyingType(itype).(*types.Interface).Methods {
- id := ssa.IdFromQualifiedName(m.QualifiedName)
+ it := itype.Underlying().(*types.Interface)
+ for i, n := 0, it.NumMethods(); i < n; i++ {
+ m := it.Method(i)
+ id := ssa.MakeId(m.Name(), m.Pkg())
if mset[id] == nil {
return fmt.Sprintf("interface conversion: %v is not %v: missing method %v", x.t, itype, id)
}
}
return "" // ok
}
-
-// underlyingType returns the underlying type of typ.
-// Copied from go/types.underlying.
-//
-func underlyingType(typ types.Type) types.Type {
- if typ, ok := typ.(*types.NamedType); ok {
- return typ.Underlying
- }
- return typ
-}
-
-// indirectType(typ) assumes that typ is a pointer type,
-// or named alias thereof, and returns its base type.
-// Panic ensues if it is not a pointer.
-// Copied from exp/ssa.indirectType.
-//
-func indirectType(ptr types.Type) types.Type {
- return underlyingType(ptr).(*types.Pointer).Base
-}
diff --git a/ssa/interp/reflect.go b/ssa/interp/reflect.go
index fd25c39..4c30086 100644
--- a/ssa/interp/reflect.go
+++ b/ssa/interp/reflect.go
@@ -15,19 +15,22 @@
"code.google.com/p/go.tools/ssa"
)
-// A bogus "reflect" type-checker package. Shared across interpreters.
-var reflectTypesPackage = &types.Package{
- Name: "reflect",
- Path: "reflect",
- Complete: true,
+type opaqueType struct {
+ types.Type
+ name string
}
+func (t *opaqueType) String() string { return t.name }
+
+// A bogus "reflect" type-checker package. Shared across interpreters.
+var reflectTypesPackage = types.NewPackage("reflect", "reflect")
+
// rtype is the concrete type the interpreter uses to implement the
// reflect.Type interface. Since its type is opaque to the target
// language, we use a types.Basic.
//
// type rtype <opaque>
-var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"})
+var rtypeType = makeNamedType("rtype", &opaqueType{nil, "rtype"})
// error is an (interpreted) named type whose underlying type is string.
// The interpreter uses it for all implementations of the built-in error
@@ -35,16 +38,11 @@
// We put it in the "reflect" package for expedience.
//
// type error string
-var errorType = makeNamedType("error", &types.Basic{Name: "error"})
+var errorType = makeNamedType("error", &opaqueType{nil, "error"})
-func makeNamedType(name string, underlying types.Type) *types.NamedType {
- nt := &types.NamedType{Underlying: underlying}
- nt.Obj = &types.TypeName{
- Name: name,
- Type: nt,
- Pkg: reflectTypesPackage,
- }
- return nt
+func makeNamedType(name string, underlying types.Type) *types.Named {
+ obj := types.NewTypeName(reflectTypesPackage, name, nil)
+ return types.NewNamed(obj, underlying, types.ObjSet{})
}
func makeReflectValue(t types.Type, v value) value {
@@ -74,11 +72,11 @@
func extÛ°reflectÛ°rtypeÛ°Bits(fn *ssa.Function, args []value) value {
// Signature: func (t reflect.rtype) int
rt := args[0].(rtype).t
- basic, ok := underlyingType(rt).(*types.Basic)
+ basic, ok := rt.Underlying().(*types.Basic)
if !ok {
panic(fmt.Sprintf("reflect.Type.Bits(%T): non-basic type", rt))
}
- switch basic.Kind {
+ switch basic.Kind() {
case types.Int8, types.Uint8:
return 8
case types.Int16, types.Uint16:
@@ -109,22 +107,9 @@
func extÛ°reflectÛ°rtypeÛ°Elem(fn *ssa.Function, args []value) value {
// Signature: func (t reflect.rtype) reflect.Type
- var elem types.Type
- switch rt := underlyingType(args[0].(rtype).t).(type) {
- case *types.Array:
- elem = rt.Elt
- case *types.Chan:
- elem = rt.Elt
- case *types.Map:
- elem = rt.Elt
- case *types.Pointer:
- elem = rt.Base
- case *types.Slice:
- elem = rt.Elt
- default:
- panic(fmt.Sprintf("reflect.Type.Elem(%T)", rt))
- }
- return makeReflectType(rtype{elem})
+ return makeReflectType(rtype{args[0].(rtype).t.Underlying().(interface {
+ Elem() types.Type
+ }).Elem()})
}
func extÛ°reflectÛ°rtypeÛ°Kind(fn *ssa.Function, args []value) value {
@@ -134,13 +119,13 @@
func extÛ°reflectÛ°rtypeÛ°NumOut(fn *ssa.Function, args []value) value {
// Signature: func (t reflect.rtype) int
- return len(args[0].(rtype).t.(*types.Signature).Results)
+ return args[0].(rtype).t.(*types.Signature).Results().Len()
}
func extÛ°reflectÛ°rtypeÛ°Out(fn *ssa.Function, args []value) value {
// Signature: func (t reflect.rtype, i int) int
i := args[1].(int)
- return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results[i].Type})
+ return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results().At(i).Type()})
}
func extÛ°reflectÛ°rtypeÛ°String(fn *ssa.Function, args []value) value {
@@ -161,10 +146,10 @@
func reflectKind(t types.Type) reflect.Kind {
switch t := t.(type) {
- case *types.NamedType:
- return reflectKind(t.Underlying)
+ case *types.Named:
+ return reflectKind(t.Underlying())
case *types.Basic:
- switch t.Kind {
+ switch t.Kind() {
case types.Bool:
return reflect.Bool
case types.Int:
@@ -287,12 +272,12 @@
func extÛ°reflectÛ°ValueÛ°Index(fn *ssa.Function, args []value) value {
// Signature: func (v reflect.Value, i int) Value
i := args[1].(int)
- t := underlyingType(rV2T(args[0]).t)
+ t := rV2T(args[0]).t.Underlying()
switch v := rV2V(args[0]).(type) {
case array:
- return makeReflectValue(t.(*types.Array).Elt, v[i])
+ return makeReflectValue(t.(*types.Array).Elem(), v[i])
case []value:
- return makeReflectValue(t.(*types.Slice).Elt, v[i])
+ return makeReflectValue(t.(*types.Slice).Elem(), v[i])
default:
panic(fmt.Sprintf("reflect.(Value).Index(%T)", v))
}
@@ -322,7 +307,7 @@
case iface:
return makeReflectValue(x.t, x.v)
case *value:
- return makeReflectValue(underlyingType(rV2T(args[0]).t).(*types.Pointer).Base, *x)
+ return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x)
default:
panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
}
@@ -333,7 +318,7 @@
// Signature: func (v reflect.Value, i int) reflect.Value
v := args[0]
i := args[1].(int)
- return makeReflectValue(underlyingType(rV2T(v).t).(*types.Struct).Fields[i].Type, rV2V(v).(structure)[i])
+ return makeReflectValue(rV2T(v).t.Underlying().(*types.Struct).Field(i).Type, rV2V(v).(structure)[i])
}
func extÛ°reflectÛ°ValueÛ°Interface(fn *ssa.Function, args []value) value {
@@ -413,10 +398,7 @@
// that is needed is the "pointerness" of Recv.Type, and for
// now, we'll set it to always be false since we're only
// concerned with rtype. Encapsulate this better.
- fn.Signature = &types.Signature{Recv: &types.Var{
- Name: "recv",
- Type: recvType,
- }}
+ fn.Signature = types.NewSignature(types.NewVar(nil, "recv", recvType), nil, nil, false)
return fn
}
diff --git a/ssa/interp/value.go b/ssa/interp/value.go
index f66392c..3c2542a 100644
--- a/ssa/interp/value.go
+++ b/ssa/interp/value.go
@@ -103,8 +103,8 @@
switch t := t.(type) {
case *types.Basic, *types.Chan, *types.Pointer:
return true
- case *types.NamedType:
- return usesBuiltinMap(t.Underlying)
+ case *types.Named:
+ return usesBuiltinMap(t.Underlying())
case *types.Interface, *types.Array, *types.Struct:
return false
}
diff --git a/ssa/lift.go b/ssa/lift.go
index 62f9993..bc836a4 100644
--- a/ssa/lift.go
+++ b/ssa/lift.go
@@ -306,7 +306,7 @@
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
// Don't lift aggregates into registers.
// We'll need a separate SRA pass for that.
- switch underlyingType(indirectType(alloc.Type())).(type) {
+ switch alloc.Type().Deref().Underlying().(type) {
case *types.Array, *types.Struct:
return false
}
@@ -374,7 +374,7 @@
Edges: make([]Value, len(v.Preds)),
Comment: alloc.Name(),
}
- phi.setType(indirectType(alloc.Type()))
+ phi.setType(alloc.Type().Deref())
phi.Block_ = v
if debugLifting {
fmt.Fprintf(os.Stderr, "place %s = %s at block %s\n", phi.Name(), phi, v)
@@ -421,7 +421,7 @@
func renamed(renaming []Value, alloc *Alloc) Value {
v := renaming[alloc.index]
if v == nil {
- v = zeroLiteral(indirectType(alloc.Type()))
+ v = zeroLiteral(alloc.Type().Deref())
renaming[alloc.index] = v
}
return v
diff --git a/ssa/literal.go b/ssa/literal.go
index d34d43d..ad07ad4 100644
--- a/ssa/literal.go
+++ b/ssa/literal.go
@@ -39,23 +39,23 @@
switch t := t.(type) {
case *types.Basic:
switch {
- case t.Info&types.IsBoolean != 0:
+ case t.Info()&types.IsBoolean != 0:
return newLiteral(exact.MakeBool(false), t)
- case t.Info&types.IsNumeric != 0:
+ case t.Info()&types.IsNumeric != 0:
return newLiteral(exact.MakeInt64(0), t)
- case t.Info&types.IsString != 0:
+ case t.Info()&types.IsString != 0:
return newLiteral(exact.MakeString(""), t)
- case t.Kind == types.UnsafePointer:
+ case t.Kind() == types.UnsafePointer:
fallthrough
- case t.Kind == types.UntypedNil:
+ case t.Kind() == types.UntypedNil:
return nilLiteral(t)
default:
panic(fmt.Sprint("zeroLiteral for unexpected type:", t))
}
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
return nilLiteral(t)
- case *types.NamedType:
- return newLiteral(zeroLiteral(t.Underlying).Value, t)
+ case *types.Named:
+ return newLiteral(zeroLiteral(t.Underlying()).Value, t)
case *types.Array, *types.Struct:
panic(fmt.Sprint("zeroLiteral applied to aggregate:", t))
}
@@ -63,13 +63,16 @@
}
func (l *Literal) Name() string {
- s := l.Value.String()
+ var s string
if l.Value.Kind() == exact.String {
- const n = 20
- if len(s) > n {
- s = s[:n-3] + "..." // abbreviate
+ s = exact.StringVal(l.Value)
+ const maxLit = 20
+ if len(s) > maxLit {
+ s = s[:maxLit-3] + "..." // abbreviate
}
s = strconv.Quote(s)
+ } else {
+ s = l.Value.String()
}
return s + ":" + l.Type_.String()
}
diff --git a/ssa/lvalue.go b/ssa/lvalue.go
index 358deea..1f5b4cf 100644
--- a/ssa/lvalue.go
+++ b/ssa/lvalue.go
@@ -31,7 +31,7 @@
}
func (a address) typ() types.Type {
- return indirectType(a.addr.Type())
+ return a.addr.Type().Deref()
}
// An element is an lvalue represented by m[k], the location of an
diff --git a/ssa/print.go b/ssa/print.go
index e950c77..f92add1 100644
--- a/ssa/print.go
+++ b/ssa/print.go
@@ -48,7 +48,7 @@
// It never appears in disassembly, which uses Value.Name().
func (v *Literal) String() string {
- return fmt.Sprintf("literal %s rep=%T", v.Name(), v.Value)
+ return fmt.Sprintf("literal %s", v.Name())
}
func (v *Parameter) String() string {
@@ -73,7 +73,7 @@
// FullName returns g's package-qualified name.
func (g *Global) FullName() string {
- return fmt.Sprintf("%s.%s", g.Pkg.Types.Path, g.Name_)
+ return fmt.Sprintf("%s.%s", g.Pkg.Types.Path(), g.Name_)
}
// Instruction.String()
@@ -83,7 +83,7 @@
if v.Heap {
op = "new"
}
- return fmt.Sprintf("%s %s", op, indirectType(v.Type()))
+ return fmt.Sprintf("%s %s", op, v.Type().Deref())
}
func (v *Phi) String() string {
@@ -120,7 +120,7 @@
if !v.IsInvoke() {
b.WriteString(relName(v.Func, instr))
} else {
- name := underlyingType(v.Recv.Type()).(*types.Interface).Methods[v.Method].Name
+ name := v.Recv.Type().Underlying().(*types.Interface).Method(v.Method).Name()
fmt.Fprintf(&b, "invoke %s.%s [#%d]", relName(v.Recv, instr), name, v.Method)
}
b.WriteString("(")
@@ -145,6 +145,10 @@
return printCall(&v.Call, "", v)
}
+func (v *ChangeType) String() string {
+ return fmt.Sprintf("changetype %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
+}
+
func (v *BinOp) String() string {
return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
}
@@ -153,7 +157,7 @@
return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
}
-func (v *Conv) String() string {
+func (v *Convert) String() string {
return fmt.Sprintf("convert %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
}
@@ -221,21 +225,21 @@
}
func (v *FieldAddr) String() string {
- fields := underlyingType(indirectType(v.X.Type())).(*types.Struct).Fields
+ st := v.X.Type().Deref().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
- if v.Field >= 0 && v.Field < len(fields) {
- name = fields[v.Field].Name
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name
}
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *Field) String() string {
- fields := underlyingType(v.X.Type()).(*types.Struct).Fields
+ st := v.X.Type().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
- if v.Field >= 0 && v.Field < len(fields) {
- name = fields[v.Field].Name
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name
}
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
@@ -352,7 +356,7 @@
}
func (p *Package) String() string {
- return "Package " + p.Types.Path
+ return "Package " + p.Types.Path()
}
func (p *Package) DumpTo(w io.Writer) {
@@ -377,7 +381,7 @@
fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type())
case *Type:
- fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying)
+ fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying())
// We display only PtrMethods since its keys
// are a superset of Methods' keys, though the
// methods themselves may differ,
diff --git a/ssa/promote.go b/ssa/promote.go
index 89f9d95..1d6f4fa 100644
--- a/ssa/promote.go
+++ b/ssa/promote.go
@@ -70,7 +70,7 @@
// type's method-set.
//
type candidate struct {
- method *types.Method // method object of abstract or concrete type
+ method *types.Func // method object of abstract or concrete type
concrete *Function // actual method (iff concrete)
path *anonFieldPath // desugared selector path
}
@@ -82,12 +82,12 @@
for p := c.path; p != nil; p = p.tail {
s = "." + p.field.Name + s
}
- return "@" + s + "." + c.method.Name
+ return "@" + s + "." + c.method.Name()
}
// ptrRecv returns true if this candidate has a pointer receiver.
func (c candidate) ptrRecv() bool {
- return c.concrete != nil && isPointer(c.concrete.Signature.Recv.Type)
+ return c.concrete != nil && isPointer(c.concrete.Signature.Recv().Type())
}
// MethodSet returns the method set for type typ,
@@ -146,24 +146,25 @@
if node != nil {
t = node.field.Type
}
- t = deref(t)
+ t = t.Deref()
- if nt, ok := t.(*types.NamedType); ok {
- for _, meth := range nt.Methods {
- addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, prog.concreteMethods[meth], node)
- }
- t = nt.Underlying
+ if nt, ok := t.(*types.Named); ok {
+ nt.ForEachMethod(func(m *types.Func) {
+ addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, prog.concreteMethods[m], node)
+ })
+ t = nt.Underlying()
}
switch t := t.(type) {
case *types.Interface:
- for _, meth := range t.Methods {
- addCandidate(nextcands, IdFromQualifiedName(meth.QualifiedName), meth, nil, node)
- }
+ t.ForEachMethod(func(m *types.Func) {
+ addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, nil, node)
+ })
case *types.Struct:
- for i, f := range t.Fields {
- nextcands[IdFromQualifiedName(f.QualifiedName)] = nil // a field: block id
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ nextcands[MakeId(f.Name, f.Pkg)] = nil // a field: block id
// Queue up anonymous fields for next iteration.
// Break cycles to ensure termination.
if f.IsAnonymous && !node.contains(f) {
@@ -226,7 +227,7 @@
// If m[id] already exists (whether nil or not), m[id] is set to nil.
// If method denotes a concrete method, concrete is its implementation.
//
-func addCandidate(m map[Id]*candidate, id Id, method *types.Method, concrete *Function, node *anonFieldPath) {
+func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Function, node *anonFieldPath) {
prev, found := m[id]
switch {
case prev != nil:
@@ -259,20 +260,20 @@
// method.
//
func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function {
- sig := *cand.method.Type // make a copy, sharing underlying Values
- sig.Recv = &types.Var{Name: "recv", Type: typ}
+ old := cand.method.Type().(*types.Signature)
+ sig := types.NewSignature(types.NewVar(nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
if prog.mode&LogSource != 0 {
defer logStack("makeBridgeMethod %s, %s, type %s", typ, cand, &sig)()
}
fn := &Function{
- Name_: cand.method.Name,
- Signature: &sig,
+ Name_: cand.method.Name(),
+ Signature: sig,
Prog: prog,
}
fn.startBody()
- fn.addSpilledParam(sig.Recv)
+ fn.addSpilledParam(sig.Recv())
createParams(fn)
// Each bridge method performs a sequence of selections,
@@ -287,7 +288,7 @@
// 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 := underlyingType(indirectType(v.Type())).(*types.Struct); !ok {
+ if _, ok := v.Type().Deref().Underlying().(*types.Struct); !ok {
panic(fmt.Sprint("not a *struct: ", v.Type(), p.field.Type))
}
sel := &FieldAddr{
@@ -307,8 +308,8 @@
var c Call
if cand.concrete != nil {
c.Call.Func = cand.concrete
- fn.Pos = c.Call.Func.(*Function).Pos // TODO(adonovan): fix: wrong.
- c.Call.Pos = fn.Pos // TODO(adonovan): fix: wrong.
+ fn.pos = c.Call.Func.(*Function).pos // TODO(adonovan): fix: wrong.
+ c.Call.pos = fn.pos // TODO(adonovan): fix: wrong.
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Recv = v
@@ -322,15 +323,17 @@
// createParams creates parameters for bridge method fn based on its Signature.
func createParams(fn *Function) {
var last *Parameter
- for i, p := range fn.Signature.Params {
- name := p.Name
+ tparams := fn.Signature.Params()
+ for i, n := 0, tparams.Len(); i < n; i++ {
+ p := tparams.At(i)
+ name := p.Name()
if name == "" {
name = fmt.Sprintf("arg%d", i)
}
- last = fn.addParam(name, p.Type)
+ last = fn.addParam(name, p.Type())
}
- if fn.Signature.IsVariadic {
- last.Type_ = &types.Slice{Elt: last.Type_}
+ if fn.Signature.IsVariadic() {
+ last.Type_ = types.NewSlice(last.Type_)
}
}
@@ -365,11 +368,11 @@
if prog.mode&LogSource != 0 {
defer logStack("makeImethodThunk %s.%s", typ, id)()
}
- itf := underlyingType(typ).(*types.Interface)
- index, meth := methodIndex(itf, itf.Methods, id)
- sig := *meth.Type // copy; shared Values
+ itf := typ.Underlying().(*types.Interface)
+ index, meth := methodIndex(itf, id)
+ sig := *meth.Type().(*types.Signature) // copy; shared Values
fn := &Function{
- Name_: meth.Name,
+ Name_: meth.Name(),
Signature: &sig,
Prog: prog,
}
@@ -398,41 +401,45 @@
//
func findPromotedField(st *types.Struct, id Id) (*anonFieldPath, int) {
// visited records the types that have been searched already.
- // Invariant: keys are all *types.NamedType.
+ // Invariant: keys are all *types.Named.
// (types.Type is not a sound map key in general.)
visited := make(map[types.Type]bool)
var list, next []*anonFieldPath
- for i, f := range st.Fields {
+ i := 0
+ st.ForEachField(func(f *types.Field) {
if f.IsAnonymous {
list = append(list, &anonFieldPath{nil, i, f})
}
- }
+ i++
+ })
// Search the current level if there is any work to do and collect
// embedded types of the next lower level in the next list.
for {
// look for name in all types at this level
for _, node := range list {
- typ := deref(node.field.Type).(*types.NamedType)
+ typ := node.field.Type.Deref().(*types.Named)
if visited[typ] {
continue
}
visited[typ] = true
- switch typ := typ.Underlying.(type) {
+ switch typ := typ.Underlying().(type) {
case *types.Struct:
- for i, f := range typ.Fields {
- if IdFromQualifiedName(f.QualifiedName) == id {
+ for i, n := 0, typ.NumFields(); i < n; i++ {
+ f := typ.Field(i)
+ if MakeId(f.Name, f.Pkg) == id {
return node, i
}
}
- for i, f := range typ.Fields {
+ i := 0
+ typ.ForEachField(func(f *types.Field) {
if f.IsAnonymous {
next = append(next, &anonFieldPath{node, i, f})
}
- }
-
+ i++
+ })
}
}
diff --git a/ssa/sanity.go b/ssa/sanity.go
index 9a6a81d..e946537 100644
--- a/ssa/sanity.go
+++ b/ssa/sanity.go
@@ -123,7 +123,8 @@
case *BinOp:
case *Call:
case *ChangeInterface:
- case *Conv:
+ case *ChangeType:
+ case *Convert:
case *Defer:
case *Extract:
case *Field:
diff --git a/ssa/ssa.go b/ssa/ssa.go
index a25766e..3a24b0a 100644
--- a/ssa/ssa.go
+++ b/ssa/ssa.go
@@ -22,10 +22,10 @@
Packages map[string]*Package // all loaded Packages, keyed by import path
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
- methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
- methodSetsMu sync.Mutex // serializes all accesses to methodSets
- concreteMethods map[*types.Method]*Function // maps named concrete methods to their code
- mode BuilderMode // set of mode bits
+ methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
+ methodSetsMu sync.Mutex // serializes all accesses to methodSets
+ concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
+ mode BuilderMode // set of mode bits
}
// A Package is a single analyzed Go package containing Members for
@@ -57,7 +57,7 @@
type Member interface {
Name() string // the declared name of the package member
String() string // human-readable information about the value
- Posn() token.Pos // position of member's declaration, if known
+ Pos() token.Pos // position of member's declaration, if known
Type() types.Type // the type of the package member
ImplementsMember() // dummy method to indicate the "implements" relation.
}
@@ -93,7 +93,7 @@
// type and method set of a named type declared at package scope.
//
type Type struct {
- NamedType *types.NamedType
+ NamedType *types.Named
Methods MethodSet // concrete method set of N
PtrMethods MethodSet // concrete method set of (*N)
}
@@ -101,10 +101,16 @@
// A Constant is a Member of Package representing a package-level
// constant value.
//
+// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
+// identifier.
+//
+// NB: a Constant is not a Value; it contains a literal Value, which
+// it augments with the name and position of its 'const' declaration.
+//
type Constant struct {
Name_ string
Value *Literal
- Pos token.Pos
+ pos token.Pos
}
// An SSA value that can be referenced by an instruction.
@@ -202,6 +208,18 @@
// Values.)
Operands(rands []*Value) []*Value
+ // Pos returns the location of the source construct that
+ // gave rise to this instruction, or token.NoPos if it was not
+ // explicit in the source.
+ //
+ // For each ast.Expr type, a particular field is designated as
+ // the canonical location for the expression, e.g. the Lparen
+ // for an *ast.CallExpr. This enables us to find the
+ // instruction corresponding to a given piece of source
+ // syntax.
+ //
+ Pos() token.Pos
+
// Dummy method to indicate the "implements" relation.
ImplementsInstruction()
}
@@ -226,16 +244,20 @@
// MakeClosure which, via its Bindings, supplies values for these
// parameters. Captures are always addresses.
//
-// If the function is a method (Signature.Recv != nil) then the first
+// If the function is a method (Signature.Recv() != nil) then the first
// element of Params is the receiver parameter.
//
+// Pos() returns the declaring ast.FuncLit.Type.Func or the position
+// of the ast.FuncDecl.Name, if the function was explicit in the
+// source.
+//
// Type() returns the function's Signature.
//
type Function struct {
Name_ string
Signature *types.Signature
- Pos token.Pos // location of the definition
+ pos token.Pos
Enclosing *Function // enclosing function if anon; nil if global
Pkg *Package // enclosing package for Go source functions; otherwise nil
Prog *Program // enclosing program
@@ -303,19 +325,23 @@
referrers []Instruction
}
-// A Literal represents a literal nil, boolean, string or numeric
-// (integer, fraction or complex) value.
+// A Literal represents the value of a constant expression.
//
-// A literal's underlying Type() can be a basic type, possibly one of
-// the "untyped" types. A nil literal can have any reference type:
-// interface, map, channel, pointer, slice, or function---but not
-// "untyped nil".
+// It may have a nil, boolean, string or numeric (integer, fraction or
+// complex) value, or a []byte or []rune conversion of a string
+// literal.
+//
+// Literals may be of named types. A literal's underlying type can be
+// a basic type, possibly one of the "untyped" types, or a slice type
+// whose elements' underlying type is byte or rune. A nil literal can
+// have any reference type: interface, map, channel, pointer, slice,
+// or function---but not "untyped nil".
//
// All source-level constant expressions are represented by a Literal
// of equal type and value.
//
// Value holds the exact value of the literal, independent of its
-// Type(), using the same representation as package go/types uses for
+// Type(), using the same representation as package go/exact uses for
// constants.
//
// Example printed form:
@@ -331,24 +357,27 @@
// A Global is a named Value holding the address of a package-level
// variable.
//
+// Pos() returns the position of the ast.ValueSpec.Names[*]
+// identifier.
+//
type Global struct {
Name_ string
Type_ types.Type
Pkg *Package
- Pos token.Pos
+ pos token.Pos
// The following fields are set transiently during building,
// then cleared.
spec *ast.ValueSpec // explained at buildGlobal
}
-// A built-in function, e.g. len.
+// A Builtin represents a built-in function, e.g. len.
//
-// Builtins are immutable values; they do not have addresses.
+// Builtins are immutable values. Builtins do not have addresses.
//
-// Type() returns an inscrutable *types.builtin. Built-in functions
-// may have polymorphic or variadic types that are not expressible in
-// Go's type system.
+// Type() returns a *types.Builtin.
+// Built-in functions may have polymorphic or variadic types that are
+// not expressible in Go's type system.
//
type Builtin struct {
Object *types.Func // canonical types.Universe object for this built-in
@@ -377,6 +406,10 @@
// the result of MakeSlice, MakeMap or MakeChan in that location to
// instantiate these types.
//
+// Pos() returns the ast.CompositeLit.Lbrace for a composite literal,
+// or the ast.CallExpr.Lparen for a call to new() or for a call that
+// allocates a varargs slice.
+//
// Example printed form:
// t0 = local int
// t1 = new int
@@ -386,14 +419,17 @@
Name_ string
Type_ types.Type
Heap bool
- Pos token.Pos
+ pos token.Pos
referrers []Instruction
index int // dense numbering; for lifting
}
-// Phi represents an SSA φ-node, which combines values that differ
-// across incoming control-flow edges and yields a new value. Within
-// a block, all φ-nodes must appear before all non-φ nodes.
+// The Phi instruction represents an SSA φ-node, which combines values
+// that differ across incoming control-flow edges and yields a new
+// value. Within a block, all φ-nodes must appear before all non-φ
+// nodes.
+//
+// Pos() returns NoPos.
//
// Example printed form:
// t2 = phi [0.start: t0, 1.if.then: t1, ...]
@@ -404,7 +440,7 @@
Edges []Value // Edges[i] is value for Block().Preds[i]
}
-// Call represents a function or method call.
+// The Call instruction represents a function or method call.
//
// The Call instruction yields the function result, if there is
// exactly one, or a tuple (empty or len>1) whose components are
@@ -412,6 +448,8 @@
//
// See CallCommon for generic function call documentation.
//
+// Pos() returns the ast.CallExpr.Lparen, if explicit in the source.
+//
// Example printed form:
// t2 = println(t0, t1)
// t4 = t3()
@@ -422,7 +460,10 @@
Call CallCommon
}
-// BinOp yields the result of binary operation X Op Y.
+// The BinOp instruction yields the result of binary operation X Op Y.
+//
+// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source.
+// TODO(adonovan): implement.
//
// Example printed form:
// t1 = t0 + 1:int
@@ -437,7 +478,7 @@
X, Y Value
}
-// UnOp yields the result of Op X.
+// The UnOp instruction yields the result of Op X.
// ARROW is channel receive.
// MUL is pointer indirection (load).
// XOR is bitwise complement.
@@ -447,6 +488,8 @@
// and a boolean indicating the success of the receive. The
// components of the tuple are accessed using Extract.
//
+// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source.
+//
// Example printed form:
// t0 = *x
// t2 = <-t1,ok
@@ -458,38 +501,53 @@
CommaOk bool
}
-// Conv yields the conversion of X to type Type().
+// The ChangeType instruction applies to X a value-preserving type
+// change to Type().
//
-// A conversion is one of the following kinds. The behaviour of the
-// conversion operator may depend on both Type() and X.Type(), as well
-// as the dynamic value.
+// Type changes are permitted:
+// - between a named type and its underlying type.
+// - between two named types of the same underlying type.
+// - between (possibly named) pointers to identical base types.
+// - between f(T) functions and (T) func f() methods.
+// - from a bidirectional channel to a read- or write-channel,
+// optionally adding/removing a name.
//
-// A '+' indicates that a dynamic representation change may occur.
-// A '-' indicates that the conversion is a value-preserving change
-// to types only.
+// This operation cannot fail dynamically.
//
-// 1. implicit conversions (arising from assignability rules):
-// - adding/removing a name, same underlying types.
-// - channel type restriction, possibly adding/removing a name.
-// 2. explicit conversions (in addition to the above):
-// - changing a name, same underlying types.
-// - between pointers to identical base types.
-// + conversions between real numeric types.
-// + conversions between complex numeric types.
-// + integer/[]byte/[]rune -> string.
-// + string -> []byte/[]rune.
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
//
-// TODO(adonovan): split into two cases:
-// - rename value (ChangeType)
-// + value to type with different representation (Conv)
+// Example printed form:
+// t1 = changetype *int <- IntPtr (t0)
+//
+type ChangeType struct {
+ Register
+ X Value
+}
+
+// The Convert instruction yields the conversion of value X to type
+// Type().
+//
+// A conversion may change the value and representation of its operand.
+// Conversions are permitted:
+// - between real numeric types.
+// - between complex numeric types.
+// - between string and []byte or []rune.
+// - from (Unicode) integer to (UTF-8) string.
+// A conversion may imply a type name change also.
+//
+// This operation cannot fail dynamically.
//
// Conversions of untyped string/number/bool constants to a specific
// representation are eliminated during SSA construction.
//
-// Example printed form:
-// t1 = convert interface{} <- int (t0)
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
//
-type Conv struct {
+// Example printed form:
+// t1 = convert []byte <- string (t0)
+//
+type Convert struct {
Register
X Value
}
@@ -499,7 +557,9 @@
//
// This operation cannot fail. Use TypeAssert for interface
// conversions that may fail dynamically.
-// TODO(adonovan): rename to "{Narrow,Restrict}Interface"?
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
//
// Example printed form:
// t1 = change interface interface{} <- I (t0)
@@ -513,7 +573,10 @@
// value and its method-set.
//
// To construct the zero value of an interface type T, use:
-// &Literal{types.nilType{}, T}
+// &Literal{exact.MakeNil(), T}
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
//
// Example printed form:
// t1 = make interface interface{} <- int (42:int)
@@ -524,14 +587,18 @@
Methods MethodSet // method set of (non-interface) X
}
-// A MakeClosure instruction yields an anonymous function value whose
-// code is Fn and whose lexical capture slots are populated by Bindings.
+// The MakeClosure instruction yields an anonymous function value
+// whose code is Fn and whose lexical capture slots are populated by
+// Bindings.
//
// By construction, all captured variables are addresses of variables
// allocated with 'new', i.e. Alloc(Heap=true).
//
// Type() returns a (possibly named) *types.Signature.
//
+// Pos() returns the ast.FuncLit.Type.Func of the function literal
+// that created this closure.
+//
// Example printed form:
// t0 = make closure anon@1.2 [x y z]
//
@@ -546,13 +613,15 @@
//
// Type() returns a (possibly named) *types.Map.
//
+// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or
+// the ast.CompositeLit.Lbrack if created by a literal.
+//
// Example printed form:
// t1 = make map[string]int t0
//
type MakeMap struct {
Register
Reserve Value // initial space reservation; nil => default
- Pos token.Pos
}
// The MakeChan instruction creates a new channel object and yields a
@@ -560,17 +629,19 @@
//
// Type() returns a (possibly named) *types.Chan.
//
+// Pos() returns the ast.CallExpr.Lparen for the make(chan) that
+// created it.
+//
// Example printed form:
// t0 = make chan int 0
//
type MakeChan struct {
Register
Size Value // int; size of buffer; zero => synchronous.
- Pos token.Pos
}
-// MakeSlice yields a slice of length Len backed by a newly allocated
-// array of length Cap.
+// The MakeSlice instruction yields a slice of length Len backed by a
+// newly allocated array of length Cap.
//
// Both Len and Cap must be non-nil Values of integer type.
//
@@ -579,6 +650,9 @@
//
// Type() returns a (possibly named) *types.Slice.
//
+// Pos() returns the ast.CallExpr.Lparen for the make([]T) that
+// created it.
+//
// Example printed form:
// t1 = make slice []string 1:int t0
//
@@ -586,15 +660,18 @@
Register
Len Value
Cap Value
- Pos token.Pos
}
-// Slice yields a slice of an existing string, slice or *array X
-// between optional integer bounds Low and High.
+// The Slice instruction yields a slice of an existing string, slice
+// or *array X between optional integer bounds Low and High.
//
// Type() returns string if the type of X was string, otherwise a
// *types.Slice with the same element type as X.
//
+// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice
+// operation, the ast.CompositeLit.Lbrace if created by a literal, or
+// NoPos if not explicit in the source (e.g. a variadic argument slice).
+//
// Example printed form:
// t1 = slice t0[1:]
//
@@ -604,13 +681,16 @@
Low, High Value // either may be nil
}
-// FieldAddr yields the address of Field of *struct X.
+// The FieldAddr instruction yields the address of Field of *struct X.
//
// The field is identified by its index within the field list of the
// struct type of X.
//
// Type() returns a (possibly named) *types.Pointer.
//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
// Example printed form:
// t1 = &t0.name [#1]
//
@@ -620,12 +700,15 @@
Field int // index into X.Type().(*types.Struct).Fields
}
-// Field yields the Field of struct X.
+// The Field instruction yields the Field of struct X.
//
// The field is identified by its index within the field list of the
// struct type of X; by using numeric indices we avoid ambiguity of
// package-local identifiers and permit compact representations.
//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
// Example printed form:
// t1 = t0.name [#1]
//
@@ -635,14 +718,17 @@
Field int // index into X.Type().(*types.Struct).Fields
}
-// IndexAddr yields the address of the element at index Index of
-// collection X. Index is an integer expression.
+// The IndexAddr instruction yields the address of the element at
+// index Index of collection X. Index is an integer expression.
//
// The elements of maps and strings are not addressable; use Lookup or
// MapUpdate instead.
//
// Type() returns a (possibly named) *types.Pointer.
//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
+//
// Example printed form:
// t2 = &t0[t1]
//
@@ -652,7 +738,10 @@
Index Value // numeric index
}
-// Index yields element Index of array X.
+// The Index instruction yields element Index of array X.
+//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
//
// Example printed form:
// t2 = t0[t1]
@@ -663,14 +752,16 @@
Index Value // integer index
}
-// Lookup yields element Index of collection X, a map or string.
-// Index is an integer expression if X is a string or the appropriate
-// key type if X is a map.
+// The Lookup instruction yields element Index of collection X, a map
+// or string. Index is an integer expression if X is a string or the
+// appropriate key type if X is a map.
//
// If CommaOk, the result is a 2-tuple of the value above and a
// boolean indicating the result of a map membership test for the key.
// The components of the tuple are accessed using Extract.
//
+// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source.
+//
// Example printed form:
// t2 = t0[t1]
// t5 = t3[t4],ok
@@ -691,8 +782,8 @@
Send Value // value to send (for send)
}
-// Select tests whether (or blocks until) one or more of the specified
-// sent or received states is entered.
+// The Select instruction tests whether (or blocks until) one or more
+// of the specified sent or received states is entered.
//
// It returns a triple (index int, recv interface{}, recvOk bool)
// whose components, described below, must be accessed via the Extract
@@ -714,6 +805,8 @@
// is true iff the selected operation was a receive and the receive
// successfully yielded a value.
//
+// Pos() returns the ast.SelectStmt.Select.
+//
// Example printed form:
// t3 = select nonblocking [<-t0, t1<-t2, ...]
// t4 = select blocking []
@@ -724,13 +817,15 @@
Blocking bool
}
-// Range yields an iterator over the domain and range of X,
-// which must be a string or map.
+// The Range instruction yields an iterator over the domain and range
+// of X, which must be a string or map.
//
// Elements are accessed via Next.
//
// Type() returns a (possibly named) *types.Result (tuple type).
//
+// Pos() returns the ast.RangeStmt.For.
+//
// Example printed form:
// t0 = range "hello":string
//
@@ -739,11 +834,11 @@
X Value // string or map
}
-// Next reads and advances the (map or string) iterator Iter and
-// returns a 3-tuple value (ok, k, v). If the iterator is not
-// exhausted, ok is true and k and v are the next elements of the
-// domain and range, respectively. Otherwise ok is false and k and v
-// are undefined.
+// The Next instruction reads and advances the (map or string)
+// iterator Iter and returns a 3-tuple value (ok, k, v). If the
+// iterator is not exhausted, ok is true and k and v are the next
+// elements of the domain and range, respectively. Otherwise ok is
+// false and k and v are undefined.
//
// Components of the tuple are accessed using Extract.
//
@@ -763,7 +858,8 @@
IsString bool // true => string iterator; false => map iterator.
}
-// TypeAssert tests whether interface value X has type AssertedType.
+// The TypeAssert instruction tests whether interface value X has type
+// AssertedType.
//
// If !CommaOk, on success it returns v, the result of the conversion
// (defined below); on failure it panics.
@@ -797,7 +893,7 @@
CommaOk bool
}
-// Extract yields component Index of Tuple.
+// The Extract instruction yields component Index of Tuple.
//
// This is used to access the results of instructions with multiple
// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
@@ -814,10 +910,12 @@
// Instructions executed for effect. They do not yield a value. --------------------
-// Jump transfers control to the sole successor of its owning block.
+// The Jump instruction transfers control to the sole successor of its
+// owning block.
//
-// A Jump instruction must be the last instruction of its containing
-// BasicBlock.
+// A Jump must be the last instruction of its containing BasicBlock.
+//
+// Pos() returns NoPos.
//
// Example printed form:
// jump done
@@ -833,6 +931,8 @@
// An If instruction must be the last instruction of its containing
// BasicBlock.
//
+// Pos() returns NoPos.
+//
// Example printed form:
// if t0 goto done else body
//
@@ -841,7 +941,8 @@
Cond Value
}
-// Ret returns values and control back to the calling function.
+// The Ret instruction returns values and control back to the calling
+// function.
//
// len(Results) is always equal to the number of results in the
// function's signature.
@@ -856,6 +957,8 @@
// Ret must be the last instruction of its containing BasicBlock.
// Such a block has no successors.
//
+// Pos() returns the ast.ReturnStmt.Return, if explicit in the source.
+//
// Example printed form:
// ret
// ret nil:I, 2:int
@@ -863,15 +966,18 @@
type Ret struct {
anInstruction
Results []Value
+ pos token.Pos
}
-// RunDefers pops and invokes the entire stack of procedure calls
-// pushed by Defer instructions in this function.
+// The RunDefers instruction pops and invokes the entire stack of
+// procedure calls pushed by Defer instructions in this function.
//
// It is legal to encounter multiple 'rundefers' instructions in a
// single control-flow path through a function; this is useful in
// the combined init() function, for example.
//
+// Pos() returns NoPos.
+//
// Example printed form:
// rundefers
//
@@ -879,7 +985,7 @@
anInstruction
}
-// Panic initiates a panic with value X.
+// The Panic instruction initiates a panic with value X.
//
// A Panic instruction must be the last instruction of its containing
// BasicBlock, which must have no successors.
@@ -887,16 +993,20 @@
// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;
// they are treated as calls to a built-in function.
//
+// Pos() returns the ast.CallExpr.Lparen if this panic was explicit
+// in the source.
+//
// Example printed form:
// panic t0
//
type Panic struct {
anInstruction
- X Value // an interface{}
+ X Value // an interface{}
+ pos token.Pos
}
-// Go creates a new goroutine and calls the specified function
-// within it.
+// The Go instruction creates a new goroutine and calls the specified
+// function within it.
//
// See CallCommon for generic function call documentation.
//
@@ -910,8 +1020,8 @@
Call CallCommon
}
-// Defer pushes the specified call onto a stack of functions
-// to be called by a RunDefers instruction or by a panic.
+// The Defer instruction pushes the specified call onto a stack of
+// functions to be called by a RunDefers instruction or by a panic.
//
// See CallCommon for generic function call documentation.
//
@@ -925,7 +1035,9 @@
Call CallCommon
}
-// Send sends X on channel Chan.
+// The Send instruction sends X on channel Chan.
+//
+// Pos() returns the ast.SendStmt.Arrow, if explicit in the source.
//
// Example printed form:
// send t0 <- t1
@@ -933,11 +1045,15 @@
type Send struct {
anInstruction
Chan, X Value
+ pos token.Pos
}
-// Store stores Val at address Addr.
+// The Store instruction stores Val at address Addr.
// Stores can be of arbitrary types.
//
+// Pos() returns the ast.StarExpr.Star, if explicit in the source.
+// TODO(addr): implement.
+//
// Example printed form:
// *x = y
//
@@ -945,9 +1061,13 @@
anInstruction
Addr Value
Val Value
+ pos token.Pos
}
-// MapUpdate updates the association of Map[Key] to Value.
+// The MapUpdate instruction updates the association of Map[Key] to
+// Value.
+//
+// Pos() returns the ast.KeyValueExpr.Colon, if explicit in the source.
//
// Example printed form:
// t0[t1] = t2
@@ -957,6 +1077,7 @@
Map Value
Key Value
Value Value
+ pos token.Pos
}
// Embeddable mix-ins and helpers for common parts of other structs. -----------
@@ -978,6 +1099,7 @@
type Register struct {
anInstruction
num int // "name" of virtual register, e.g. "t0". Not guaranteed unique.
+ pos token.Pos // position of source expression, or NoPos
Type_ types.Type // type of virtual register
referrers []Instruction
}
@@ -1045,7 +1167,7 @@
Func Value // target of call, iff function call
Args []Value // actual parameters, including receiver in invoke mode
HasEllipsis bool // true iff last Args is a slice of '...' args (needed?)
- Pos token.Pos // position of call expression
+ pos token.Pos // position of CallExpr.Lparen, iff explicit in source
}
// IsInvoke returns true if this call has "invoke" (not "call") mode.
@@ -1053,6 +1175,8 @@
return c.Recv != nil
}
+func (c *CallCommon) Pos() token.Pos { return c.pos }
+
// StaticCallee returns the called function if this is a trivially
// static "call"-mode call.
func (c *CallCommon) StaticCallee() *Function {
@@ -1068,8 +1192,8 @@
// MethodId returns the Id for the method called by c, which must
// have "invoke" mode.
func (c *CallCommon) MethodId() Id {
- meth := underlyingType(c.Recv.Type()).(*types.Interface).Methods[c.Method]
- return IdFromQualifiedName(meth.QualifiedName)
+ m := c.Recv.Type().Underlying().(*types.Interface).Method(c.Method)
+ return MakeId(m.Name(), m.Pkg())
}
// Description returns a description of the mode of this call suitable
@@ -1081,7 +1205,7 @@
case *MakeClosure:
return "static function closure call"
case *Function:
- if fn.Signature.Recv != nil {
+ if fn.Signature.Recv() != nil {
return "static method call"
}
return "static function call"
@@ -1089,8 +1213,8 @@
return "dynamic function call"
}
-func (v *Builtin) Type() types.Type { return v.Object.GetType() }
-func (v *Builtin) Name() string { return v.Object.GetName() }
+func (v *Builtin) Type() types.Type { return v.Object.Type() }
+func (v *Builtin) Name() string { return v.Object.Name() }
func (*Builtin) Referrers() *[]Instruction { return nil }
func (v *Capture) Type() types.Type { return v.Outer.Type() }
@@ -1099,12 +1223,12 @@
func (v *Global) Type() types.Type { return v.Type_ }
func (v *Global) Name() string { return v.Name_ }
-func (v *Global) Posn() token.Pos { return v.Pos }
+func (v *Global) Pos() token.Pos { return v.pos }
func (*Global) Referrers() *[]Instruction { return nil }
func (v *Function) Name() string { return v.Name_ }
func (v *Function) Type() types.Type { return v.Signature }
-func (v *Function) Posn() token.Pos { return v.Pos }
+func (v *Function) Pos() token.Pos { return v.pos }
func (*Function) Referrers() *[]Instruction { return nil }
func (v *Parameter) Type() types.Type { return v.Type_ }
@@ -1121,19 +1245,21 @@
func (v *Register) setNum(num int) { v.num = num }
func (v *Register) Referrers() *[]Instruction { return &v.referrers }
func (v *Register) asRegister() *Register { return v }
+func (v *Register) Pos() token.Pos { return v.pos }
+func (v *Register) setPos(pos token.Pos) { v.pos = pos }
func (v *anInstruction) Block() *BasicBlock { return v.Block_ }
func (v *anInstruction) SetBlock(block *BasicBlock) { v.Block_ = block }
-func (t *Type) Name() string { return t.NamedType.Obj.Name }
-func (t *Type) Posn() token.Pos { return t.NamedType.Obj.GetPos() }
+func (t *Type) Name() string { return t.NamedType.Obj().Name() }
+func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() }
func (t *Type) String() string { return t.Name() }
func (t *Type) Type() types.Type { return t.NamedType }
-func (p *Package) Name() string { return p.Types.Name }
+func (p *Package) Name() string { return p.Types.Name() }
func (c *Constant) Name() string { return c.Name_ }
-func (c *Constant) Posn() token.Pos { return c.Pos }
+func (c *Constant) Pos() token.Pos { return c.pos }
func (c *Constant) String() string { return c.Name() }
func (c *Constant) Type() types.Type { return c.Value.Type() }
@@ -1179,7 +1305,8 @@
func (*Call) ImplementsValue() {}
func (*Capture) ImplementsValue() {}
func (*ChangeInterface) ImplementsValue() {}
-func (*Conv) ImplementsValue() {}
+func (*ChangeType) ImplementsValue() {}
+func (*Convert) ImplementsValue() {}
func (*Extract) ImplementsValue() {}
func (*Field) ImplementsValue() {}
func (*FieldAddr) ImplementsValue() {}
@@ -1212,7 +1339,8 @@
func (*BinOp) ImplementsInstruction() {}
func (*Call) ImplementsInstruction() {}
func (*ChangeInterface) ImplementsInstruction() {}
-func (*Conv) ImplementsInstruction() {}
+func (*ChangeType) ImplementsInstruction() {}
+func (*Convert) ImplementsInstruction() {}
func (*Defer) ImplementsInstruction() {}
func (*Extract) ImplementsInstruction() {}
func (*Field) ImplementsInstruction() {}
@@ -1242,9 +1370,20 @@
func (*TypeAssert) ImplementsInstruction() {}
func (*UnOp) ImplementsInstruction() {}
-// Operands.
+func (v *Alloc) Pos() token.Pos { return v.pos }
+func (v *Call) Pos() token.Pos { return v.Call.pos }
+func (s *Defer) Pos() token.Pos { return s.Call.pos }
+func (s *Go) Pos() token.Pos { return s.Call.pos }
+func (s *MapUpdate) Pos() token.Pos { return s.pos }
+func (s *Panic) Pos() token.Pos { return s.pos }
+func (s *Ret) Pos() token.Pos { return s.pos }
+func (s *Send) Pos() token.Pos { return s.pos }
+func (s *Store) Pos() token.Pos { return s.pos }
+func (s *If) Pos() token.Pos { return token.NoPos }
+func (s *Jump) Pos() token.Pos { return token.NoPos }
+func (s *RunDefers) Pos() token.Pos { return token.NoPos }
-// REVIEWERS: Should this method be defined nearer each type to avoid skew?
+// Operands.
func (v *Alloc) Operands(rands []*Value) []*Value {
return rands
@@ -1278,7 +1417,11 @@
return append(rands, &v.X)
}
-func (v *Conv) Operands(rands []*Value) []*Value {
+func (v *ChangeType) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *Convert) Operands(rands []*Value) []*Value {
return append(rands, &v.X)
}
diff --git a/ssa/ssadump.go b/ssa/ssadump.go
index d7f64aa..0f4417d 100644
--- a/ssa/ssadump.go
+++ b/ssa/ssadump.go
@@ -101,7 +101,7 @@
context := &ssa.Context{
Mode: mode,
- Loader: ssa.GorootLoader,
+ Loader: ssa.MakeGoBuildLoader(nil),
}
b := ssa.NewBuilder(context)
mainpkg, args, err := ssa.CreatePackageFromArgs(b, args)
diff --git a/ssa/typeinfo.go b/ssa/typeinfo.go
index 38673fd..0f8cbd5 100644
--- a/ssa/typeinfo.go
+++ b/ssa/typeinfo.go
@@ -32,7 +32,7 @@
// TODO(gri): This is a typechecker bug. When fixed,
// eliminate this case and panic.
if id, ok := e.(*ast.Ident); ok {
- return info.ObjectOf(id).GetType()
+ return info.ObjectOf(id).Type()
}
panic("no type for expression")
}
@@ -56,6 +56,8 @@
// IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs.
+// e must be a true expression, not a KeyValueExpr, or an Ident
+// appearing in a SelectorExpr or declaration.
//
func (info *TypeInfo) IsType(e ast.Expr) bool {
switch e := e.(type) {
@@ -83,7 +85,7 @@
func (info *TypeInfo) isPackageRef(sel *ast.SelectorExpr) types.Object {
if id, ok := sel.X.(*ast.Ident); ok {
if obj := info.ObjectOf(id); objKind(obj) == ast.Pkg {
- return obj.(*types.Package).Scope.Lookup(sel.Sel.Name)
+ return obj.(*types.Package).Scope().Lookup(sel.Sel.Name)
}
}
return nil
@@ -115,52 +117,52 @@
t1 = info.TypeOf(e.Args[1]) // no conversion
} else {
// append([]T, ...T) []T
- t1 = underlyingType(t0).(*types.Slice).Elt
+ t1 = t0.Underlying().(*types.Slice).Elem()
isVariadic = true
}
params = append(params,
- &types.Var{Type: t0},
- &types.Var{Type: t1})
+ types.NewVar(nil, "", t0),
+ types.NewVar(nil, "", t1))
case "print", "println": // print{,ln}(any, ...interface{})
isVariadic = true
// Note, arg0 may have any type, not necessarily tEface.
params = append(params,
- &types.Var{Type: info.TypeOf(e.Args[0])},
- &types.Var{Type: tEface})
+ types.NewVar(nil, "", info.TypeOf(e.Args[0])),
+ types.NewVar(nil, "", tEface))
case "close":
- params = append(params, &types.Var{Type: info.TypeOf(e.Args[0])})
+ params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
case "copy":
// copy([]T, []T) int
// Infer arg types from each other. Sleazy.
var st *types.Slice
- if t, ok := underlyingType(info.TypeOf(e.Args[0])).(*types.Slice); ok {
+ if t, ok := info.TypeOf(e.Args[0]).Underlying().(*types.Slice); ok {
st = t
- } else if t, ok := underlyingType(info.TypeOf(e.Args[1])).(*types.Slice); ok {
+ } else if t, ok := info.TypeOf(e.Args[1]).Underlying().(*types.Slice); ok {
st = t
} else {
panic("cannot infer types in call to copy()")
}
- stvar := &types.Var{Type: st}
+ stvar := types.NewVar(nil, "", st)
params = append(params, stvar, stvar)
case "delete":
// delete(map[K]V, K)
tmap := info.TypeOf(e.Args[0])
- tkey := underlyingType(tmap).(*types.Map).Key
+ tkey := tmap.Underlying().(*types.Map).Key()
params = append(params,
- &types.Var{Type: tmap},
- &types.Var{Type: tkey})
+ types.NewVar(nil, "", tmap),
+ types.NewVar(nil, "", tkey))
case "len", "cap":
- params = append(params, &types.Var{Type: info.TypeOf(e.Args[0])})
+ params = append(params, types.NewVar(nil, "", info.TypeOf(e.Args[0])))
case "real", "imag":
// Reverse conversion to "complex" case below.
var argType types.Type
- switch info.TypeOf(e).(*types.Basic).Kind {
+ switch info.TypeOf(e).(*types.Basic).Kind() {
case types.UntypedFloat:
argType = types.Typ[types.UntypedComplex]
case types.Float64:
@@ -170,11 +172,11 @@
default:
unreachable()
}
- params = append(params, &types.Var{Type: argType})
+ params = append(params, types.NewVar(nil, "", argType))
case "complex":
var argType types.Type
- switch info.TypeOf(e).(*types.Basic).Kind {
+ switch info.TypeOf(e).(*types.Basic).Kind() {
case types.UntypedComplex:
argType = types.Typ[types.UntypedFloat]
case types.Complex128:
@@ -184,11 +186,11 @@
default:
unreachable()
}
- v := &types.Var{Type: argType}
+ v := types.NewVar(nil, "", argType)
params = append(params, v, v)
case "panic":
- params = append(params, &types.Var{Type: tEface})
+ params = append(params, types.NewVar(nil, "", tEface))
case "recover":
// no params
@@ -197,5 +199,5 @@
panic("unknown builtin: " + builtin)
}
- return &types.Signature{Params: params, IsVariadic: isVariadic}
+ return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic)
}
diff --git a/ssa/util.go b/ssa/util.go
index 09682d9..acb4d33 100644
--- a/ssa/util.go
+++ b/ssa/util.go
@@ -43,23 +43,10 @@
//// Type utilities. Some of these belong in go/types.
-// underlyingType returns the underlying type of typ.
-// TODO(gri): this is a copy of go/types.underlying; export that function.
-//
-func underlyingType(typ types.Type) types.Type {
- if typ, ok := typ.(*types.NamedType); ok {
- return typ.Underlying // underlying types are never NamedTypes
- }
- if typ == nil {
- panic("underlyingType(nil)")
- }
- return typ
-}
-
// isPointer returns true for types whose underlying type is a pointer.
func isPointer(typ types.Type) bool {
- if nt, ok := typ.(*types.NamedType); ok {
- typ = nt.Underlying
+ if nt, ok := typ.(*types.Named); ok {
+ typ = nt.Underlying()
}
_, ok := typ.(*types.Pointer)
return ok
@@ -67,39 +54,22 @@
// pointer(typ) returns the type that is a pointer to typ.
func pointer(typ types.Type) *types.Pointer {
- return &types.Pointer{Base: typ}
-}
-
-// indirect(typ) assumes that typ is a pointer type,
-// or named alias thereof, and returns its base type.
-// Panic ensures if it is not a pointer.
-//
-func indirectType(ptr types.Type) types.Type {
- if v, ok := underlyingType(ptr).(*types.Pointer); ok {
- return v.Base
- }
- // When debugging it is convenient to comment out this line
- // and let it continue to print the (illegal) SSA form.
- panic("indirect() of non-pointer type: " + ptr.String())
- return nil
-}
-
-// deref returns a pointer's base type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if typ, ok := underlyingType(typ).(*types.Pointer); ok {
- return typ.Base
- }
- return typ
+ return types.NewPointer(typ)
}
// methodIndex returns the method (and its index) named id within the
-// method table methods of named or interface type typ. If not found,
+// method table of named or interface type typ. If not found,
// panic ensues.
//
-func methodIndex(typ types.Type, methods []*types.Method, id Id) (i int, m *types.Method) {
- for i, m = range methods {
- if IdFromQualifiedName(m.QualifiedName) == id {
- return
+func methodIndex(typ types.Type, id Id) (int, *types.Func) {
+ t := typ.(interface {
+ NumMethods() int
+ Method(i int) *types.Func
+ })
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ m := t.Method(i)
+ if MakeId(m.Name(), m.Pkg()) == id {
+ return i, m
}
}
panic(fmt.Sprint("method not found: ", id, " in interface ", typ))
@@ -109,15 +79,17 @@
// i.e. x's methods are a subset of y's.
//
func isSuperinterface(x, y *types.Interface) bool {
- if len(y.Methods) < len(x.Methods) {
+ if y.NumMethods() < x.NumMethods() {
return false
}
// TODO(adonovan): opt: this is quadratic.
outer:
- for _, xm := range x.Methods {
- for _, ym := range y.Methods {
- if IdFromQualifiedName(xm.QualifiedName) == IdFromQualifiedName(ym.QualifiedName) {
- if !types.IsIdentical(xm.Type, ym.Type) {
+ for i, n := 0, x.NumMethods(); i < n; i++ {
+ xm := x.Method(i)
+ for j, m := 0, y.NumMethods(); j < m; j++ {
+ ym := y.Method(j)
+ if MakeId(xm.Name(), xm.Pkg()) == MakeId(ym.Name(), ym.Pkg()) {
+ if !types.IsIdentical(xm.Type(), ym.Type()) {
return false // common name but conflicting types
}
continue outer
@@ -152,9 +124,9 @@
func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool {
switch typ := typ.(type) {
case *types.Pointer:
- return allowPtr && canHaveConcreteMethods(typ.Base, false)
- case *types.NamedType:
- switch typ.Underlying.(type) {
+ return allowPtr && canHaveConcreteMethods(typ.Elem(), false)
+ case *types.Named:
+ switch typ.Underlying().(type) {
case *types.Pointer, *types.Interface:
return false
}
@@ -176,7 +148,7 @@
func DefaultType(typ types.Type) types.Type {
if t, ok := typ.(*types.Basic); ok {
k := types.Invalid
- switch t.Kind {
+ switch t.Kind() {
// case UntypedNil:
// There is no default type for nil. For a good error message,
// catch this case before calling this function.
@@ -201,7 +173,9 @@
// makeId returns the Id (name, pkg) if the name is exported or
// (name, nil) otherwise.
//
-func makeId(name string, pkg *types.Package) (id Id) {
+// Exported to exp/ssa/interp.
+//
+func MakeId(name string, pkg *types.Package) (id Id) {
id.Name = name
if !ast.IsExported(name) {
id.Pkg = pkg
@@ -213,15 +187,6 @@
return
}
-// IdFromQualifiedName returns the Id (qn.Name, qn.Pkg) if qn is an
-// exported name or (qn.Name, nil) otherwise.
-//
-// Exported to exp/ssa/interp.
-//
-func IdFromQualifiedName(qn types.QualifiedName) Id {
- return makeId(qn.Name, qn.Pkg)
-}
-
type ids []Id // a sortable slice of Id
func (p ids) Len() int { return len(p) }