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
This commit is contained in:
Rob Pike 2013-05-17 14:02:47 -07:00
parent 83f21b9226
commit 87334f402b
44 changed files with 1984 additions and 1431 deletions

View File

@ -29,10 +29,7 @@ package types
// 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 @@ type Importer func(imports map[string]*Package, path string) (pkg *Package, err
// 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...)
}

View File

@ -21,7 +21,7 @@ import (
// 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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
// 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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
}
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
goto Error
}
x.mode = variable
x.typ = &Pointer{Base: resultTyp}
x.typ = &Pointer{base: resultTyp}
case _Panic:
x.mode = novalue
@ -342,12 +342,12 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
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 @@ Error:
//
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 unparen(x ast.Expr) ast.Expr {
}
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)

View File

@ -117,7 +117,7 @@ func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
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 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe
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 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe
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 @@ func (check *checker) object(obj Object, cycleOk bool) {
// 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 @@ func (check *checker) object(obj Object, cycleOk bool) {
// 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 @@ func (check *checker) object(obj Object, cycleOk bool) {
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 @@ func (check *checker) object(obj Object, cycleOk bool) {
}
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 @@ func (check *checker) object(obj Object, cycleOk bool) {
}
}
// 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 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) {
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 @@ func (check *checker) decl(decl ast.Decl) {
// 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 @@ func (check *checker) decl(decl ast.Decl) {
// 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 @@ func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package,
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")
}
}

View File

