mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
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:
parent
83f21b9226
commit
87334f402b
@ -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...)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) }
|
||||
|
253
go/types/expr.go
253
go/types/expr.go
@ -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, ©)
|
||||
}
|
||||
} 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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
64
go/types/objset.go
Normal 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()
|
||||
}
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) }
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
426
ssa/builder.go
426
ssa/builder.go
File diff suppressed because it is too large
Load Diff
98
ssa/doc.go
98
ssa/doc.go
@ -27,8 +27,9 @@
|
||||
// By supplying an instance of the SourceLocator function prototype,
|
||||
// clients may control how the builder locates, loads and parses Go
|
||||
// sources files for imported packages. This package provides
|
||||
// GorootLoader, which uses go/build to locate packages in the Go
|
||||
// source distribution, and go/parser to parse them.
|
||||
// MakeGoBuildLoader, which creates a loader that uses go/build to
|
||||
// locate packages in the Go source distribution, and go/parser to
|
||||
// parse them.
|
||||
//
|
||||
// The builder initially builds a naive SSA form in which all local
|
||||
// variables are addresses of stack locations with explicit loads and
|
||||
@ -38,6 +39,61 @@
|
||||
// subsequent analyses; this pass can be skipped by setting the
|
||||
// NaiveForm builder flag.
|
||||
//
|
||||
// The primary interfaces of this package are:
|
||||
//
|
||||
// - Member: a named member of a Go package.
|
||||
// - Value: an expression that yields a value.
|
||||
// - Instruction: a statement that consumes values and performs computation.
|
||||
//
|
||||
// A computation that yields a result implements both the Value and
|
||||
// Instruction interfaces. The following table shows for each
|
||||
// concrete type which of these interfaces it implements.
|
||||
//
|
||||
// Value? Instruction? Member?
|
||||
// *Alloc ✔ ✔
|
||||
// *BinOp ✔ ✔
|
||||
// *Builtin ✔ ✔
|
||||
// *Call ✔ ✔
|
||||
// *Capture ✔
|
||||
// *ChangeInterface ✔ ✔
|
||||
// *ChangeType ✔ ✔
|
||||
// *Constant ✔ (const)
|
||||
// *Convert ✔ ✔
|
||||
// *Defer ✔
|
||||
// *Extract ✔ ✔
|
||||
// *Field ✔ ✔
|
||||
// *FieldAddr ✔ ✔
|
||||
// *Function ✔ ✔ (func)
|
||||
// *Global ✔ ✔ (var)
|
||||
// *Go ✔
|
||||
// *If ✔
|
||||
// *Index ✔ ✔
|
||||
// *IndexAddr ✔ ✔
|
||||
// *Jump ✔
|
||||
// *Literal ✔
|
||||
// *Lookup ✔ ✔
|
||||
// *MakeChan ✔ ✔
|
||||
// *MakeClosure ✔ ✔
|
||||
// *MakeInterface ✔ ✔
|
||||
// *MakeMap ✔ ✔
|
||||
// *MakeSlice ✔ ✔
|
||||
// *MapUpdate ✔
|
||||
// *Next ✔ ✔
|
||||
// *Panic ✔
|
||||
// *Parameter ✔
|
||||
// *Phi ✔ ✔
|
||||
// *Range ✔ ✔
|
||||
// *Ret ✔
|
||||
// *RunDefers ✔
|
||||
// *Select ✔ ✔
|
||||
// *Slice ✔ ✔
|
||||
// *Type ✔ (type)
|
||||
// *TypeAssert ✔ ✔
|
||||
// *UnOp ✔ ✔
|
||||
//
|
||||
// Other key types in this package include: Program, Package, Function
|
||||
// and BasicBlock.
|
||||
//
|
||||
// The program representation constructed by this package is fully
|
||||
// resolved internally, i.e. it does not rely on the names of Values,
|
||||
// Packages, Functions, Types or BasicBlocks for the correct
|
||||
@ -59,32 +115,31 @@
|
||||
//
|
||||
// const message = "Hello, World!"
|
||||
//
|
||||
// func hello() {
|
||||
// func main() {
|
||||
// fmt.Println(message)
|
||||
// }
|
||||
//
|
||||
// The SSA Builder creates a *Program containing a main *Package such
|
||||
// as this:
|
||||
//
|
||||
// Package(Name: "main")
|
||||
// Package (Name: "main")
|
||||
// Members:
|
||||
// "message": *Literal (Type: untyped string, Value: "Hello, World!")
|
||||
// "message": *Constant (Type: untyped string, Value: "Hello, World!")
|
||||
// "init·guard": *Global (Type: *bool)
|
||||
// "hello": *Function (Type: func())
|
||||
// "main": *Function (Type: func())
|
||||
// Init: *Function (Type: func())
|
||||
//
|
||||
// The printed representation of the function main.hello is shown
|
||||
// The printed representation of the function main.main is shown
|
||||
// below. Within the function listing, the name of each BasicBlock
|
||||
// such as ".0.entry" is printed left-aligned, followed by the block's
|
||||
// instructions, i.e. implementations of Instruction.
|
||||
// Instructions.
|
||||
// For each instruction that defines an SSA virtual register
|
||||
// (i.e. implements Value), the type of that value is shown in the
|
||||
// right column.
|
||||
//
|
||||
// # Name: main.hello
|
||||
// # Name: main.main
|
||||
// # Declared at hello.go:7:6
|
||||
// # Type: func()
|
||||
// func hello():
|
||||
// func main():
|
||||
// .0.entry:
|
||||
// t0 = new [1]interface{} *[1]interface{}
|
||||
// t1 = &t0[0:untyped integer] *interface{}
|
||||
@ -102,14 +157,21 @@
|
||||
// TODO(adonovan): demonstrate more features in the example:
|
||||
// parameters and control flow at the least.
|
||||
//
|
||||
// TODO(adonovan): Consider how token.Pos source location information
|
||||
// should be made available generally. Currently it is only present in
|
||||
// Package, Function and CallCommon.
|
||||
//
|
||||
// TODO(adonovan): Consider the exceptional control-flow implications
|
||||
// of defer and recover().
|
||||
//
|
||||
// TODO(adonovan): build tables/functions that relate source variables
|
||||
// to SSA variables to assist user interfaces that make queries about
|
||||
// specific source entities.
|
||||
// TODO(adonovan): Consider how token.Pos source location information
|
||||
// should be made available generally. Currently it is only present
|
||||
// in package Members and selected Instructions for which there is a
|
||||
// direct source correspondence. We'll need to work harder to tie all
|
||||
// defs/uses of named variables together, esp. because SSA splits them
|
||||
// into separate webs.
|
||||
//
|
||||
// TODO(adonovan): it is practically impossible for clients to
|
||||
// construct well-formed SSA functions/packages/programs directly; we
|
||||
// assume this is the job of the ssa.Builder alone.
|
||||
// Nonetheless it may be wise to give clients a little more
|
||||
// flexibility. For example, analysis tools may wish to construct a
|
||||
// fake ssa.Function for the root of the callgraph, a fake "reflect"
|
||||
// package, etc.
|
||||
package ssa
|
||||
|
102
ssa/emit.go
102
ssa/emit.go
@ -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
62
ssa/example_test.go
Normal 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
|
||||
}
|
54
ssa/func.go
54
ssa/func.go
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
30
ssa/print.go
30
ssa/print.go
@ -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,
|
||||
|
@ -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++
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
371
ssa/ssa.go
371
ssa/ssa.go
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
89
ssa/util.go
89
ssa/util.go
@ -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) }
|
||||
|
Loading…
x
Reference in New Issue
Block a user