@ -121,7 +121,7 @@ func errMap(t *testing.T, testname string, files []*ast.File) map[string][]strin
}
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 @@ func checkFiles(t *testing.T, testname string, testfiles []string) {
// 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 @@ func TestCheck(t *testing.T) {
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,

View File

@ -32,11 +32,11 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
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 @@ func (x *operand) isConvertible(ctxt *Context, T Type) bool {
// "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 @@ func (x *operand) isConvertible(ctxt *Context, T Type) bool {
// "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 (x *operand) isConvertible(ctxt *Context, T Type) bool {
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 isPointer(typ Type) bool {
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
}

View File

@ -53,14 +53,16 @@ func (check *checker) untrace(format string, args ...interface{}) {
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 @@ func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
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 @@ func typeString(typ Type) string {
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 @@ func writeType(buf *bytes.Buffer, typ Type) {
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 @@ func writeType(buf *bytes.Buffer, typ Type) {
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 @@ func writeType(buf *bytes.Buffer, typ Type) {
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) }

View File

@ -93,14 +93,14 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
// 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, &copy)
}
} 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 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
// 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 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
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 @@ func (check *checker) tag(t *ast.BasicLit) string {
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 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
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 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
}
} 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 @@ func (check *checker) unary(x *operand, op token.Token) {
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 @@ func (check *checker) unary(x *operand, op token.Token) {
}
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 @@ func (check *checker) isRepresentable(x *operand, typ *Basic) {
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 @@ func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) {
// 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 @@ func (check *checker) convertUntyped(x *operand, target Type) {
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 @@ func (check *checker) convertUntyped(x *operand, target Type) {
}
// 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 @@ func (check *checker) convertUntyped(x *operand, target Type) {
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 @@ func (check *checker) convertUntyped(x *operand, target Type) {
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 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota
}
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 @@ func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok b
// 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) indexedElts(elts []ast.Expr, typ Type, length int64, iota
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 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
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 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
// 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 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
}
}
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 @@ func (check *checker) callExpr(x *operand) {
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// 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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// 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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// 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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
} 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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// []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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
// 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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
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 {

View File

@ -124,7 +124,7 @@ func GcImport(imports map[string]*Package, path string) (pkg *Package, err error
}
// 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 @@ func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*
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 (p *gcParser) next() {
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 @@ func (p *gcParser) getPkg(id, name string) *Package {
}
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 @@ func (p *gcParser) parseBasicType() Type {
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 @@ func (p *gcParser) parseArrayType() Type {
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 @@ func (p *gcParser) parseMapType() Type {
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 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
// 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 @@ func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
// 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) parseField() *Field {
//
func (p *gcParser) parseStructType() Type {
var fields []*Field
var tags []string
p.expectKeyword("struct")
p.expect('{')
@ -489,11 +494,19 @@ func (p *gcParser) parseStructType() Type {
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 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
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 @@ func (p *gcParser) parseSignature() *Signature {
}
}
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 @@ func (p *gcParser) parseSignature() *Signature {
// 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 @@ func (p *gcParser) parseChanType() Type {
dir = ast.RECV
}
elt := p.parseType()
return &Chan{Dir: dir, Elt: elt}
return &Chan{dir: dir, elt: elt}
}
// Type =
@ -636,19 +651,19 @@ func (p *gcParser) parseType() Type {
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 @@ func (p *gcParser) parseConstDecl() {
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 @@ func (p *gcParser) parseConstDecl() {
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 @@ func (p *gcParser) parseTypeDecl() {
// 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 @@ func (p *gcParser) parseVarDecl() {
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 @@ func (p *gcParser) parseMethodDecl() {
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 (p *gcParser) parseFuncDecl() {
// "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 @@ func (p *gcParser) parseExport() *Package {
}
// package was imported completely and without errors
pkg.Complete = true
pkg.complete = true
return pkg
}

View File

@ -146,7 +146,7 @@ func TestGcImportedTypes(t *testing.T) {
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 @@ func TestGcImportedTypes(t *testing.T) {
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 @@ func TestGcImportedTypes(t *testing.T) {
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)
}

View File

@ -16,143 +16,157 @@ import (
// 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 @@ func newObj(pkg *Package, astObj *ast.Object) Object {
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 @@ func newObj(pkg *Package, astObj *ast.Object) Object {
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
}

64
go/types/objset.go Normal file
View File

@ -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()
}

View File

@ -136,8 +136,8 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool {
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 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool {
// 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 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool {
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 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool {
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 lookupResult struct {
}
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 @@ type embeddedType struct {
// 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 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
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 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
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 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
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 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
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 @@ func findType(list []embeddedType, typ *NamedType) *embeddedType {
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 @@ func lookupField(typ Type, name QualifiedName) lookupResult {
// 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}
}
}

View File

@ -10,82 +10,82 @@ func isNamed(typ Type) bool {
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 @@ func IsIdentical(x, y Type) bool {
// 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 @@ func IsIdentical(x, y Type) bool {
// 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 @@ func IsIdentical(x, y Type) bool {
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 @@ func IsIdentical(x, y Type) bool {
// 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 @@ func IsIdentical(x, y Type) bool {
// 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 @@ func IsIdentical(x, y Type) bool {
// 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 deref(typ Type) Type {
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 @@ func defaultType(typ Type) Type {
// 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 @@ func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
}
// 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
}
}

View File

@ -15,28 +15,28 @@ func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport t
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) resolveIdent(scope *Scope, ident *ast.Ident) bool {
}
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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
// 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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
// 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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
// (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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
// (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 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
}
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

View File

@ -66,14 +66,14 @@ func TestResolveQualifiedIdents(t *testing.T) {
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 @@ func TestResolveQualifiedIdents(t *testing.T) {
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 @@ func TestResolveQualifiedIdents(t *testing.T) {
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}
}
}
}

View File

@ -36,7 +36,7 @@ func (check *checker) isTerminating(s ast.Stmt, label string) bool {
// 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
}
}

View File

@ -28,7 +28,7 @@ func (s *Scope) Lookup(name string) Object {
return s.large[name]
}
for _, obj := range s.Entries {
if obj.GetName() == name {
if obj.Name() == name {
return obj
}
}
@ -41,7 +41,7 @@ func (s *Scope) Lookup(name string) Object {
// 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 @@ func (s *Scope) Insert(obj Object) Object {
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 @@ func (s *Scope) String() string {
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")

View File

@ -21,9 +21,9 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
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 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
}
}
} else {
offsets = DefaultOffsetsof(s.Fields)
offsets = DefaultOffsetsof(s.fields)
}
s.offsets = offsets
}
@ -45,12 +45,12 @@ func (ctxt *Context) offsetsof(s *Struct) []int64 {
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 @@ const DefaultMaxAlign = 8
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 @@ const DefaultPtrSize = 8
// 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
}

View File

@ -42,7 +42,7 @@ var excluded = map[string]bool{
}
// 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 @@ func typecheck(t *testing.T, filenames []string) {
ctxt := Context{
Error: func(err error) { t.Error(err) },
}
ctxt.Check(fset, files)
ctxt.Check(path, fset, files...)
pkgCount++
}
@ -121,7 +121,7 @@ func walkDirs(t *testing.T, dir string) {
// 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

View File

@ -24,9 +24,9 @@ func (check *checker) assignment(x *operand, to Type) bool {
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 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
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 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
}
}
}
obj.Type = typ
obj.typ = typ
}
}
@ -147,10 +147,10 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
// 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 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
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 @@ Error:
}
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 @@ func (check *checker) stmt(s ast.Stmt) {
// 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 @@ func (check *checker) stmt(s ast.Stmt) {
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 @@ func (check *checker) stmt(s ast.Stmt) {
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 @@ func (check *checker) stmt(s ast.Stmt) {
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 @@ func (check *checker) stmt(s ast.Stmt) {
} 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 @@ func (check *checker) stmt(s ast.Stmt) {
if len(clause.List) != 1 || typ == nil {
typ = x.typ
}
lhs.Type = typ
lhs.typ = typ
}
check.stmtList(clause.Body)
}
@ -614,7 +615,7 @@ func (check *checker) stmt(s ast.Stmt) {
// 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 @@ func (check *checker) stmt(s ast.Stmt) {
// 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 @@ func (check *checker) stmt(s ast.Stmt) {
}
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
}

View File

@ -6,10 +6,23 @@ package types
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 @@ const (
// 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 @@ const (
_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 @@ type builtin struct {
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) }

View File

@ -20,7 +20,7 @@ func makePkg(t *testing.T, src string) (*Package, error) {
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 @@ func TestTypes(t *testing.T) {
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)

View File

@ -57,13 +57,13 @@ var aliases = [...]*Basic{
}
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 @@ var predeclaredFunctions = [...]*builtin{
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 @@ func init() {
}
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 @@ func init() {
// 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()
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -15,7 +15,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value {
return f.emit(&Alloc{
Type_: pointer(typ),
Heap: true,
Pos: pos,
pos: pos,
})
}
@ -24,7 +24,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) Value {
//
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 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value {
// 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 @@ func emitCompare(f *Function, op token.Token, x, y Value) Value {
// 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 @@ func emitCompare(f *Function, op token.Token, x, y Value) Value {
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 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
// 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 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
}
// A representation-changing conversion.
c := &Conv{X: val}
c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
}
@ -183,7 +200,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
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 emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
//
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 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value {
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 @@ func emitTailCall(f *Function, call *Call) {
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 @@ func emitTailCall(f *Function, call *Call) {
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

62
ssa/example_test.go Normal file
View File

@ -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
}

View File

@ -179,11 +179,12 @@ func (f *Function) addParam(name string, typ types.Type) *Parameter {
// 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 @@ func (f *Function) startBody() {
// 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 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
}
// 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 @@ func (f *Function) createSyntacticParams(idents map[*ast.Ident]types.Object) {
}
// 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 @@ func (f *Function) removeNilBlocks() {
// 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 @@ func (f *Function) addNamedLocal(obj types.Object) *Alloc {
// 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 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
// 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 @@ func (f *Function) fullName(from *Package) string {
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 @@ func (f *Function) fullName(from *Package) string {
// 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 (f *Function) fullName(from *Package) string {
//
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 @@ func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Pa
}
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 writeSignature(w io.Writer, name string, sig *types.Signature, params []*Pa
//
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 @@ func (f *Function) DumpTo(w io.Writer) {
if len(f.Locals) > 0 {
io.WriteString(w, "# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), indirectType(l.Type()))
fmt.Fprintf(w, "# % 3d:\t%s %s\n", i, l.Name(), l.Type().Deref())
}
}

View File

@ -53,7 +53,7 @@ func (b *Builder) doImport(imports map[string]*types.Package, path string) (typk
} 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 @@ func (b *Builder) doImport(imports map[string]*types.Package, path string) (typk
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

View File

@ -158,14 +158,17 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
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 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
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 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
// 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 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
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 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
}
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 @@ func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
// 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 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
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 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
locals: make([]value, len(fn.Locals)),
}
for i, l := range fn.Locals {
fr.locals[i] = zero(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 @@ func Interpret(mainpkg *ssa.Package, mode Mode, filename string, args []string)
for _, m := range pkg.Members {
switch v := m.(type) {
case *ssa.Global:
cell := zero(indirectType(v.Type()))
cell := zero(v.Type().Deref())
i.globals[v] = &cell
}
}

View File

@ -140,7 +140,10 @@ func run(t *testing.T, dir, input string) bool {
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())

View File

@ -3,7 +3,6 @@ package interp
import (
"fmt"
"go/token"
"os"
"runtime"
"strings"
"unsafe"
@ -29,9 +28,9 @@ func literalValue(l *ssa.Literal) value {
}
// 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 @@ func literalValue(l *ssa.Literal) value {
}
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 asUint64(x value) uint64 {
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 @@ func zero(t types.Type) value {
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 @@ func lookup(instr *ssa.Lookup, x, idx value) value {
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 @@ func unop(instr *ssa.UnOp, x value) value {
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 unop(instr *ssa.UnOp, x value) value {
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 @@ func widen(x value) value {
}
// 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 @@ func conv(t_dst, t_src types.Type, x value) value {
// + 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 @@ func conv(t_dst, t_src types.Type, x value) value {
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 @@ func conv(t_dst, t_src types.Type, x value) value {
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 @@ func conv(t_dst, t_src types.Type, x value) value {
return res
}
case *types.Basic:
if ut_dst.Kind == types.String {
if ut_dst.Kind() == types.String {
return x.(string)
}
}
@ -1214,7 +1190,7 @@ func conv(t_dst, t_src types.Type, x value) value {
}
// 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 @@ func conv(t_dst, t_src types.Type, x value) value {
}
// 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 @@ func conv(t_dst, t_src types.Type, x value) value {
}
// 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 @@ func conv(t_dst, t_src types.Type, x value) value {
// 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
}

View File

@ -15,19 +15,22 @@ import (
"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 @@ var rtypeType = makeNamedType("rtype", &types.Basic{Name: "rtype"})
// 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۰Init(fn *ssa.Function, args []value) value {
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۰Bits(fn *ssa.Function, args []value) value {
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۰Kind(fn *ssa.Function, args []value) value {
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 ext۰reflect۰ValueOf(fn *ssa.Function, args []value) value {
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۰Pointer(fn *ssa.Function, args []value) value {
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 @@ func ext۰reflect۰Value۰Elem(fn *ssa.Function, args []value) value {
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 @@ func ext۰reflect۰Value۰Field(fn *ssa.Function, args []value) value {
// 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 @@ func newMethod(pkg *ssa.Package, recvType types.Type, name string) *ssa.Function
// 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
}

View File

@ -103,8 +103,8 @@ func usesBuiltinMap(t types.Type) bool {
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
}

View File

@ -306,7 +306,7 @@ type newPhiMap map[*BasicBlock][]newPhi
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 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
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 replaceAll(x, y Value) {
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

View File

@ -39,23 +39,23 @@ func zeroLiteral(t types.Type) *Literal {
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 zeroLiteral(t types.Type) *Literal {
}
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()
}

View File

@ -31,7 +31,7 @@ func (a address) store(fn *Function, v Value) {
}
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

View File

@ -48,7 +48,7 @@ func relName(v Value, i Instruction) string {
// 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 @@ func (v *Function) String() string {
// 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 @@ func (v *Alloc) String() string {
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 @@ func printCall(v *CallCommon, prefix string, instr Instruction) string {
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 @@ func (v *Call) String() string {
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 @@ func (v *UnOp) String() string {
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 *MakeChan) String() string {
}
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 (s *MapUpdate) String() string {
}
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 @@ func (p *Package) DumpTo(w io.Writer) {
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,

View File

@ -70,7 +70,7 @@ func (p *anonFieldPath) isIndirect() bool {
// 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 @@ func (c candidate) String() string {
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 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
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 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
// 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 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Method, concrete *Fu
// 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 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
// Iterate over selections e.A.B.C.f in the natural order [A,B,C].
for _, p := range cand.path.reverse() {
// Loop invariant: v holds a pointer to a struct.
if _, ok := 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 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
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 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
// 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 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
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 makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
//
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++
})
}
}

View File

@ -123,7 +123,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
case *BinOp:
case *Call:
case *ChangeInterface:
case *Conv:
case *ChangeType:
case *Convert:
case *Defer:
case *Extract:
case *Field:

View File

@ -22,10 +22,10 @@ type Program struct {
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 Package struct {
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 MethodSet map[Id]*Function
// 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 @@ type Type struct {
// 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 @@ type Instruction interface {
// 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 @@ type Instruction interface {
// 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 @@ type Parameter struct {
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 @@ type Literal struct {
// 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 @@ type Builtin struct {
// 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 @@ type Alloc struct {
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 @@ type Phi struct {
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 @@ type Phi struct {
//
// 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 @@ type Call struct {
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 @@ type BinOp struct {
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 @@ type BinOp struct {
// 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 @@ type UnOp struct {
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 @@ type Conv struct {
//
// 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 @@ type ChangeInterface struct {
// 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 @@ type MakeInterface struct {
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 MakeClosure struct {
//
// 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 MakeMap struct {
//
// 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 MakeChan struct {
//
// 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 @@ type MakeSlice struct {
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 @@ type Slice struct {
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 @@ type FieldAddr struct {
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 @@ type Field struct {
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 @@ type IndexAddr struct {
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 @@ type Index struct {
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 @@ type SelectState struct {
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 @@ type SelectState struct {
// 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 @@ type Select struct {
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 @@ type Range struct {
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 @@ type Next struct {
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 @@ type TypeAssert struct {
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 @@ type Extract struct {
// 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 @@ type Jump struct {
// 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 @@ type If struct {
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 @@ type If struct {
// 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 If struct {
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 @@ type RunDefers struct {
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 @@ type RunDefers struct {
// 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 @@ type Go struct {
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 @@ type Defer struct {
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 Defer struct {
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 @@ type Store struct {
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 @@ type MapUpdate struct {
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 MapUpdate struct {
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 @@ type CallCommon struct {
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 @@ func (c *CallCommon) IsInvoke() bool {
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 @@ func (c *CallCommon) StaticCallee() *Function {
// 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 @@ func (c *CallCommon) Description() string {
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 @@ func (c *CallCommon) Description() string {
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 *Capture) Referrers() *[]Instruction { return &v.referrers }
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) Name() string { return fmt.Sprintf("t%d", v.num)
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 (*Builtin) ImplementsValue() {}
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 (*Alloc) ImplementsInstruction() {}
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 (*Store) ImplementsInstruction() {}
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 @@ func (v *ChangeInterface) Operands(rands []*Value) []*Value {
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)
}

View File

@ -101,7 +101,7 @@ func main() {
context := &ssa.Context{
Mode: mode,
Loader: ssa.GorootLoader,
Loader: ssa.MakeGoBuildLoader(nil),
}
b := ssa.NewBuilder(context)
mainpkg, args, err := ssa.CreatePackageFromArgs(b, args)

View File

@ -32,7 +32,7 @@ func (info *TypeInfo) TypeOf(e ast.Expr) types.Type {
// 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 @@ func (info *TypeInfo) ObjectOf(id *ast.Ident) types.Object {
// 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) IsType(e ast.Expr) bool {
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 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
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 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
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 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
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 @@ func builtinCallSignature(info *TypeInfo, e *ast.CallExpr) *types.Signature {
panic("unknown builtin: " + builtin)
}
return &types.Signature{Params: params, IsVariadic: isVariadic}
return types.NewSignature(nil, types.NewTuple(params...), nil, isVariadic)
}

View File

@ -43,23 +43,10 @@ func isBlankIdent(e ast.Expr) bool {
//// 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 @@ func isPointer(typ types.Type) bool {
// 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 @@ func methodIndex(typ types.Type, methods []*types.Method, id Id) (i int, m *type
// 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 objKind(obj types.Object) ast.ObjKind {
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 canHaveConcreteMethods(typ types.Type, allowPtr bool) bool {
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 @@ func DefaultType(typ types.Type) types.Type {
// 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 @@ func makeId(name string, pkg *types.Package) (id Id) {
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) }